Scope Chain(作用域链)
本章,我们讨论一下ECMAScript中的作用域链 , 开门见山。
什么是作用域链
i.ECMAScript是允许创建内部函数的,甚至能从父函数中返回这些函数。作用域链正是内部上下文中所有变量对象(及自由对象)的列表。此链用来变量解析查询。
作用域链的特性
i.是执行上下文的一个属性
- activeExecutionContext = {
- vo : {},
- this : thisValue,
- scope : []
- }
ii.逻辑上是一个数组,每一个元素是一个变量对象
iii.定义为:Scope = ActiveContext.VO + Function.[[Scope]] ([[Scope]] 是函数的属性)
理解作用域链
i.[[Scope]] 是函数的私有属性,在函数被解析时创建,不会改变。
为了让大家更好的理解,先让大家看一段代码:
- var x = 'test';
- function foo() {
- console.log(x);
- }
- (function() {
- var x = 'what';
- foo();
- })();
上面的代码输出会是什么呢?为什么?
控制台将会输出 'test',而非 'what' 。这个例子也说明,一个函数的[[Scope]] 持续存在,即使是在函数创建的作用域已经完成之后。
ii.[[Scope]] “通常”(存在意外) 包含了父级函数的[[Scope]]属性,ECMAScript依靠这个特性来实现闭包。
iii.[[Scope]]是函数的属性,这也意味着ECMAScript中没有Java那样的块级作用域。(ES6中对这一块得到了加强)只有函数级作用域。
观察以下3种函数构造方式的差异:
- var x = 10;
- function foo() {
- var y = 20;
- // 函数声明方式创建
- function innerFoo() {
- console.log(x ,y);
- }
- //函数表达式方式创建
- var innerFoo2 = function() {
- console.log(x ,y);
- }
- //构造函数方式创建
- var innerFoo3 = Function('console.log(x);console.log(y);');
- innerFoo(); //10 20
- innerFoo2(); //10 20
- innerFoo3(); //10 ,y not defined
- }
通过以上代码,我们可以看出,通过Function构造函数创建的方法只拥有全局作用域
iiii.变量的二维链式查找
变量的解析是通过作用域链来实现的。
变量本质是上以变量对象的属性方式存在,当变量对象与JavaScript中对象重叠时,它就会天然的受到原型链的影响。
一段有趣的代码:
- function foo() {
- console.log(x);
- }
- Object.prototype.x = 10;
- foo(); //
原因: 此时,全局对象为 window(假设在浏览器中运行该段代码),而window对象是Object所派生的。根据原型链查找规则,实例中访问不到的属性和方法,将会在原型中查找。
以下面的代码为例,其查找顺序是这样的:
iiiii.全局代码和 eval 的作用域链
全局代码中的作用域链仅包含全局对象
eval的上下文与当前 calling context(调用上下文) 拥有相同的作用域链
ES5中规定,如果对eval建立别名(非直接调用),这时作用域链仅包含全局对象
iiiiii.作用域链是可以在运行时动态改变的
在with 和 catch 语句中:Scope = withObject || catchObject + VO + [[Scope]]
大多数情况下是不变的,但在with语句和catch语句块中,可以改变作用域链。这种技巧在有些时候非常有用,但大多数情况下,我们要尽可能避免
ES5中,通过词法环境、词法环境记录的方式来扫描这种变化
根据我们上面讲到的,大家看看如下代码:
- var x = 10,
- y = 10;
- with({x: 20}) {
- var x = 30 ,
- y = 30;
- console.log(x ,y); // 30 30
- }
- console.log(x ,y); // 10 30
输出的结果是: 30 30 10 30 ,为什么呢?实际上上面讲到的作用域链的动态改变时,已经对该问题做出了解答, 下面我们分析一下:
在with和catch语块中的作用域链:
1.x = 10 ,y = 10;
2.对象{x:20}被添加到了作用域的前端
3.在with内部,遇到了var声明。但是什么也没创建,因为在进入上下文时,所有变量已被解析添加
4.在步骤2中,仅修改变量'x' ,实际上对象中的'x'现在被解析,并添加到作用域链的前端,'x'由 20 变为 30
5.同样也有变量对象的属性'y'的修改,被解析后其值也由10变为30
6.此外,在with声明完后,它的特定对象从作用域链中移除,(已改变的变量“x”--30也从那个对象中移除),即作用域链的结构恢复到with得到加强以前的状态。
7.最后console中,当前变量对象 'x'保持相同,'y'的值 = 30(在with声明运行中已发生改变)
iiiiiii.结合this一起
直接调用函数,作用域为 withObject
- var x = 10;
- with({
- foo : function() {
- console.log(this.x);
- } ,
- x : 20
- }) {
- foo(); //
- }
总结
理解执行上下文、VO(变量对象)、this、作用域链是理解JavaScript执行的基础,尤其是this和作用域链
Scope Chain(作用域链)的更多相关文章
- JS -- The Scope Chain 作用域链
The Scope Chain JavaScript is a lexically scoped language: the scope of a variable can be thought of ...
- JavaScript的语法要点 2 - Scope Chain
前文所述,JavaScript是基于词法作用域(lexically scoped)的,所以标识符被固定在它们被定义的作用域而不是语法上或是其被调用时的作用域.即全局变量的作用域是整个程序,局部变量的作 ...
- javascript 执行环境,变量对象,作用域链
前言 这几天在看<javascript高级程序设计>,看到执行环境和作用域链的时候,就有些模糊了.书中还是讲的不够具体. 通过上网查资料,特来总结,以备回顾和修正. 要讲的依次为: EC( ...
- javascript 之作用域链-10
前言 在<执行环境>文中说到,当JavaScript代码执行一段可执行代码时,会创建对应的执行上下文(execution context). 变量对象(Variable object,VO ...
- VO、AO、执行环境和作用域链
1.变量对象(variable object) 原文:Every execution context has associated with it a variable object. Variabl ...
- JavaScript——执行环境、变量对象、作用域链
前言 这几天在看<javascript高级程序设计>,看到执行环境和作用域链的时候,就有些模糊了.书中还是讲的不够具体.通过上网查资料,特来总结,以备回顾和修正. 目录: EC(执行环境或 ...
- JavaScript 作用域链解析
JavaScript 中有 Scope( 作用域 ) , Scope chain( 作用域链 ) , Execute context( 执行上下文 ) , Active Object ( 活动对象 ) ...
- 【转】javascript 执行环境,变量对象,作用域链
这篇文章比较清晰的解释了一些作用域链相关的概念,忍不住收藏了 原文地址:http://segmentfault.com/a/1190000000533094 前言 这几天在看<javascrip ...
- (翻译) How variables are allocated memory in Javascript? | scope chain | lexicial scope
总结: 阅读下面文章需要15分钟 提问者的问题是JavaScript中内存是怎么分配的,在介绍的过程作者涉及计到了JS中 Scope Chain和调用函数call生成lexicial environm ...
随机推荐
- git 创建多个账户ssh
创建一个账户 创建ssh本地秘钥. $ ssh-keygen -t rsa -C "youremail@xxx.com" 一路回车,会在~/.ssh/目录下生成id_rsa和id_ ...
- SecurityCRT输出日志重定向
使用CRT进行抓取log,因为工具本省缓冲区有限,导致,刷屏特别快,可能会错过一些log,可以对CRT的log进行增加输出源,或者说将输出到控制台的log再输出到本地文件中: 文件->点击(勾选 ...
- 2015_WEB页面前端工程师_远程测题_东方蜘蛛_1
请使用HTML+CSS实现如下效果: 1. 使用CSS Sprites,实现如图1效果,素材图片为: icons.png: 2. 使用脚本语言验证邮箱.密码的必填以及邮箱的合法性: 若验证失败,则出现 ...
- java程序编译
Empoyee.java package Company; public class Empoyee { String name = ""; public Empoyee(Stri ...
- golang_protobuf环境搭建
搭建golang使用rotobuf使用环境 一 安装protobuf: 1 下载protobuf源码:https://github.com/google/protobuf 2 进入源码目录: ./au ...
- 简单易学的机器学习算法——EM算法
简单易学的机器学习算法——EM算法 一.机器学习中的参数估计问题 在前面的博文中,如“简单易学的机器学习算法——Logistic回归”中,采用了极大似然函数对其模型中的参数进行估计,简单来讲即对于一系 ...
- Eclipse中Android公共库的正确建立及调用方法
Eclipse中Android公共库的正确建立及调用方法 引言 之前一直头痛于没有办法在多个程序中共享资源,用作公共类库的方法也是使用的导出jar再导入的办法,现在终于初步搞明白了,可算解脱了~,分享 ...
- Asm.js: Javascript的编译目标
正如许多开发者一样,我也为Asm.js的前景而感到兴奋不已.最近的新闻——Asm.js正 在被Firefox支持——引起了我的兴趣.同样感兴趣的还有Mozilla和Epic声明(mirror)他们已经 ...
- spoj PARTIT
三维DP 第K字典序从左向右找 根据dp数组的值算出每一位该打印什么 代码: #include <cstdio> #include <cstring> using namesp ...
- JSON和XML:不可同日而语
[编者按]本文作者 Yegor Bugayenko 是 Teamed.io 公司的联合创始人,在软件质量和工程管理方法领域有深入的研究.本文中,作者通过对比 JSON ,向大家更详细地阐述了 XML ...