资讯专栏INFORMATION COLUMN

深究JavaScript——执行上下文

netScorpion / 3156人阅读

摘要:一系列活动的执行上下文从逻辑上形成一个栈。堆栈中的上下文就会被依次执行并且弹出堆栈,直到回到全局的上下文。确定上下文中的指向对象确定指向或当前对象。

定义

执行上下文(也称执行环境execution context)(简称:EC)是个抽象的概念,是在函数被调用时,但是在函数体被真正执行以前所创建的。每个执行上下文都有一个变量对象(variable object),保存着当前环境中所有的变量或函数声明或形参arguments以及this。

   一系列活动的执行上下文从逻辑上形成一个栈。栈底总是全局上下文,栈顶是当前(活动的)执行上下文。当执行流进入函数时,函数的环境就会被推入一个环境栈中,在函数执行之后,栈再将其环境弹出,把控制权返回给之前的执行环境。
   代码在一个环境中执行,会创建变量对象的一个作用域链,作用域链保证了执行的顺序。

三种运行环境

全局:

   只存在一个全局的上下文,该上下文能被任何其它的上下文所访问到。

函数:

   每调用执行一个函数时,引擎就会自动新建出一个函数执行上下文,就是新建一个局部作用域,可以在该局部作用域中声明私有变量等,在外部的上下文中是无法直接访问到该局部作用域内的元素的。
   当在全局上下文中调用执行一个函数时,程序流就进入该被调用函数内,此时引擎就会为该函数创建一个新的执行上下文(即使是调用自身函数),并且将其压入到执行上下文堆栈的顶部。浏览器总是执行当前在堆栈顶部的上下文,一旦执行完毕,该上下文就会从堆栈顶部被弹出,然后,进入最新的堆栈顶部的上下文执行代码。堆栈中的上下文就会被依次执行并且弹出堆栈,直到回到全局的上下文。

eval函数

  在eval函数内运行的代码。

执行上下文建立、运行过程 建立阶段:

发生在调用一个函数时,但是在执行函数体内具体的代码以前。

建立variableObject对象

先是处理arguments参数,接着是函数的声明,最后是变量的声明。

  建立arguments对象,检查当前上下文中的参数,建立该对象下的属性以及属性值。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。

  检查当前上下文中的函数声明:找到函数声明的函数名,在variableObject下面用该函数名建立一个属性,属性值就是指向该函数在内存中的地址的一个引用;如果上述函数名已经存在于variableObject下,那么对应的属性值会被新的引用所覆盖。

   检查当前上下文中的变量声明:每找到一个变量的声明,就在variableObject下,用变量名建立一个属性,属性值为undefined。如果该变量名已经存在于variableObject属性中,直接跳过(防止指向函数的属性的值被变量属性覆盖为undefined),原属性值不会被修改。

初始化作用域链

  每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。 ECMAScript 程序中的执行流正是由这个方便的机制控制着。当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

确定上下文中this的指向对象

  确定this指向window或当前对象。

代码执行阶段

给variableObject中的变量赋值

function aoo(i){
    var a = "staven";
    var b = function boo(){
        
    };
    function coo(){
        
    }
}
aoo(1);

调用aoo(1),建立阶段执行下上文对象:

aooExecutionContext = {
    variableObject:{
        arguments:{
            0:1,
            length:1
        },
        i:1,
        coo:(指向function coo()),
        a:undefined,
        b:undefined
    },
    scopeChain:{……},
    this:{……}
};

运行阶段,执行上下文对象:

aooExecutionContext = {
    variableObject:{
        arguments:{
            0:1,
            length:1
        },
        i:1,
        coo:(指向function coo()),
        a:"staven",
        b:(指向function boo())
    },
    scopeChain:{……},
    this:{……}
};
解析变量提升
(function(){
    console.log(typeof foo);  //function
    console.log(typeof bar); //undefined
    
    var foo = "hello",
        bar = function(){
            return "staven";
        };
    function foo(){
        return "hello";
    }
    console.log(typeof foo); //string
    console.log(typeof bar); //function
}());

  因为在上下文的建立阶段,先是处理arguments, 参数,接着是函数的声明,最后是变量的声明。那么,发现foo函数的声明后,就会在variableObject下面建立一个foo属性,其值是一个指向函数的引用。当处理变量声明的时候,发现有var foo的声明,但是variableObject已经具有了foo属性,所以直接跳过。当进入代码执行阶段的时候,就可以通过访问到foo属性了,因为它已经就存在,并且是一个函数引用。
  bar是变量的声明,在建立阶段的时候,被赋予的默认的值为undefined。由于它只要在代码执行阶段才会被赋予具体的值,所以,当调用typeof(bar)的时候输出的值为undefined。

☞☞☞深究JavaScript系列☜☜☜

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87889.html

相关文章

  • 深究JavaScript——函数调用与this详解

    摘要:构造器的目的是要创建一个新对象并对其进行设置,然后将其作为构造器的返回值进行返回,是通过函数调用初始化创建新对象。或方法进行调用通过或调用函数,被调用的函数的指向第一个参数指向的。 this   JavaScript的this总是指向一个对象,而这个对象是基于函数运行时动态绑定的,并非函数声明时绑定。 函数调用方式 作为对象的方法调用 作为普通函数进行调用 作为构造器进行调用 通过a...

    PiscesYE 评论0 收藏0
  • WebComponent魔法堂:深究Custom Element 之 面向痛点编程

    摘要:前言最近加入到新项目组负责前端技术预研和选型,一直偏向于以为代表的技术线,于是查阅各类资料想说服老大向这方面靠,最后得到的结果是资料是英语无所谓,最重要是上符合要求,技术的事你说了算。但当我们需要动态实例化元素时,命令式则是最佳的选择。 前言  最近加入到新项目组负责前端技术预研和选型,一直偏向于以Polymer为代表的WebComponent技术线,于是查阅各类资料想说服老大向这方面...

    flyer_dev 评论0 收藏0
  • 深究JavaScript——闭包

    摘要:函数是中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。闭包中的匿名函数的执行环境具有全局性,因此其对象通常指向在通过或改变函数执行环境的情况下,就会指向其他对象。当匿名函数执行完毕,其作用域链立即销毁,从而可以减少闭包占用资源问题。 概念   闭包是指能够引用外部函数中的局部变量的函数,并导致外部函数调用后函数对象与局部变量无法及时销毁。函数是JavaScript中唯一拥有自身作...

    Dionysus_go 评论0 收藏0
  • (深究)声明提前(Hoisting)

    摘要:有意思的是,这意味着变量在声明之前甚至已经可用。的这个特性被非正式地称为声明提前,即函数里声明的所有变量但不涉及赋值都被提前至函数体的顶部。但实际上会将其看成两个声明和。第二个赋值声明会被留在原地等待执行阶段。 简介 JavaScript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的。有意思的是,这意味着变量在声明之前甚至已经可用。JavaScript的这个特性被非正式地...

    陈伟 评论0 收藏0
  • 深究JavaScript——参数传递与变量复制

    摘要:在操作对象时,实际上是在操作对象的引用而不是实际的对象。任一变量值操作互不影响。访问变量有按值和按引用两种方式,而参数只能按值传递。基本类型参数传递传给函数的是数值的一个复制,函数中对其的修改外部不可见。    ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。 基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。   5 种基本数据类...

    jzman 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<