为了账号安全,请及时绑定邮箱和手机立即绑定

为什么这道题目答案不全是4

为什么这道题目答案不全是4

30秒到达战场 2019-03-16 19:15:35
var a=2;var func=(function(){    var a=3;    return function(){       a++;       alert(a);    }})();func();func();经运行分别是4,5 为什么不全是4呢
查看完整描述

5 回答

?
qq_遁去的一_1

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的!


查看完整回答
反对 回复 2019-03-19
?
喵喵时光机

TA贡献1846条经验 获得超7个赞

自执行函数自己有一个作用域,return出来的那个函数里面操作的a是自执行函数环境下的a。且返回的这个函数中对a的引用在程序结束前没有释放。所以第一次执行func时修改了a,第二次在执行的时候a还会叠加,并没有重新创建这个环境。


相当于


var a = 1;

function plus(){

    a++;

}

plus();

plus(); 

console.log(a) // 3


查看完整回答
反对 回复 2019-03-19
?
侃侃尔雅

TA贡献1801条经验 获得超16个赞

返回函数里的a是func里的啊 修改的也是func里的a


查看完整回答
反对 回复 2019-03-19
?
一只甜甜圈

TA贡献1836条经验 获得超5个赞

好好研究一下闭包
教程

查看完整回答
反对 回复 2019-03-19
?
翻翻过去那场雪

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的底层执行原理的大体认识,尚有细枝末节未涉及,不足之处难免,仅供参阅。


查看完整回答
反对 回复 2019-03-19
  • 5 回答
  • 0 关注
  • 567 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信