函数的自调用
函数自己调用自己
例子1:不要运行,浏览器会崩溃
function fn () {
console.log(666)
fn()
}
fn()
列子2:实现10的阶乘
function fn (n) {
if(n<=1){
return 1
}
else{
return n*fn(n-1)
}
}
var re=fn(10)
console.log(re)
1.fn标识符
因为它是函数定义时的名字 函数体内部可以直接访问
var obj={say:function fn(n){
console.log(666)
if(n<0){
return
}
// say(n-1)//报错
fn(n-1)//自己调用自己
}}
2.匿名函数的自调用
通过arguments属性调用
(function(n){
if(n<=1){console.log(1)}
else{console.log(n);arguments.callee(n-1) }
})(10)
预编译
函数调用时,运行代码的过程:
1.分析代码是否正确(符号,词法分析)
2.隐式操作 ==> 预编译:函数调用了以后 在运行代码之前
3.运行代码: 预编译过的就不在运行
1.局部预编译
(1) 创建AO对象--Activation Object(执行期上下文):AO{ };
(2) 找形参和变量声明,将变量和形参名作为AO属性名,即变量提升过程,值为undefined;
(3) 将实参的值放到形参中去
(4) 在函数体里面找函数声明,值赋予函数体
1.1 具体流程
1.分析代码是否正确 符号 词法分析
var var=20;
var 01js=100
var a;
var a,
2.隐式操作 ==> 预编译:函数调用了以后 在运行代码之前
2.1函数每次调用都会生成一个对象:执行期上下文对象
2.2.给AO对象添加成员:函数内部的局部变量和形参变量 名 作为AO对象的属性名
AO:{a:undefined}
ao.a=undefined
ao.a=undefined//形参和局部变量一样的时候 不影响
2.3.把传入的实参赋值给AO对象的属性
AO:{a:100}
2.4.局部函数声明,赋值 把局部函数的名字让AO对象也有一个一样的成员名,把函数体赋值给这个属性
AO:{a:100,fn:function fn () {}}
function fm(a) {
console.log(a)
var a=20
function fn () {}
console.log(a)
}
var b=100
fm(b)
3.运行代码: 预编译过的就不在运行
AO:{a:100==>20,fn:function fn () {}}
console.log(a)//100
a=20
console.log(a)//20
2.全局预编译
创建一个函数
console.log(a)
var a = 20
function fn() {
console.log(66)
}
过程:
1.生成一个对象Global Object (GO)
GO:{}
2.把所有的全局变量 设置为GO的属性名
GO:{a:undefend}
3.把所有函数名 作为GO的成员名,把函数体赋值给这个成员
GO:{a:undefend,fn:function fn(){console.log(66)}}
4.执行代码
GO:{a:undefend==>20,fn:function fn(){console.log(66)}}
console.log(a) //undef
a = 20 //
2.1 不同环境下的全局预编译
1.不同的环境中运行js代码不一样
2.GO对象的成员全部浅拷贝给环境对象window
3.node.js 环境中没有这一步
作用域链
函数有属性length,name ,[[scoped]]
js对象有两种成员
一种是上文成员(js语法可以直接访问的成员)
一种是下文成员(底层语法访问的成员),[[scopes]]括起来的成员名 就是下文成员
函数在定义/声明的时候 就有了[[scopes]] 里面保存了上层的AO对象,函数调用时会生成AO对象 AO保存在scopes对象内部的
函数生成了就会有个属性 [[scopes]] 作用域"数组"(只能引擎使用)
函数调用时生成AO对象 会把AO对象放在scopes
每次调用都会放在scopes前面(顶部)
每个函数scopes数组中天生就有一个AO对象 就是这个函数的上层的AO
列:
function a() {
function b(){
function c() {
}
c();
}
b();
}
a();
分析:
a 定义: a.[[scope]] --> 0:GO
a 执行: a.[[scope]] --> 0:AO(a)
1:GO
b 定义: b.[[scope]] --> 0:AO(a)
1:GO
注意b执行了才会产生c的定义哈!!
b 执行: b.[[scope]] --> 0:AO(b)
1:AO(a)
2:GO
c 定义: c.[[scope]] --> 0:AO(b)
1:AO(a)
2:GO
c 执行: c.[[scope]] --> 0:AO(c)
1:AO(b)
2:AO(a)
3:GO
列:
function a() {
var aaa=123;
function b() {
var bbb=234;
console.log(aaa);
} return b;
}
var glob=100;
var demo=a();
demo();
分析: 函数a执行时,a.[[scope]]如下:
函数b被定义时,b.[[scope]]如下:
函数b出生时是直接继承a的作用域的,所以:
整体的情况是
a函数被执行完毕之后,a函数的执行期上下文肯定是要被销毁(销毁=拆线)的,所以应该是下面的状况: