zoukankan      html  css  js  c++  java
  • 一次关于执行上下文的深入了解

      最近在刷 冴羽 大大的JavaScript深入系列文章 很良心的文章,再看到第8章 JavaScript深入之执行上下文 的时候发现一个很有趣的题目。

       这里做个笔记。把之前的内容串起来。。毕竟看文章不如自己写一遍心得,好记性不如烂笔头。。。

      以下的内容很多参考了 冴羽 大大的JavaScript深入系列文章 以及 九死蚕传人bo的前端基础进阶目录

    底下的一条评论吸引了我的眼光。。 ,checkscope已经出栈了,为啥子checkscope上下文入栈时创建的变量对象还能访问= =。。 后面才想清楚是关于数据结构的问题。 执行上下文是保存在栈结构的。一般来说,执行上下文出栈,那么入栈的过程中创建的变量也会被回收= = 。。。后面才想到。 可执行代码执行完了,执行上下文就出栈了,变量的回收基于垃圾回收机制,只有当变量没有再被引用的时候才会被回收。这是两种不同的数据结构问题。 简单说就是执行上下文的出栈取决于可执行代码执行完了,变量的回收取决于垃圾回收机制,虽然变量是在执行上下文入栈的时候创建的,但是这是两码子关系。就好比:大明生了一个儿子小明,但是小明寄样在婆婆家(堆结构),大明住在自己家(栈结构),大明家着火大明去世(出栈)并不会造成小明也跟着去世(被垃圾回收机制回收)

      两端代码看似结果一样,但是内在的执行上下文栈缺不一样。 这里尽量还原一下为啥是这种结果,个人心得。推荐小伙伴们直接去看原链接

    1.数据结构:堆,栈,队列

      首先js中JavaScript的执行上下文沿用了栈结构,即我们经常说的执行上下文栈,栈是一个什么样的数据结构呢。简单说就是:先进后出,后进后出。就像高中时化学实验的量筒,假设往量筒中放入乒乓球的模型一样

      一号球是最开始放进去的,5好球是最后放入的。但是如果要把所有球拿出来,那么就是5号球先拿出来,一号球最后。执行上下文栈就是这种结构。

      堆的结构类似于图书馆的藏书架子一样,书本整齐的放在书架上,你只需要知道相对应的书架行号和列号,那么就可以直接拿到这本书,类似于对象的数据格式一样 。

      js中的变量都以堆的形式放在内存中

      

    var obj = {name:'cat',age:2} //假设我要拿到cat这个值,我只要知道name这个参数 直接使用obj.name就可以拿到
    

    队列 

        队列的机构类似于人的消化系统一样。。先吃进去的东西先消化,后出的东西必须等前面吃的消化完了之后才能消化。总结就是:先进先出,后进后出。 js中事件的循环机制依托于队列结构。

     

    2.执行上下文

      竟然知道了执行上下文是以栈的结构存在的,那么执行上下文到底是啥。

      我们都知道js引擎是按顺序一行一行的去编译代码的,每当js引擎遇到可执行代码的时候就会创建一个执行上下文,可以理解为当前代码执行所处的环境。而这个可执行代码的环境分为三种

    1. 全局环境:JavaScript代码运行起来会首先进入该环境 (script标签)
    2. 函数环境:当函数被调用执行时,会进入当前函数中执行代码( fn() )
    3. eval(不建议使用,可忽略)

      这里注意一点。。。是函数调用的时候才会创建一个执行上下文 函数声明的时候并不会 

      当创建一个执行上下文的时候又会分两个步骤 1:创建阶段 2执行阶段

      创建阶段干了三件事  

    • 创建变量对象 (vo variable object)
    • 对this的赋值 (  这里说明了为啥this是在调用的时候确定值的,而不是函数声明的时候确定值得. 函数调用--创建上下文 -- this进行赋值 )
    • 建立作用域链  (   作用域是在函数声明时候确定的  js的作用域是词法作用域 词法作用域 = 声明时就已经确定了 动态作用域 = 在调用的时候确定,类似于this的赋值= =  注意一下作用域链是作用域的嵌套关系确定的。。 老哥们去看大大们的文章吧= = 我也算是一点点懂 )

       其中变量对象的创建又干了三件事情 。。。为啥这么多事情 懵逼中

    1. 建立arguments对象。检查当前上下文中的参数,建立该对象下的属性与属性值。
    2. 检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。
    3. 检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。

       总结:函数声明优先于变量声明,函数声明会覆盖前面的声明,变量声明如果前面已经声明过了就跳过不会覆盖  (这里涉及到了声明提升的面试问题啦。。。)

       举个栗子:

      

      执行流程是这样滴

      皮拉巴拉创建完之后就进入到了执行阶段,当可执行代码全部执行完成当前执行上下文出栈,也就是说当函数内的代码执行完毕之后它就出栈释放了。ok 看一下最开始放的两端代码的执行上下文栈结构

     代码分析

      第一部分

     1 var scope = "global scope";
     2 function checkscope(){
     3     var scope = "local scope";
     4     function f(){
     5         return scope;
     6     }
     7     return f(); // 相当于 var _content = f() ; return _content 就是先执行f() 然后把f()执行后返回的scope 再一次返回出去
     8 }
     9 checkscope();
    10 
    11 //执行上下文栈的情况
    12 /*  ECStack 栈结构
    13 *   1首先全局执行上下文global context 入栈在栈底      ---ECStack = [global context]
    14 *   2执行到底16行 checkscope函数调用执行 checkscope context 入栈  ---ECStack = [global context , checkscope context]
    15 *   3这时候进入第13行checkscope函数内部执行 遇到第17行 f()函数调用 f context入栈 --- ECStack = [global context,checkscope context, f context]
    16 *   4进入f函数内部 执行到第16行 f函数可执行代码完成全部执行完毕出栈 --- ECStack = [global context,checkscope context]
    17 *   5这时候重新返回checkscope函数第18行 返回f函数执行完返回的scope  checkscope可执行代码完成全部执行完毕出栈 --- ECStack = [global context]
    18 *   6ok 这时候只剩下全局上下文。。 全局上下文只有在网页关闭的时候才出栈
    19 *
    20 */

      第二部分

      

    var scope = "global scope";
    function checkscope(){
        var scope = "local scope";
        function f(){
            return scope;
        }
        return f;
    }
    checkscope()(); //相当于 var _test = checkscope() ;
                    //  _test()
    
    //执行上下文栈的情况
    /*  ECStack 栈结构
    *   1首先全局执行上下文global context 入栈在栈底      ---ECStack = [global context]
    *
    *   2执行到底9行 checkscope函数调用执行 checkscope context 入栈  ---ECStack = [global context , checkscope context]
    *
    *   --------------------这里都与前面的第一段代码一样 ------------------------------------
    *
    *   3这时候进入第9行checkscope函数内部执行  执行到第7行全部代码执行完毕了 checkscope context出栈 ---ECStack = [global context] 。 但是因为返回了一个f函数的引用 虽*然出栈了,但是垃圾回收机制因为函数f的引用还在被占用(下面f函数会被调用) 没法进行回收形成闭包(相当于第九行的注释 )
    *
    *   4 第九行第二个括号相当于第十行 f函数进行调用 f context 入栈 --- ECStack = [global context,f context] 。注意这里和上面第一部分代码的区别 checkscope已经出栈了
    *
    *   5 f函数可执行代码完成全部执行完毕出栈  --- ECStack = [global context]
    *
    *   6 ok 这时候只剩下全局上下文。。 全局上下文只有在网页关闭的时候才出栈
    *
    */

       这时候就回答了这个问题啦 第二部分的代码checkscope确实是出栈了 只是因为形成了闭包 返回了一个f函数的引用 垃圾回收机制无法进行回收  好咯 吃饭去咯

      

  • 相关阅读:
    docker
    mitmproxy
    20145103《JAVA程序设计》课程总结
    20145103第五次实验报告
    20145103《JAVA程序设计》第十周学习总结
    《JAVA程序设计》第九周学习总结
    第四次实验报告
    第三次实验报告
    《java程序设计》第八周学习总结
    20145103 《Java程序设计》第7周学习总结
  • 原文地址:https://www.cnblogs.com/carrotWu/p/8797871.html
Copyright © 2011-2022 走看看