精华内容
下载资源
问答
  • 背景:在面试中经常会遇到函数和变量提升,作用域等问题,如果想深入理解其原理,那么首先要弄清楚函数执行上下文执行上下文栈这两个概念。 再次之前先介绍下栈的数据结构: 总结起来一句话:新的数据从栈顶...

    背景:在面试中经常会遇到函数和变量提升,作用域等问题,如果想深入理解其原理,那么首先要弄清楚函数执行上下文和执行上下文栈这两个概念。

    再次之前先介绍下栈的数据结构:


    总结起来一句话:新的数据从栈顶压入,弹出数据也是从栈顶进行弹出,也就是我们所说的弹夹原理。

    1.执行上下文(Excution Context)

    执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。

    全局环境:JavaScript代码运行起来会首先进入该环境

    函数环境:当函数被调用执行时,会进入当前函数中执行代码

    eval(不建议使用,可忽略)

    2.执行环境栈(执行上下文栈Excution Context Stack)

    JavaScript执行在单线程上,所以的代码都是排队执行.栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。当一开始浏览

    器执行全局的代码时,首先创建唯一的一个全局的执行上下文,并将其压入执行栈的顶部(在浏览器关闭的时候出栈).当没进入一个函数

    的执行就会创建新的函数执行上下文,并相应的压入执行栈的顶部.当前函数完成之后,当前函数的执行上下文从栈顶出栈,等待垃圾回

    收。

    3.执行上下文的生命周期



    总的生命周期:创建-->执行-->出栈等待销毁

    创建阶段:
    A 创建变量对象:首先初始化函数的参数arguments,初始化函数声明,初始化变量(undefined)。函数的优先级要高于变量,如果变
    量和函数名重名,变量会被忽略。
    a 创建arguments对象,检查上下文,初始化参数名称和值并创建引用的复制。
    b 扫描上下文的函数声明(而非函数表达式)
    1.每找到一个函数,在变量对象variableObject上创建一个属性-----切确的说是函数的名字---属性值就是指向该函数在内存中的地址的一个引用。
    2.如果上述函数名已经存在于variableObject下,那么对应的属性值会被新的引用所覆盖。
     扫描上下文的变量声明
    1.每找到一个变量声明,就会在变量对象上创建一个属性---就是变量名字,并且将变量的值初始化为undefined
    d.确定上下文内部this的指向
    B 创建作用域链

    执行阶段:

             执行变量赋值,代码执行

    回收阶段:

             执行上下文栈等待垃圾回收机制回收上下文

    案例:(下面代码用来说明执行上下文栈的工作原理)
    	//变量声明
    	var a1 = 9,
    	a2 = 8,
    	a3 = "sss",
    	b1 = {name:"xixi"};
    
    	//函数调用
    	a1 = f1(a1,a2);
    	
    	//函数声明
    	function f1(a,b){
    		//f1函数的执行上下文
    		/*
    			1.扫描参数: a = 9 b = 8
    			2.扫描函数声明 f2 = function(){}
    			3.扫描变量声明 t = undefined , m = undefined , i = undefined
    		*/
    		var t = 0,
    		m = 10;
    
    		for(var i=0;i<a;i++){
    			console.log(i);
    		}
    
    		function f2(){
    			console.log("f2");
    		}
    
    		return a+b;
    	}
    

    环境栈示意图:


    以上是关于执行上下文执行环境栈的说明,关于作用域的问题可以参考本博客高级JavaScript部分的文章。
    展开全文
  • 一、 执行上下文 1、理解执行上下文 广义上来说: 执行上下文一套非常严格的规则 狭义上来说: 执行上下文一个c++对象 2、 定义:执行上下文以一套非常严格的规则,规范变量存储、提升、this指向。 3、分类:...

    一、 执行上下文

    1、理解执行上下文
    广义上来说:
    执行上下文一套非常严格的规则
    狭义上来说:
    执行上下文一个c++对象

    2、 定义:执行上下文以一套非常严格的规则,规范变量存储、提升、this指向。
    3、分类: 全局执行上下文;函数执行上下文
    3、作用:对数据进行预处理
    4、如何计算执行上下文环境的个数?

    • n+1
    • n:函数调用的次数,函数每调用一次,开启一个对全局数
    • 1:全局上下文环境
        <!--    
        执行上下文(也称执行上下文环境)
                  定义:执行上下文以一套非常严格的规则,用来规范
                             函数体代码执行前,变量声明并赋值(默认为underfined)
                                                             函数声明,函数和变量提升,this赋值等操作。
                  分类: 全局执行上下文;函数执行上下文
    
                   什么时候创建执行上下文环境?
                            全局执行上下文:全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
                            函数执行上下文:函数调用时;
    
                  如何计算执行上下文环境的个数?
                            n+1
                             n:函数调用的次数,函数每调用一次,执行上下文环境。
                             1:执行上下文环境      
    
                  注意:执行上下文是附属于自己对应的作用域的
                       一个作用域在一个时刻可以拥有多个执行上下文,当处于活动状态的只有一个
    
        -->
        <script type="text/javascript">
    
            function wrap(){
                console.log("函数执行上下文环境")
            }
            wrap();
            wrap();
            wrap();
            wrap();
            wrap();
            //6个执行上下文环境
            console.log("全局执行上下文环境")
        </script>

    二、 执行上下文栈

    1、 理解压栈

    • 当全局代码开始执行前,先创建全局执行上下文环境
    • 当全局执行上下文环境创建好了以后将上下文中的所有内容放入栈内存
    • 最先放入的在最下边(global)
    • 其他执行的函数的执行上下文依次放入(放入的顺序是代码的执行顺序)
    • 栈中最后放入的执行完最先出栈。
      这里写图片描述

    2、 执行上下文栈是执行上下文的活动记录。

    <!--    
         执行上下文栈
                                   定义: 执行上下文栈是执行上下文的活动记录(数据的出栈&压栈)。
            1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
            2. 在全局执行上下文确定后, 将其添加到栈中(压栈)
            3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
            4. 在当前函数执行完后,将栈顶的对象移除(出栈)
            5. 当所有的代码执行完后, 栈中只剩下在全局执行上下文
    
        一个作用域在一个时刻可以拥有多个执行上下文,当处于活动状态的只有一个
        -->
    <script type="text/javascript">
    
        function wrap() {
            console.log("函数执行上下文环境")
        }
    
        //      wrapp执行时同时可有5个上下文环境,但处于活动状态的只有一个.
        (function wrapp() {
            var b = 2;
    
            function inner() {
                var c = 3;
    

    三、作用域和执行上下文

    区别1:
    1. 除全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时。
    2. 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建。
    3. 函数执行上下文环境是在调用函数时, 函数体代码执行之前创建。
    总结:作用域是在代码编译时确定的, 全局执行上下文环境在代码执行前确定, 函数执行上下文环境在函数体代码执行前创建。

    区别2:
    1. 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
    2. 上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会被释放
    总结:
    作用域是静态的,上下文环境是动态的。
    执行上下文是附属于自己对应的作用域的。

    区别3:
    一个作用域 在一个时刻可以有多个上下文,处于活动状态的上下文只有一个。

    联系
    1. 上下文环境(对象)是从属于所在的作用域
    2. 全局上下文环境==>全局作用域
    3. 函数上下文环境==>对应的函数使用域

    
    <!--    
        作用域&执行上下文的区别与联系:
           区别:
            1、什么时间确定?
                           作用域:
                                       全局作用域&局部作用域都是在代码编写时确定的。
                             执行上下文环境:  
                                       全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
                                        函数执行上下文环境是在调用函数时, 函数体代码执行之前创建  
    
            2、状态 
                             作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
                                   上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会被释放
    
           联系:
            1. 上下文环境(对象)是从属于所在的作用域
            2. 全局上下文环境==>全局作用域
            3. 函数上下文环境==>对应的函数作用域
            -->
        <script type="text/javascript">
    
        </script>

    四、规则

    1、全局执行上下文
    在执行全局代码前将window确定为全局执行上下文
    对全局数据进行预处理:

    • 1.var定义的全局变量==>undefined, 添加为window的属性
    • 2.function声明的全局函数==>赋值(fun), 添加为window的方法
    • 3.提升
    • 4.this==>赋值(window)

    开始执行全局代码.
    2、函数执行上下文
    在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象
    对局部数据进行预处理:

    • 1.形参变量==>赋值(实参)
    • 2.arguments==>赋值(实参列表)
    • 3.处理 var定义的局部变量
    • 4.处理 function声明的函数
    • 5.提升
    • 6.this==>赋值(调用函数的对象)

    开始执行函数体代码
    3、变量的查找
    如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的处于活动状态的执行上下文环境,再在其中寻找变量的值

        <!--    
        执行上下文的规则:
                  全局执行上下文
                                  在执行全局代码前将window确定为全局执行上下文
                                   对全局数据进行预处理
                        1.var定义的全局变量==>undefined, 添加为window的属性
                        2.function声明的全局函数==>赋值(fun), 添加为window的方法
                        3. 提升
                        4.this==>赋值(window)
                                   开始执行全局代码
                         函数执行上下文
                                   在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象
                                              对局部数据进行预处理
                        1.形参变量==>赋值(实参)
                        2.arguments==>赋值(实参列表)
                        3.处理 var定义的局部变量
                        4.处理 function声明的函数
                        5.提升
                        6.this==>赋值(调用函数的对象)
                                              开始执行函数体代码
                         变量的查找
                                    如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的处于活动状态的执行上下文环境,再在其中寻找变量的值
    
    -->
    
        <script type="text/javascript">
        /*全局执行上下文
            1、var a=underfined;
            2、
        */  
            function wrap(b) {
                /*函数执行上下文
    
               */
                var c = b;
                function inner(b){
    
                }
            }
            var a = "a";
            wrap(1);
        </script>

    五、提升

    1、变量提升

    • 通过var定义(声明)的变量, 在定义语句之前就可以访问到
    • 值: undefined

    2、 函数提升

    • 通过function声明的函数, 在之前就可以直接调用
    • 值: 函数定义(对象)

    3、 提升注意点

    • 函数的提升是整体的提升
    • 变量的提升是声明的提升
    • 函数的提升优于变量的提升
    • 提升是指提升到本层作用域的最顶层

    4、注意事项和使用细节

    • 变量的提升不会理会if else 这种条件暗示,会跳出流程,进行提示。
    • 永远不要再流程控制语句的块内部定义函数。
    <!--    
        提升:提升都是提升到本层作用域的最顶层
                         变量:声明提升
                         函数:整体提升
                         函数表达式的提升本质是变量的提升。
                         函数提升优于变量提升。
               注意:变量的提升不会理会if else 这种条件暗示,会跳出流程,进行提示。
                          永远不要再流程控制语句的块内部定义函数。
                          -->
        <script type="text/javascript">
    
            function wrap(b) {
    
                var c = b;
                function inner(b){
    
                }
            }
            var a = "a";
            wrap(1);
            /*提升后如下:
             全局:
             function wrap(b) {
    
                var c = b;
                function inner(b){
    
                }
            }
            var a = underfined;
             a = "a";
             函数:
             function wrap(b) {
                function inner(b){
    
                }
                var c = underfined;
                c = b;
    
            }
             */
        </script>

    六、this到底指向谁?

    1、之前this使用总结:

    <script type="text/javascript">
            /*  之前的this总结
                     new fn()   fn的this--->实例
                     fn() fn的this---> window 
                     fn.call(a) fn的this---> a 
                     fn.apply(b) fn的this---> b 
                     定时器的回调中的this ----> window 
                */
            function wrap() {
                console.log(this)
            }
            setTimeout(function() {
                console.log(this);
            }, 1000)
            new wrap();
            console.log(new wrap())
            wrap();
            wrap.call(a);
            wrap.apply(b)
        </script>

    2、全局上下文
    无论是否在严格模式下,在全局执行上下文中(在任何函数体外部)this 都指代全局对象。

        <!--    
             全局上下文
                             无论是否在严格模式下,在全局执行上下文中(在任何函数体外部)this 都指代全局对象window。
    -->
        <script type="text/javascript">
            console.log(this)
        </script>

    3、函数上下文:在函数内部,this的值取决于函数被调用的方式。
    * 默认绑定*:独立调用(普通调用):
    this 的值默认指向全局对象。
    代码在严格模式(“use strict”)下执行,默认this指向underfind

    </body>
        <!--
               函数上下文:在函数内部,this的值取决于函数被调用的方式。
                         默认绑定:独立调用(普通调用):
                    this 的值默认指向全局对象。
                                               代码在严格模式(“use strict”)下执行,默认this指向underfind
                 注意:虽然 this 的绑定规则完全取决于调用位置,但是只有 foo()运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下调用foo()不会影响默认绑定规则
    
               总结:无论函数是在哪个作用域中被调用,只要是独立调用则就会按默认绑定规则被绑定到全局对象或者undefined上
       -->
        <script type="text/javascript">
             //独立调用
            function wrap() {
                console.log(this.a)
            }
            var a = 1;
            wrap();
    
            //函数运行在严格模式中
            "use strict";
            function wrap1() {
                console.log( this.b );
            }
            var b = 2;
            wrap1();
    
    
            // 在严格模式下进行函数调用
            var c = 3;
            function wrap2() {
                console.log( this.c );
            }
            (function(){
                "use strict";
                wrap2();
            })()
    
    //      虽然 this 的绑定规则完全取决于调用位置,但是只有 foo()运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下调用foo()不会影响默认绑定规则
            function foo() {
                console.log( this.a );
            }
            (function(){
                "use strict";
                foo(); 
            })();
    
    
            //如果使用严格模式( strict mode ),this 会绑定到 undefine
            function wrap3() {
                "use strict";
                console.log( this.a );
            }
            wrap3(); 
        </script>

    隐式绑定

    隐式调用(obj.属性):使用隐式绑定规则,this指向离他最近的调用者。
    当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。

        <!--
              函数上下文:在函数内部,this的值取决于函数被调用的方式。
                         隐式绑定 :隐式调用(obj.属性):使用隐式绑定规则,this指向离他最近的调用者。
                                              当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。       
               隐式绑定的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含
        当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象
    
        对象属性引用链中只有最顶层或者说最后一层会影响调用位置                            
       -->
        <script type="text/javascript">
    
            function wrap() {
                console.log( this.a );//this指向调用者
            }
            var obj = {
                a: 2,
                foo: wrap
            };
            obj.foo(); 
            //  对象属性引用链中只有最顶层或者说最后一层会影响调用位置
            function wrap1() {
                console.log( this.a );
            }
            var obj2 = {
                a: 42,
                foo: wrap1
            };
            var obj1 = {
                a: 24,
                obj2: obj2
            };
            obj1.obj2.foo(); 
        </script>
    

    显式绑定

    可以使用函数的 call(..) 和 apply(..) 方法来实现显示绑定
    硬绑定:在一个包裹函数内使用显示绑定去修改一个指定函数的this指向,最终将其返回出来!这种包裹函数对指定函数的约束,我们称之为硬绑定 。

    <!--
     函数上下文:在函数内部,this的值取决于函数被调用的方式。
    显式绑定:可以使用函数的 call(..) 和 apply(..) 方法来实现显示绑定
    硬绑定(bind):在一个包裹函数内使用显示绑定去修改一个指定函数的this指向,最终将其返回出来!这种包裹函数对指定函数的约束,我们称之为硬绑定    。   
    
       -->
        <!--
        我们不想在对象内部包含函数引用,而想在某个对象上强制调用函数
        具体点说,可以使用函数的 call(..) 和 apply(..) 方法来实现显示绑定
    
    -->
        <script type="text/javascript">
            function wrap(a, b) {
                console.log(this.a, a, b)
            }
            var obj = {
                a: 2
            };
    //      call()&apply()的区别:传入参数的不同
            wrap.call(obj, "a", "b");
            wrap.apply(obj, ["c", "d"]);
            wrap.bind(obj)("e", "f")
        </script>

    new 绑定:作为构造函数绑定
    当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

        <!--
       函数上下文:在函数内部,this的值取决于函数被调用的方式。
        new 绑定:作为构造函数绑定
                                   当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。
        JavaScript中的“构造函数”:
                JavaScript,构造函数只是一些使用 new 操作符时被调用的函数。
                                    它们并不会属于某个类,也不会实例化一个类。
                                    实际上,它们甚至都不能说是一种特殊的函数类型,它们只是被 new 操作符调用的普通函数而已。  
                                    实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”
               使用 new 来调用函数,或者说发生构造函数调用时,对于我们的this来说,这个新对象会绑定到函数调用的 this 。
       -->
        <script type="text/javascript">
            /*使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上。
                  new 是最后一种可以影响函数调用时 this 绑定行为的方法,我们称之为 new 绑定。
            */
            function foo(a) {
                this.a = a;
            }
            var bar = new foo(2);
            console.log(bar.a); // 2
        </script>

    this绑定 优先级

    new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

    4、函数上下文隐式绑定的问题:

    1)隐式丢失(以隐式绑定的形式进行传参或者赋值 最终却以其他形式调用函数 导致this指向发生偏差)

     <!--隐式丢失: 以隐式绑定的形式传入,最终以独立调用的方式调用,产生了隐式丢失-->
         <script type="text/javascript">
    //   write()函数改变了write的this的指向,让它指向global或window对象,
    //          导致执行时提示非法调用异常
            var write=document.write();
            write("1111111111111");
            document.write("2222222222");
    
         </script>

    2)怎么解决隐式丢失?——硬绑定

    <script type="text/javascript">
    
            var write = document.write.bind(document);
            write("123");
    
    
            var obj={
                age:18,
                test:function(a,b){
                    console.log(this.age,a,b);
                }
            }
    
            setTimeout(obj.test.bind(obj,1,2),1000);
    
            /*
            call  apply
                    将指定函数中的this绑给他们的第一个参数,并且进行调用!!!
            bind
                将将指定函数中的this绑给他的第一个参数,并且返回一个新的this指向已经定死的硬绑定函数!!
            */
        </script>

    5、显式绑定变种_硬绑定
    1)在一个包裹函数内使用显示绑定去修改一个指定函数的this指向,最终将其返回出来!这种包裹函数对指定函数的约束,我们称之为硬绑定

    <!--    
    显式绑定变种_硬绑定如何工作?
               创建函数 bar() ,并在它的内部手动调用了 bar.call(obj) ,
               因此强制把 foo 的 this 绑定到了 obj 。无论之后如何调用函数 bar ,它总会手动
        在 obj 上调用 foo 。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。
        -->
        <script type="text/javascript">
            function foo() {
                console.log(this.a);
            }
    
            var bar = function() {
                foo.call(obj);
            }
            var obj_test = {
                a: "test"
            };
            var a = 1;
            var obj = {
                a: "a"
            }
            bar();
            setTimeout(bar, 1000);
            bar.call(obj_test); //  硬绑定的bar不可能再修改它的this(指的是foo中的this)
        </script>

    2)硬绑定函数_bind函数

    <!--
        简单的辅助绑定函数    bind函数的作用:返回一个新的函数,并且指定该新函数的this指向
    -->
    <script>
        //  类bind函数的实现
        function foo(something, otherthing) {
            console.log(this.a + " " + something + " " + otherthing);
            return this.a + something;
        }
    
        function bind(fn, obj) {
            return function () {
                return fn.apply(obj, arguments);
            };
        }
    
        var obj = {
            a: 2
        };
        var obj_test = {
            a: 22
        };
    
        var bar = bind(foo, obj);
        var b = bar(3); // 2 3 undefined
        console.log(b); // 5
        bar.call(obj_test, 3); //2 3 undefined
    </script>

    3)典型应用场景
    硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接收到的所有值

    <!--硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接收到的所有值-->
        <script>
    
        function foo(arg1,arg2) {
            console.log( this.a,arg1,arg2);
            return this.a + arg1;
        }
        var obj = {
            a:2
        };
        var bar = function() {
            return foo.apply( obj, arguments);
        };
    
    
        var b = bar(3,2); 
        console.log( b ); 
        </script>

    6、this污染:
    独立调用时,this把函数中方法挂载全局window上,造成全局变量的污染.

        <script type="text/javascript">
            /*
                    function app(){
                        function inner(){
                            console.log(inner);
                        }
                        var inner;
                        inner();
                    }
                    app();
                */
            /*
                this污染:
                      独立调用时,this把函数中方法挂载全局window上,造成全局变量的污染.
                */
            function app2() {
                //this 有没有污染全局环境??
                this.test = function() {
    
                };
                this.toString = function() {
    
                }
            }
            app2();
            console.log(window)
            app2.apply(Object.create(null));
            console.log(Object.create(null));
        </script>

    6、注意事项和使用细节:

    1)虽然 this 的绑定规则完全取决于调用位置,但是只有 foo()运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下调用foo()不会影响默认绑定规则
    2) JavaScript中的“构造函数”:
    JavaScript,构造函数只是一些使用 new 操作符时被调用的函数。
    它们并不会属于某个类,也不会实例化一个类。
    实际上,它们甚至都不能说是一种特殊的函数类型,它们只是被 new 操作符调用的普通函数而已。

    展开全文
  • JS之执行上下文执行上下文

    千次阅读 2018-12-04 18:43:49
    分享一下JS中很基础也很重要的概念:执行上下文(Execution Context); 将我们的代码进行分类: 1.代码分类(位置):  * 全局代码  * 函数(函数)代码. 2.全局执行上下文  (1) 在执行全局代码前将window确定为全局...

    分享一下JS中很基础也很重要的概念:执行上下文(Execution Context);

    将我们的代码进行分类:

    1.代码分类(位置):
              * 全局代码
              * 函数(函数)代码.

    2.全局执行上下文
              (1) 在执行全局代码前将window确定为全局执行上下文
              (2)对全局数据进行预处理
                * var定义的全局变量===>undefined,添加为window的属性
                * function声明的全局函数===>赋值(fun),添加为window的方法
                * this===>赋值(window) 
              (3) 开始执行全局代码

    3.函数执行上下文
              (1) 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象 
              (2) 对局部数据进行预处理
                * 形参变量==>赋值(实参),添加为执行上下文的属性 
                * arguments==>赋值(实参列表),添加为执行上下文的属性
                * var定义的局部变量==>undefined,添加为执行上下文的属性
                * function声明的函数==>赋值(fun),添加为执行上下文的属性
                * this==>赋值(调用函数的对象)
              (3) 开始执行函数体代码

    // 全局执行上下文
    	console.log(a1) // undefined
    	console.log(a2) // 可以访问到
    	var a1 = 3
    	function a2(){}
    
    
    	// 函数执行上下文
    	function fn(b1){
    		console.log(b1) // 2
    		console.log(b2) // undefined
    		console.log(b3)
    		b3()
    		console.log(this) // window
    		console.log(arguments) //[2,3]伪数组
    
    
    		var b2 = 3
    		function b3(){
    			console.log('b3')
    		}
    
    
    	}
    	fn(2,3)

    JS中并没有严格意义上区分栈内存和堆内存,因此我们可以简单粗暴的理解为JavaScript的所有数据都保存在堆内存中。但是在某些场景,我们仍然需要基于堆栈数据结构的思维来实现一些功能,比如JavaScript的执行上下文.执行上下文的执行顺序借用了栈数据结构的存取方式。(也就是我们说的函数调用栈).

    栈的存取方式是先进后出.

    当代码的执行过程中,遇到全局代码和函数代码都会生成一个执行上下文放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈。

    执行上下文栈:

    1.在全局代码执行前,JS就会创建一个栈来储存管理所有的执行上下文对象
    2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
    3.在函数执行上下文创建后,将其添加到栈中(压栈)
    4.当当前函数执行完后,将栈顶的对象移除(出栈)
    5.当所有的代码执行完后,栈中只剩window(全局上下文)
        注意: 1.全局上下文在浏览器窗口关闭后出栈
                  2.函数执行中,遇到return直接终止可执行的代码,会直接将当前上下文弹出栈

    function f1(){
    	    var n=999;
    	    function f2(){
    	        console.log(n);
    	    }
    	    return f2;
    	}
    	var result=f1();
    	result(); // 999
    	console.log(result)

    我们分析一下这段代码中执行上下文的顺序:

    第一步,首先是全局上下文入栈

    全局上下文入栈之后,其中的可执行代码开始执行,直到遇到了f1();这一句激活函数f1创建它自己的执行上下文,因此第二步就是f1的执行上下文入栈。

    f1中的可执行代码并没有调用f2,所以没有创建新的上下文,直到return f2时才创建.这时候f1可执行代码完毕,出栈

    遇到result(),创建result的执行上下文

    执行result中的可执行代码,输出n,执行完毕后,result出栈

    这个时候只剩下全局上下文,全局上下文在浏览器窗口关闭后出栈.

    对执行上下文总结一下:

    • 执行上下文是单线程的
    • 是同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
    • 全局上下文只有唯一的一个,它在浏览器关闭时出栈
    • 函数的执行上下文的个数没有限制
    • 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。

     

     

     

    展开全文
  • 因为想弄透闭包,所以想研究研究作用域链的,但是这两天在查资料的过程中发现原来作用域链的背后还有更需要深入了解的知识,就是本文要记的知识点:执行上下文执行上下文堆栈。理解了这个就更更方便理解作用域链...

    因为想弄透闭包,所以想研究研究作用域链的,但是这两天在查资料的过程中发现原来作用域链的背后还有更需要深入了解的知识,就是本文要记的知识点:执行上下文和执行上下文堆栈。理解了这个就更更方便理解作用域链以及之后的闭包啦。(刚开始看的时候,有点晕菜的感觉,写的不好或有误的地方,还请指出)。

    一、执行上下文(execution context)

    1. 定义很简单
      在文章ECMA-262-3 in detail. Chapter 1. Execution Contexts. 中的定义是:

    Every time when control is transferred to ECMAScript executable code, control is entered an execution context.

    每次控制器转到ECMAScript可执行程序的时候,控制器进入执行上下文。

    EExecution context (abbreviated form — EC) is the abstract concept used by ECMA-262 specification for typification and differentiation of an executable code.

    执行上下文(简称-EC)是ECMA-262定义的一个抽象概念,用来和可执行代码进行区分。上面提到的文章中写到可能是ECMAScript引擎考虑的问题。
    2. 理解

    这个定义并没有实质内容的定义,execution context也解释为执行环境(自我感觉比上下文好理解一些)。那么根据这个定义便可以这么理解:每个可执行代码,都会有一个自己的执行上下文(EC),比如全局的JS代码(可能存在函数实例,不包括函数体内的代码)会有个全局的上下文(global context),对于函数实例的每次执行(是实例不是定义,包括函数被递归调用或作为构造函数)都会有一个函数执行的上下文(function context,每次调用时的函数上下文都不同),同样的,这个函数上下文不包括其内部函数的代码(inner functions)。

    比如下面这段代码:

    var a = "123";
    function fun(arg){
    var b = arg;
    }
    fun(a);
    fun("o2");

    上下文情况如图(这只是我自己理解的情况,不代表真实情况)

    执行上下文

    当解释器进入这段js代码之后,进入全局代码的globalContext,globalContext可能包含变量,函数声明,可能还有其他属性等等 ,当js代码执行到fun(a)这个函数实例时,进入到这个函数实例的functionContext,functionContext可能包含传入参数,arguments,自己的变量,还有其他属性等,这两个执行上下文有关系,但不是从属关系。

    在js代码中有另一个函数实例,在fun(a)之后还有个fun(“o2”),执行到fun(“o2”)时,会进入另一个函数的上下文functionContext2,里面的参数为“o2”,变量b 等于 “o2”,这两个函数上下文是两个不同的上下文,以此类推同一个函数的每个实例的上下文都是不同的。

    参考文章JavaScript. The core. 对执行上下文的解释和图示。

    一个执行上下文可以抽象的表示为一个简单的对象。如下图所示
    Execution context
    An execution context structure.

    每个上下文对象都有一系列的必需的属性,称为上下文状态,用来追踪相关代码的执行情况。上面图中有三个属性:variable object(变量对象),this value(this值),scope chain(作用域链),除了这个三个属性,在不同的情况下,执行上下文对象可能还有其他的属性。

    二、执行上下文堆栈(Execution context stack)

    执行上下文直接的关系可以用堆栈来表示,一个执行上下文可以激活另一个执行上下文,比如进入js全局程序之后,进入全局上下文,在全局上下文运行程序的过程中,如果遇到函数调用,那么就会激活对应的函数上下文,并进入这个函数上下文中运行代码,当这个函数执行结束后,就会退出这个函数上下文,并重新回到全局上下文,直到整个程序结束(或抛出异常)就退出全局上下文。
    这一过程在逻辑上可以看做堆栈。不过全局上下文始终位于堆栈底部,而堆栈顶部是当前活动的执行山下文,堆栈在进入和退出上下文的时候执行推入和弹出。
    如下图为一个函数上下文(EC1)和全局上下文(Global EC)在堆栈中的执行过程。
    这里写图片描述

    程序开始后,进入全局上行文,将Global EC push到堆栈底部,执行程序的一系列操作,遇到函数1时,进入函数上下文EC1,并将EC1 push到堆栈中,执行函数的一系列操作,函数执行结束后,pop出EC1,回到Global EC继续执行全局上下文,直到程序结束,pop出Global EC。

    参考文献

    1. ECMA-262-3 in detail. Chapter 1. Execution Contexts.
    2. JavaScript. The core.
    3. 深入理解JavaScript系列(11):执行上下文(Execution Contexts)
    4. 深入理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)
    展开全文
  • js执行上下文

    2019-11-29 16:39:55
    定义:每调用一次函数就会产生一个函数执行上下文,全局执行上下文默认会产生且只产生一个 分类: 全局执行上下文 有且只有一个 函数执行上下文 可以有多个 eval函数执行上下文 特点: 先进后出,后进先出 出口只有...
  • 本文主要会讲解我们经常看到执行上下文知识点,旨在帮助自己和大家加深对它理解。主要内容包括:1、描述执行上下文;2、执行栈;3、模拟执行栈;4、思考题。思维导图+流程图+代码的方式全方位轰炸你的大脑,一起进步...
  • javaScript执行上下文

    2021-04-16 13:31:39
    一、执行上下文是什么? 执行上下文用大白话来说也就是代码在被解析以前或者在执行时候所处的环境 二、执行上下文分类 , 执行上下文分为全局执行上下文,函数执行上下文, Eval(不常用,不做过多解释): 1.全局执行...
  • JavaScript执行上下文执行上下文

    千次阅读 2018-07-22 20:36:28
    JavaScript执行上下文执行上下文栈 变量提升与函数提升 1、变量声明提升 通过var定义(声明)的变量,在定义语句之前就可以访问到 值为:undefined 2、函数声明提升 通过function声明的函数,在之前就可以...
  • 在这个系列文章里,我首先要说的是,闭包是和执行上下文,作用域是有紧密的联系的,不能单独的去理解闭包,否则就容易走入死胡同。 这篇讲执行上下文。 一、什么是执行上下文 我们可以将执行上下文看作代码当前...
  • JavaScript的执行上下文

    2020-10-12 21:18:42
    这里写自定义目录标题JavaScript的执行上下文执行上下文是什么?JavaScript的执行顺序总结 JavaScript的执行上下文 第一遍翻红宝书,读到执行上下文这一块时觉得十分生涩难懂,一直卡在上下文这几个字眼导致后面的...
  • 深入理解JavaScript执行上下文

    千次阅读 2018-05-12 15:22:17
    在《深入理解JavaScript执行上下文栈》这篇文章中,我们已经介绍了执行上下文相关概念: 执行上下文 分类:全局上下文、函数上下文 全局上下文:执行全局代码时,创建全局上下文。 函数上下文:执行函数时,...
  • js系列六:执行上下文

    千次阅读 2019-02-23 22:37:25
    javascript代码在执行时,会进入一个执行上下文中,执行上下文可以理解为当前代码的运行环境。 javascript中运行环境主要包括以下三种情况 1 全局环境:代码运行起来首先会进入全局环境 2 函数环境:当函数被调用...
  • Execution Context(执行上下文

    千次阅读 2019-06-09 17:57:51
    在这一部分我们将会提及js中的一些执行上下文(execution context),还有与执行上下文中相关联的可执行代码(executable code). Execution Context是通过ECMA 262 规范用于Exectuable code的类型和区分的抽象概念。该...
  • 在上一篇里也给出了答案,我们不是在作用域里取到变量的值,而是在作用域所对应的执行上下文取到变量的值,并且可能同样的作用域,相同的变量取到的值是不同的。 在这里,提一句很重要的话,作用域在函数创建的时就...
  • JavaScript之执行上下文(EC)

    千次阅读 2020-07-13 23:37:15
    JavaScript之执行上下文(EC) 在JavaScript中,执行上下文是一个基本概念,在执行上下文中,包括了变量对象(VO,Variable Object),作用域链(Scope Chain),this指向等,在闭包,函数/变量提升息息相关 执行上...
  • 一、JS执行上下文 执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行。 执行上下文类型分为:全局执行上下文和函数执行上下文执行上下文...
  • 说明此文译自Dmitry A.Soshnikov 的文章Execution Context概要本文将向大家介绍ECMAScript的执行上下文以及相关的ࡤ#21487;执行代码类型。定义每当控制器到达ECMAScript可执行代码的时候,控制器就进入了一个执行上...
  • js中的执行上下文和作用域

    千次阅读 2017-12-06 10:36:36
    一、执行上下文1、什么是执行上下文? 分析一个问题之前,首先我们都需要知道它是什么。执行上下文又称词法环境,它是存储和处理数据的栈,主要分为两部分: 预处理阶段: ①在这个阶段它会将所有的var类型的...
  • 深入javascript之执行上下文

    千次阅读 2018-04-02 22:48:28
    前言:深入js对学习框架很重要,希望这一系列文章会对你有帮助(持续更新中)深入系列:深入javascript之原型和原型链深入系列:深入javascript之作用域一,相关概念 EC : 执行上下文 ECS : 执行环境栈 VO : ...
  • JavaScript 深入之执行上下文

    千次阅读 2017-04-14 12:36:45
    JavaScript深入系列第七篇,结合之前所讲的四篇文章,以权威指南的demo为例,具体讲解当函数执行的时候,执行上下文栈、变量对象、作用域链是如何变化的。https://juejin.im/user/58e4b9b261ff4b006b3227f4 ...
  • 说说JS的执行上下文

    千次阅读 2018-09-21 03:24:32
    先推荐几篇微信文章链接,有兴趣的小伙伴可以看看。 1、内存空间详细图解 ...2、执行上下文详细图解 http://mp.weixin.qq.com/s/hRE3HzeSxxok1bLI8vH1yw 3、变量对象详解 http://mp.weixin.qq.com/s/LijjPErxcF...
  • 下面的这些概念,无论是执行上下文、 还是执行栈,它在规范中的概念都很抽象,很多内容的理解实际靠的都是想象力,若有错误之处,还请指正。 执行上下文 简而言之,执行上下文(Execution Context)是正在运行的可...
  • 深入理解javascript执行上下文

    千次阅读 2016-08-10 09:46:09
    在这篇文章中,将比较深入地阐述下执行上下文 - Javascript中最基础也是最重要的一个概念。相信读完这篇文章后,你就会明白javascript引擎内部在执行代码以前到底做了些什么,为什么某些函数以及变量在没有被声明...
  • JS - 执行上下文(上)

    千次阅读 2017-01-10 15:24:28
    什么是“执行上下文”(也叫做“执行上下文环境”)?暂且不下定义,先看一段代码: 第一句报错,a未定义,很正常。第二句、第三句输出都是undefined,说明浏览器在执行console.log(a)时,已经知道了a是undefined,...
  • Zephyr OS 内核篇: 执行上下文

    千次阅读 2016-10-07 17:04:03
    Zephyr OS 所有的学习笔记已托管到 Github,CSDN 博客里的内容只是 Github 里内容的拷贝,因此...本文简要介绍一下 Zephyr OS 执行上下文的基本概念以及相关的 API,这是后面学习的基础。 上下文的概念 上下文类型的
  • 1. 执行上下文(execution context) 2. 变量对象(variable object) 执行上下文 我们都知道JavaScript的作用域一共分三种 全局作用域 函数作用域 eval作用域 实际上每一次的函数调用都会有一个对应的执行...
  • 深入JavaScript 执行上下文(一):作用域 深入JavaScript 执行上下文(二):执行上下文栈 深入JavaScript 执行上下文(三):变量对象 作用域链 当要查找某变量的时候,会先从当前上下文的变量对象中查找,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 619,245
精华内容 247,698
关键字:

执行上下文