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函数的引用 垃圾回收机制无法进行回收  好咯 吃饭去咯

      

  • 相关阅读:
    Oracle中有大量的sniped会话
    Error 1130: Host '127.0.0.1' is not allowed to connect to this MySQL server
    汉字转换为拼音以及缩写(javascript)
    高效率随机删除数据(不重复)
    vs2010 舒服背景 优雅字体 配置
    mvc中的ViewData用到webfrom中去
    jquery ajax return值 没有返回 的解决方法
    zShowBox (图片放大展示jquery版 兼容性好)
    动感效果的TAB选项卡 jquery 插件
    loading 加载提示······
  • 原文地址:https://www.cnblogs.com/carrotWu/p/8797871.html
Copyright © 2011-2022 走看看