作用域的原理,对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定义时的作用域链,而不是被调用时的作用域链,对于看懂多层函数嵌套的情况下作用域链的坑,也是非常关键的。

JavaScript:理解执行环境、作用域链和活动对象的更多相关文章

  1. 深入理解javascript中执行环境(作用域)与作用域链

    深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...

  2. javascript中函数的执行环境、作用域链、变量对象与活动对象

    javascript高级程序设计中:对执行环境.作用域链.变量对象.活动对象的解释: 1.执行环境: 执行环境:有时也叫环境:是JavaScript中最为重要的一个概念:执行环境定义了变量或函数有权访 ...

  3. JavaScript之执行环境及作用域

        执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为.每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中.我们编写的代码是无法访问这个对象的,但解 ...

  4. Javascript 的执行环境(execution context)和作用域(scope)及垃圾回收

    执行环境有全局执行环境和函数执行环境之分,每次进入一个新执行环境,都会创建一个搜索变量和函数的作用域链.函数的局部环境不仅有权访问函数作用于中的变量,而且可以访问其外部环境,直到全局环境.全局执行环境 ...

  5. javascript中执行环境和作用域(js高程)

    执行环境(execution context,为简单起见,有时也成为“环境”)是javascript中最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为.每个执行环境 ...

  6. Javascript手记-执行环境和作用域

    执行环境是javascript一个重要的概念,执行环境定义了变量有权访问其他数据决定了他们各自的行为,每个执行环境 都有一个与之关联的变量,环境中定义的所有变量和函数都保存在这个对象中,虽然我们编写的 ...

  7. 从头开始学JavaScript (九)——执行环境和作用域

    原文:从头开始学JavaScript (九)--执行环境和作用域 一.执行环境:定义了变量或者函数有权访问的其他数据,决定了它们各自的行为.每个执行环境都有与之关联的变量对象. 变量对象:保存着环境中 ...

  8. 【进阶2-2期】JavaScript深入之从作用域链理解闭包(转)

    这是我在公众号(高级前端进阶)看到的文章,现在做笔记   https://github.com/yygmind/blog/issues/18 红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一 ...

  9. JavaScript中执行环境和栈

    在这篇文章中,我会深入理解JavaScript最根本的组成之一 : "执行环境(执行上下文)".文章结束后,你应该对解释器试图做什么,为什么一些函数/变量在未声明时就可以调用并且他 ...

随机推荐

  1. Project Euler 19 Counting Sundays( 蔡勒公式计算星期数 )

    题意:在二十世纪(1901年1月1日到2000年12月31日)中,有多少个月的1号是星期天? 蔡勒公式:计算 ( year , month , day ) 是星期几 以下图片仅供学习! /****** ...

  2. win主机ping不通linux的IP

    1.虚拟机的中的linux系统设置成桥接模式 2.点击虚拟机的编辑选择虚拟网络编辑器 3.点击更改设置 4点击还原默认设置即可

  3. Loaded APR based Apache Tomcat Native library 1.1.24 using APR version 1.4.6.

    Loaded APR based Apache Tomcat Native library 1.1.24 using APR version 1.4.6. 我复制的几个地方: MySql C:\WIN ...

  4. C#中的Dynamic

    dynamic dyn = (dynamic)1; int j = (int)dyn; 可以看到1被强制转换成dynamic,然后又被强制转换回int. 然而dynamic可以隐式的转换成任何类型,并 ...

  5. DynaActionForm(动态ActionForm)的使用

    在struts中利用DynaActionForm(动态ActionForm)可以节省代码的编写. 1.在struts-config.xml中配置DynaActionForm:加入这个Form中有三个属 ...

  6. 在centOS6.5 上安装使用pipework

    需求:镜像生成了2个含有tomcat的容器,用nginx进行负载均衡.但是容器重启后ip会自动改变...所以使用pipework进行分配静态ip pipework安装 OS:centos6.5 第一步 ...

  7. centos: git clone提示Permission denied publickey 问题

    问题: Initialized empty Git repository in /data1/mouxuan/fastsocket-private/.git/ Permission denied (p ...

  8. 【iOS开发-78】用代码实现UITabBarController+UINavigationController

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2Vpc3ViYW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...

  9. BestCoder Round #75 King's Order dp:数位dp

    King's Order Accepts: 381 Submissions: 1361 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 655 ...

  10. Cocos2d-x3.0 RenderTexture(三)

    .h #include "cocos2d.h" #include "cocos-ext.h" #include "ui/CocosGUI.h" ...