推广

4道题考察js堆栈内存

iseeyu2年前 (2024-02-22)推广137

  • b = a,同理,在全局变量环境VO(G)创建一个全局变量b,指向堆内存地址:AAAFFF00

  • a.x = a = { n: 2 },这个的优先级,其实等同于a.x = {n: 1}a = {n: 1}

  • a.x = {n: 1},在堆内存中再创建一个地址(比如:AAAFFF11),内容是n: 1,在a的内存地址AAFF00中创建一个x变量,指向AAAFFF11,此时,b = a = {n:1, x: {n: 1}}

  • a = {n: 1},将a的指向由AAAFFF00改为AAAFFF11

  • 所以最后结果为:
    b = {n:1, x: {n: 1}}
    a = {n: 1}

    2. 变态的变量提升

    变量提升:当前上下文执行之前,会把var/function声明或者定义提升,带var的只声明,带function的声明+定义

    但如果遇到{}块级作用域时,新老版本浏览器表现不一样(兼容ES3、兼容ES6)

    • IE浏览器<=IE10(老版本)
      不管{},还是一如既往的function声明+定义,而且也不会存在块级作用域
    • 新版本浏览器(这里的{}是除对象中的{}
      {}中的function,在全局下只声明不再定义;
      {}中出现function/let/const,会创建一个块级作用域

    下面拿一道题来说明:

    var a = 0
    if(true) {
      a = 1
      function a() {}
      a = 21
      console.log(a)  // 21
    }
    console.log(a) // 1
    

    如果是在IE10以下的执行过程:

    1. 变量提升function a() {}
    2. 全局变量提升var a
    3. 开始执行,全局 a = 0
    4. 全局a = 1
    5. 全局a = 21
    6. 打印全局a: 21
    7. 打印全局a: 21

    如果是在新版本浏览器,也就是向前兼容ES3,向后兼容ES6的执行过程:

    1. 因为变量提升,所以var a,function a都在全局执行上下文EC(G)中的全局变量环境VO(G)中创建一个全局变量a

    2. 代码执行a = 0,全局变量环境VO(G)a = 0

    3. 遇到{}块级作用域,生成一个块级执行上下文EC(block),会生成一个私有变量对象AO(block)

    4. 在这个块级作用域中,因为有function a,所以块级中发生变量提升,声明+定义,在heap堆内存中生成一个函数,在AO(block)创建一个变量a指向函数堆内存,这之后在这块级中,遇到的a都是私有的

      image.png

    5. 块级中执行a = 1AO(block)a = 1

      image.png

    6. 遇到function a() {},因为这个函数,为了向前兼容ES3,所以在全局内提升过一回;为了向后兼容ES6,又在块级作用域中提升一回,所以浏览器为了兼容,真遇到块级中的函数时,它会做一件事:遇到这代码前,会把代码之前所有对a的操作,映射给全局一份,但是后面的则不会再处理了,它会认为这之后的都是自己私有的了,即之前a = 1会映射到全局VO(G) 中 a =1

      image.png

    7. 之后a = 21,已经是私有的了,所以只影响块级AO(block) 中 a = 21

      image.png

    8. 块级中console.log(a) => AO(block) a => 21

    9. 全局中console.log(a) => VO(G) a => 1

    扩展练习

    为了加深这个变态的规则,我们多做几道题:

    变态提升练习1

    {
      function foo() {}
      foo = 1
    }
    
    console.log(foo);
    
    • 第1步:{}中的function foo在变量提升,全局中只声明,不定义
    EC(G):  
       AO(G) function foo
    
    • 第2步,在{}块级中有function,所以会产生一个块级作用域EC(Block)foo在这个块级作用域里,变量提升:声明+定义
    EC(block):   
       AO(block): funciont foo() {}
    
    • 第3步,开始执行(执行过程后,AO变量对象 =>VO激活对象),因为foo在全局和私有都声明过,为了兼容ES3ES6,在执行到function foo() {}里,这之前的操作映射到全局,也就是AO(block): funciont foo() {}声明+定义的过程
    EC(G):  
       VO(G) function foo() {}
    
    • 第4步,执行foo = 1,因为在块级作用域中有foo私有变量,所以是在EC(block)中赋值
    EC(block):
          VO(G) foo => 1
    

    变态提升练习2

    下面我要开始偷懒了,哈哈哈,请结合上下文理解下面注释步骤

    // 第1步,EC(G), AO(G): function foo
    // 第2步,EC(block), AO(block): foo => function foo() {}
    // 第3步,开始执行,EC(G), VO(G): foo => function foo() {}
    // 第4步,foo = 1, EC(block), VO(block): foo => 1
    // 第5步,EC(G), VO(G): foo => 1
    {
      function foo() {}
      foo = 1
      function foo() {} // 1
    }
    
    console.log(foo); // 1
    

    变态提升练习2

    
    // 第1步,EC(G), AO(G): function foo
    // 第2步,EC(block), AO(block): foo => function foo() {}
    // 第3步,开始执行function foo() {},EC(G), VO(G): foo => function foo() {}
    // 第4步,foo = 1, EC(block), VO(block): foo => 1
    // 第5步,function foo() {} => EC(G), VO(G): foo => 1
    // 第6步,foo = 2,EC(block), VO(block): foo => 2
    
    {
      function foo() {}
      foo = 1
      function foo() {}
      foo = 2
      console.log(foo); // 2
    }
    console.log(foo); // 1
    

    3. 带形参的堆栈内存考察

    以下函数输出结果是?

    var x = 1
    function func(x, y = function func2() { x = 2 }) {
      x = 3;
      y()
      console.log(x)  // 2
    }
    func(5)
    console.log(x)  // 1
    

    下面使用图分析下过程:

    1. 全局执行x = 1,在EC(G)中创建一个VO(G),创建值1,再创建一个对象x,x指向值1

      image.png

    2. 发现函数声明,为其创建一个堆内存,定义其函数,在这个内存中,声明其所在的作用域,即全局作用域VC(G),形参x、y,及其函数体字符串,并在全局变量中创建一个func,指向这个堆内存

      image.png

    3. 执行func(5),执行一个函数,会为其创建一个私有的执行上下文EC(func),在其中会创建一个私有变量环境AO(func)

    4. EC(func)初始其作用域链:它自身作用域EC(func)和其上级作用域EC(G)(全局作用域)

    5. 形参赋值:传了x = 5y没传参,所以使用其默认值函数func2,碰到函数,为其申请堆内存AAAFFF111,同样,分析其作用域和形参,并将函数体保存到内存中,并将y指向AAAFFF111

      image.png

    6. 分析完关系后,开始执行func5函数体

    7. x = 3, 在自己作用域EC(func)中查找x,发现有私有x,所以将x指向3

    8. y(),在自己作用域EC(func)中查找y,发现有y指向func2,执行函数func2,并其又单独创建一个执行上下文EC(func2),为其分析作用域链,自身所在作用域EC(func2)和其上级作用域EC(func),因为其没有形参赋值,所以也没创建相关的私有变量

      image.png

    9. 开始执行函数func2的函数体,x = 2,所以在其自身作用域EC(func2)中找不到该变量x,会向其上级作用域EC(func)中查找,发现有x,所以将EC(func)x指向2

      image.png

    10. func2执行结束,继续执行EC(func)中的console.log(x),输出其自身的x:2

    11. func执行结束,继续执行EC(G)中的console.log(x),输出全局VO(G)中的x:1

      image.png

    4. 变态版的带形参函数的堆栈内存

    下面题目输出是?

    var x = 1
    function func(x, y = function func2() { x = 2 }) {
      // 这里,x多一个var声明
      var x = 3;
      y()
      console.log(x)  
    }
    func(5)
    console.log(x)
    

    这里就要讲到浏览器运行es6的机制了:

    ES6产生块级作用域的两种情况

    第1种,正常的{}产生的块级作用域

    这种是我们平常所认识的,ES6中存在块级作用域,即只要{}(除了对象中的{})出现let/const/function

    第2种,是浏览器在运行时产生的,即只要符合以下两个条件:
    1. 函数有任意一个形参赋值了默认值
    2. 函数体中有单独声明过某个变量(var/let/const/function都算)
      那么这个函数就会产生2个上下文:

      • 一个是函数本身执行产生的私有上下文,比如上面func函数执行时,会生成EC(FUNC)
      • 一个是函数体大括号包起来的块级上下文EC(BLOCK)

    下面,我们依然画图来分析:

    1. 第一个过程,和上一题1\2步骤是一致的,依然是生成全局执行上下文EC(G),它在VO(G)声明两个变量:xfuncx指向值1,func指向函数堆内存AAAFFF000

      image.png

    2. 第二个过程,执行func(5),这时,因为func中形参y设有默认值,且函数体中声明了变量var x = 3,按照上面的规则,这里会生成两个执行上下文EC(FUNC)EC(BLOCK)

    3. 先来分析EC(FUNC) ,它的作用域是EC(G),作用域链是它自身上下文EC(FUNC) 和上级作用域上下文EC(G),它的形参是x => 5y => 函数func2堆内存AAAFFF111

      image.png

    4.对EC(BLOCK)的代码块分析,它的作用域是EC(FUNC),作用域链是它自身上下文EC(BLOCK)和上级执行上下文EC(FUNC),在块级中,var x = 3它声明了x,所以x是块级作用域中的私有变量,当执行x = 3时,将块级中的x => 3

    image.png

    1. 继续执行y(),发现块级中没有y变量,去它的作用域链上级EC(FUNC)找,找到了y,执行y(),生成func2执行上下文EC(FUNC2),它的作用域是EC(FUNC),所以其作用域链是[自身执行上下文EC(FUNC2),作用域EC(FUNC)],没有形参和变量声明,所以没有自身私有变量;执行里面函数体x = 2,在EC(FUNC2)中找不到x,所以去它作用域上级找EC(FUNC)EC(FUNC)中有x,所以将EC(FUNC)x => 2

      image.png

    2. EC(BLOCK)继续执行console.log(x),打印的是 EC(BLOCK)中私有的x,即3

    3. func(5)执行完毕,继续执行到EC(G)console.log(x),打印的是VO(G)中的x,即1

      image.png

    所以,答案是3和1

    下面,为了证明我没有胡说八道=’=,我们在浏览器上断点调试下最后一题

    1. 在开始位置打上断点debugger,在浏览器打开,因为window全局变量太多,不好找到全局VO(G)x,所以我在Watch中添加了window.x变量,方便我们观察VO(G)中(也就是浏览器的Global)x的值,可以看到,还没调试之前,全局中的xundefind
    // 第4题:变态带形参的堆栈考核
    debugger;
    var x = 1
    function func(x, y = function func2() { x = 2 }) {
      var x = 3;
      y()
      console.log(x)
    }
    func(5)
    console.log(x)
    

    image.png

    1. 当执行完x=1时,func(5)执行前,可以看到全局VO(G)x = 1

      image.png

    2. 继续往下执行func(5),可以看到生了Scope中,也就是EC(FUNC)的作用域中,生成两个执行上下文BLOCKLOCAL,对应我们上文说的EC(BLOCK)EC(FUNC),因为私有变量中有x,然后块级中也有声明x,所以会将私有的x映射到块级中的x

      image.png

    3. 执行块级中var x = 3,发现块级中私有变量x变为3

      image.png

    4. 执行y(),即生成func2的执行上下文EC(FUNC2),因为没有私有变量,所以其Local为空

      image.png

    5. 执行完y()x = 2后,看到EC(FUNC)中的x => 2

      image.png

    6. 执行EC(BLOCK)console.log(x),输出的是Block中的x

      image.png

    7. 执行EC(G)console.log(x),输出的是Global中的x

      image.png

    由此,验证了上面的分析结论。

    扫描二维码推送至手机访问。

    版权声明:本文由西安泽虎代运营发布,如需转载请注明出处。

    转载请注明出处https://0291.com.cn/post/56247.html

    相关文章

    八分之一的家庭都拥有百万欧元财富!这个国家是怎么做到的 ...

    八分之一的家庭都拥有百万欧元财富!这个国家是怎么做到的 ...

    疫情之下,全球民众的家庭财富受到了前所未有的重创。然而,在一个国家,情况却有所不同——显示,在过去的八年里,爱尔兰的富人比以往任何时候都更富有,百万富翁数量激增了2.5倍。根据爱尔兰中央银行的数据,大约有22.3万户家庭(即全国总家庭的12%)拥有100万欧元或以上的净财富...

    建好律师网站和建错律师网站会有什么不同效果。

    建好律师网站和建错律师网站会有什么不同效果。

    现如今各种各样的律师网站建设不断涌现出来,网页吸引程度也越来越高,亮点越来越足,许多律师宁可花大钱去建立网站而不是再去用模板网站,是怎么回事呢? 要找到出答案,就需要了解二点: 一、是总体趋势。 互联网客户每天通过搜索引擎和律师网站来寻求法律援助超过一百万次。从未来发展的来看,如公司的地址...

    2023年:一个靠谱的健康搜索该长啥样

    2023年:一个靠谱的健康搜索该长啥样

    “奥密克戎正在让越来越多人胃疼。”如果你在一些大众社交媒体上搜索类似的关键词,你会发现作为胃疼正在成为一项隐秘的“新冠后遗症”。而这些奥密克戎之外的小折磨,都与在各处流行的偏方有关。一些人抱怨被灌入了过量的大蒜水、浓度过高的生姜汤,又或者吃了太多冻梨、饮用了刚被快递进来的冰...

    预防网站关键词突然下降的有效防范措施。

    预防网站关键词突然下降的有效防范措施。

    站长或者是seo优化人员经常会遇到网站关键词排名突然下降的情况,辛辛苦苦做的排名一夜之间回到100名以外,这该怎么办呢?如何防止这种情况发生呢?下面给您做出详细介绍: 接下来我们具体分析一下,做好防范措施: 一、网站经常性改版不可取 网站不可能永远一层不变,所以会面临改版的问题,网站改版时搜索...

    我来分享喜好落伍胚胎,挪动电子商务的o2o是“小而含蓄”。

    我来分享喜好落伍胚胎,挪动电子商务的o2o是“小而含蓄”。

    浆果互动打造热门App方案-通过App讲述品牌故事(PDF下载)浆果App服务内容营销优势将科技转化为新鲜有趣的营销方式技术优势国内顶级技术团队让App惊艳您的想象提供以下专业服务ARApp营销方案策划执行App定制开发:iOS、Android、Winphone7微博应用开发:Wap(HTML5活动...

    抖音直播权限新颖消息详解

    抖音直播权限新颖消息详解

      抖音的玩法一直在变,抖音直播权限也一直在更新。5月上旬抖音还需要3位数的粉丝才能开通直播。但是现在,抖音直播权限又降低了。 抖商公社在近得实操过程中发现,抖音账号在没有任何违规操作的情况下,账号权重正常,没有被限流,被重置等,低仅需30个真实粉丝以上,并发布了10个以上原...

    现在,非常期待与您的又一次邂逅

    我们努力让每一部企业宣传片和抖音短视频成为商业大片