资讯专栏INFORMATION COLUMN

JS 环境执行栈、变量对象、执行上下文

enrecul101 / 248人阅读

摘要:检查上下文中的参数,建立该对象下的属性与属性值检查当前上下文的函数声明,也就是使用关键字声明的函数。

    function test() {
        console.log(a);                // undefined
        console.log(foo());            // 2
        
        var a = 1;
        function foo() {
            return 2;
        };
    };
    test();

不是很准确的草图,大概了解就好。

首先JS解释器(引擎)开始解释代码,构建执行环境栈(Execution Context Stack),并根据执行环境的不同生成不同的执行上下文(Execution Context)

栈底永远是全局上下文(后面说),当遇到test(),确认调用函数,就创建生成test函数自己的上下文,然后将函数执行上下文入栈(push on)到执行环境栈中。

testEC(test Execution Context)将会开始创建变量对象,我们知道,当调用一个函数(激活),一个新的执行上下文就会被创建。而一个执行上下文的生命周期可以分为两个阶段。

创建阶段:
在这个阶段中,执行上下文分别会创建变量对象、建立作用域链,以及确定this的指向。

代码执行阶段:
创建完成后,就会开始执行代码,这个时候,会完成变量赋值,函数引用,以及执行其他代码。

所以testEC将会生成一个变量对象,与之还有作用域链、this(这里先不讨论),注意,这里是变量对象的创建阶段,用VO简示。

变量对象的创建,经历以下过程。

建立arguments对象。检查上下文中的参数,建立该对象下的属性与属性值(key-value)

检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。

检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。

所以,于是就有了图中的VO引用的对象的图示,这就是变量提升的真正原因,对于函数声明,它的优先级是大于变量声明的,所以在创建阶段,函数声明的函数名就持有了函数的引用。而变量赋值需要在后面的执行阶段才被赋值。

未进入执行阶段前,变量对象中的属性都不能被访问!但是进入执行阶段之后,变量对象转变为了活动对象(Active Object),里面的属性都能被访问了,然后开始进行执行阶段的操作。

进入执行阶段,这时可称之为活动对象了(Active Object),将进行赋值操作。于是这时的变量就能被自由访问了。

关于arguments对象,是个类数组结构

目前我的测试:

函数内设有形参,但是不传实参,遍历arguments对象时没有任何值

函数内不设形参,但是传实参,遍历arguments对象有值对应的索引

    function test(a,b,c,d) {
        for (var i in arguments) {
            console.log(i);
        }
        console.log(arguments.length+"个");
    };
    test(1,2,3,4);
    // 0 1 2 3
    // "4个"
    function test2(a,b,c,d) {
        for (var i in arguments) {
            document.write(i+"
") } document.write(arguments.length+"个"); }; test2(); // 什么也没有 // 0个

脑子里有这副图后,我们在来看另一个栗子:

      function test() {
        console.log(foo());
        console.log(bar);
        
        var foo = "Hello";
        console.log(foo);
        var bar = function () {
            return "world";
        };
        function foo() {
            return "hello";
        };
    };
    test();
    // "hello"
    // "undefined"
    // "Hello"

主要讨论同变量声明与同变量声明与函数声明的情况,这也是变量对象在创建时所做的工作;

检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。

检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。

如代码中的foo,在test函数的执行上下文创建变量对象后,创建阶段,foo就是变量对象中的的键名(Key),而键值就是函数的地址指针,因为函数声明的优先级大于变量声明,所以foo此时就持有了函数的引用,而var foo的变量声明就被跳过。

执行阶段,进行赋值操作,foo被重新赋值"Hello",同时变量bar从未赋值(undefined)到持有一个函数的引用,这就是变量对象=>活动对象,执行栈,执行上下文所发生的操作。

关于全局上下文
    var globla = 10;
    function test() {
        return globla++;
    };
    test();
// 以浏览器中为例,全局对象为window
// 全局上下文
windowEC = {
    VO: window,
    scopeChain: {},
    this: window
}

以浏览器中为例,全局对象为window
全局上下文有一个特殊的地方,它的变量对象,就是window对象。而这个特殊,在this指向上也同样适用,this也是指向window
除此之外,全局上下文的生命周期,与程序的生命周期一致,只要程序运行不结束,比如关掉浏览器窗口,全局上下文就会一直存在。其他所有的上下文环境,都能直接访问全局上下文的属性。

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

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

相关文章

  • 33 个 js 核心概念(一):函数调用执行下文变量对象

    摘要:最先执行完毕的一定是最里面的函数,执行过后弹出调用栈,接着执行上一层函数,直至所有函数执行完,调用栈清空。到这里你应该就会明白,上面函数调用栈,就是生成了一个函数的执行上下文。 showImg(http://upload-images.jianshu.io/upload_images/7803415-36e8e7d048f63524.jpg?imageMogr2/auto-orient...

    ZHAO_ 评论0 收藏0
  • 一、 函数调用执行下文变量对象

    摘要:最先执行完毕的一定是最里面的函数,执行过后弹出调用栈,接着执行上一层函数,直至所有函数执行完,调用栈清空。到这里你应该就会明白,上面函数调用栈,就是生成了一个函数的执行上下文。 showImg(http://upload-images.jianshu.io/upload_images/7803415-36e8e7d048f63524.jpg?imageMogr2/auto-orient...

    kbyyd24 评论0 收藏0
  • 一、 函数调用执行下文变量对象

    摘要:最先执行完毕的一定是最里面的函数,执行过后弹出调用栈,接着执行上一层函数,直至所有函数执行完,调用栈清空。到这里你应该就会明白,上面函数调用栈,就是生成了一个函数的执行上下文。 showImg(http://upload-images.jianshu.io/upload_images/7803415-36e8e7d048f63524.jpg?imageMogr2/auto-orient...

    BaronZhang 评论0 收藏0
  • 前端进击的巨人(一):执行下文执行变量对象

    摘要:在中,通过栈的存取方式来管理执行上下文,我们可称其为执行栈,或函数调用栈。而处于栈顶的是当前正在执行函数的执行上下文,当函数调用完成后,它就会从栈顶被推出理想的情况下,闭包会阻止该操作,闭包后续文章深入详解。 写在开篇 已经不敢自称前端小白,曾经吹过的牛逼总要一点点去实现。 正如前领导说的,自己喝酒吹过的牛皮,跪着都得含着泪去实现。 那么没有年终完美总结,来个新年莽撞开始可好。 进击巨...

    _Suqin 评论0 收藏0
  • 大话javascript 2期:执行下文执行下文

    摘要:在中,通过栈的存取方式来管理执行上下文,我们可称其为执行栈,或函数调用栈。因为执行中最先进入全局环境,所以处于栈底的永远是全局环境的执行上下文。 一、什么是执行上下文? 执行上下文(Execution Context): 函数执行前进行的准备工作(也称执行上下文环境) JavaScript在执行一个代码段之前,即解析(预处理)阶段,会先进行一些准备工作,例如扫描JS中var定义的变量、...

    denson 评论0 收藏0
  • 从底层看JS执行机制

    摘要:作用域链用于表明上下文的执行顺序。当前上下文执行完毕则出栈,执行下一个上下文。 从一个简单的例子出发 先从一个简单的例子出发(先不涉及异步),看看自己是否大致了解浏览器的执行机制: console.log(a); var a=1; function foo(a){ console.log(a); var a=2; console.log(a); } foo(a)...

    thursday 评论0 收藏0

发表评论

0条评论

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