5 回答
TA贡献1725条经验 获得超7个赞
作用域链!
你的代码可以拆解为:
var a = 1;
var fn = function(){
var a=3;
return function(){
a++;
alert(a);
}
}
// 自执行过程
var func = fn();
func(); // => 4
func(); // => 5
链1:global -> a
链2:global -> func -> a
所以 return function 执行时,会按这个规则去找变量a,找到的即是链2的!
TA贡献1846条经验 获得超7个赞
自执行函数自己有一个作用域,return出来的那个函数里面操作的a是自执行函数环境下的a。且返回的这个函数中对a的引用在程序结束前没有释放。所以第一次执行func时修改了a,第二次在执行的时候a还会叠加,并没有重新创建这个环境。
相当于
var a = 1;
function plus(){
a++;
}
plus();
plus();
console.log(a) // 3
TA贡献2065条经验 获得超14个赞
这个题是典型的考查JS的代码执行原理,其执行过程解析如下:
1、当JS引擎遇到上面的这段代码时,会创建一个全局执行上下文(一个执行上下文的生命周期包含两个阶段,即创建阶段和执行阶段);
2、创建阶段,JS引擎会做如下工作:
a,创建变量对象(Variable Object -> VO),注:全局执行上下文和函数执行上下文有一点区别的,全局是没有arguments的相关操作
a-1、查找function函数声明,在VO上添加一个属性,属性名为函数名,属性值为函数在内存中的引用,遇
同名覆盖
a-2、查找var变量声明,在VO上添加一个属性,属性名是变量名,属性值为undefined,遇同名跳过
这一步完成后的结果如下:
VO = {
a:undefined,
func:undefined
}
b,建立作用域链
作用域链其实就是一个数组,在数组的顶端存放的永远是当前执行上下文的VO对象,在全局执行上下文中,作
用域链中只有一个全局的VO,即 [Global_Context.VO],此时JS引擎还做做一件事情,给当前执行上下文中函数添加个内部属性[[scope]](我们不能使用,仅供JS引擎使用)
比如:fn.[[scope]] = [Global_Context.VO]
c,确定this指向
这里this->window
一个执行上下文创建完成后压入栈
3、执行阶段,该阶段主要完成变量赋值、函数调用等等操作,此时 VO(用户不可操作) -> AO(用户可操作)(注:VO/AO是同一个对象,不同阶段的不同叫法)
首先遇到 a = 2;JS引擎会去当前执行上下文的作用域链中查找(查找的过程是从第一个AO对象开始,依次查找,如果找到,则返回;否则继续查找,直到全局的AO,如果还找不到,则报错。)
这一步完成后的结果是如下:
AO={
a:2,
/*
注:由于func表达式后面是一个自执行匿名函数,所以func的值最终是自执行匿名函数执行后的结果,
这个自执行匿名函数执行时,也会创建对应的执行上下文,和上面的过程一样的,有自己的AO、作用域链
这里的作用域链是由当前执行上下文的VO和函数的内部属性[[scope]]组合而成,则function(){ a++;alert(a); }此函数
也有内部属性[[scope]]指向父级执行上下文的作用域链,由于function(){ a++;alert(a); }被外部变量引用着,而该函数
的[[scope]]有指向自执行匿名函数的作用域链,所以当自执行匿名函数执行完毕后,执行上下文弹出栈,但不会被销毁,而是
又保存到内存中其他位置。
*/
func:function(){
a++;
alert(a);
}
}
作用域链 [AO,Global_Context.VO]
4、第一次执行func()时,首先会在func的执行上下文的AO中查找a,没有找到,就继续到父级执行上下文的AO中找,找到a是3,然后+1,得4
5、第二次执行func时,同样的执行过程,首先会在func的执行上下文的AO中查找a,没有找到,就继续到父级执行上下文的AO中找,找到a是4,然后+1,得5
上述执行过程涵盖了变量提升、作用域、作用域链、闭包(插一个题外话:为什么闭包可以外部访问内部变量,很多人都知道可以访问,但是为什么能访问? 上面已经给出解释)
以上是本人对JS的底层执行原理的大体认识,尚有细枝末节未涉及,不足之处难免,仅供参阅。
添加回答
举报