zoukankan      html  css  js  c++  java
  • JavaScript:理解执行环境、作用域链和活动对象

    作用域的原理,对JS将如何解析标识符做出了解答。而作用域的形成与执行环境和活动对象紧密相关。

    我们对于JS标识符解析的判断,存在一个常见误区

    首先,看一个关于JS标识符解析的问题 ,源于风雪之隅提出的问题

    var name = 'globalName';
    function funcA() {
         console.log(name);
         var name = 'funAName';
         console.log(name);
         console.log(age);
    }
     
    funcA();
    

    这段代码的运行结果是怎样的?

    相信会有人跟我最初遇到这个问题时一样,以为结果会是这样:

    globalName
    funAName
    [脚本错误: ReferenceError]
    

    ​ 我们认为:在funA中, 第一次console.log的时候,会取到全局变量name的值'globalName', 而第二次值被局部变量name覆盖, 所以第二次console.log是'funAName'。 而age属性没有定义, 所以脚本会出错。

    但是实际上,运行结果是这样的:

    undefined
    funAName
    [脚本错误: ReferenceError]
    

    ​ 为什么会这样呢?在JS中,标识符解析是沿作用域链一级一级地搜索标识符的过程,搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直到找到标识符为止(如果找不到标识符,通常会导致错误发生)。所以为了正确地判断标识符的解析结果,我们必须把作用域链原理弄清楚。

    深入理解作用域原理能够为我们解密这个误区

    ​ 作用域链(scope chain)的生成跟执行环境(execution context)、函数对象(function object)和活动对象(activation object)紧密相关。

    执行环境

    ​ 执行环境是JavaScript中最为重要的一个概念。执行函数定义了变量或函数有权访问的其它数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(variable object)和一个作用域链(scope chain),环境中定义的所以变量和函数都保存在其变量对象中。执行环境分为两种,一种是全局执行环境,一种是函数执行环境。

    1. 全局执行环境

      ​ 全局执行环境是最外围的一个执行环境,其变量对象就是全局活动对象(window activation object),全局执行环境直到应用程序退出——例如关闭网页或浏览器——时才会被销毁。

    2. 函数执行环境

      ​ 每个函数都有自己的执行环境。当执行流进入一个函数时,函数环境就会被推入一个环境栈中。当函数执行完之后,栈将其环境弹出,把控制权返回给之前的执行环境。函数执行环境的变量对象是该函数的活动对象(activation object)。

    作用域链

    ​ 对于每一个执行环境,都会创建一个与之关联的作用域链。每个执行环境的作用域链的前端,始终都是该执行环境的变量对象,对于全局执行环境就相当于window对象,对于函数执行环境就相当于该函数的活动对象;对于全局执行环境,已经是根部,没有后续,对于函数执行环境,其作用域链的后续是该函数对象的[[scope]]属性里的作用域链。

    函数对象

    ​ 在一个函数定义的时候, 会创建一个这个函数对象的[[scope]]属性(内部属性,只有JS引擎可以访问, 但FireFox的几个引擎(SpiderMonkey和Rhino)提供了私有属性__parent__来访问它),并将这个[[scope]]属性指向定义它的作用域链上。 在这里的问题中,因为funcA定义在全局环境, 所以此时的[[scope]]只是指向全局活动对象window active object。

    活动对象

    ​ 在一个函数对象被调用的时候,会创建一个活动对象,首先将该函数的每个形参和实参,都添加为该活动对象的属性和值;将该函数体内显示声明的变量和函数,也添加为该活动的的属性(在刚进入该函数执行环境时,未赋值,所以值为undefined,这个是JS的提前声明机制)。

    ​ 然后将这个活动对象做为该函数执行环境的作用域链的最前端,并将这个函数对象的[[scope]]属性里作用域链接入到该函数执行环境作用域链的后端。

    现在让我们回到最初的问题

    var name = 'globalName';
    function funcA() {
    	 //当funcA()被调用时,刚进入funcA的执行环境,其作用域链最前端的funA activation object里有name属性,值为undefined。
         console.log(name);
         var name = 'funAName';
         console.log(name);
         console.log(age);
    }
     
    funcA();
    

    ​ 虽然感觉这个例子的误区跟JS提前声明机制的关系也很大,但是理解funA.[[scope]]属性的作用域链是funA定义时的作用域链,而不是被调用时的作用域链,对于看懂多层函数嵌套的情况下作用域链的坑,也是非常关键的。

  • 相关阅读:
    Maven——下载、安装、环境变量配置
    APP——自动化——java——截图到桌面
    APP——自动化——java——锁屏
    APP——自动化——java——设置和查看横屏和竖屏
    APP——自动化——java——程序后台运行,关闭、启动、是否安装第三方app、删除第三方app、安装第三方app
    APP——自动化——java——程序安装好,直接调用
    APP——自动化——java——初始化安装程序
    APP——自动化——python——driver其他常用的方法
    APP——自动化——python——swipe从坐标点滑动到坐标点以及其它滑动
    APP——自动化——python——设置网络模式
  • 原文地址:https://www.cnblogs.com/amy-fox/p/5856771.html
Copyright © 2011-2022 走看看