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(); //10

原因: 此时,全局对象为 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(); // 20
}    

总结

理解执行上下文、VO(变量对象)、this、作用域链是理解JavaScript执行的基础,尤其是this和作用域链

时间: 2024-11-12 10:10:38

Scope Chain(作用域链)的相关文章

JavaScript 作用域链解析

JavaScript 中有 Scope( 作用域 ) , Scope chain( 作用域链 ) , Execute context( 执行上下文 ) , Active Object ( 活动对象 ),Dynamic Scope( 动态作用域 ) , Closure( 闭包 ) 这些概念,要理解这些概念,我们从静态和动态两个方面去分析一下. 首先我们写一个简单的 function 来做一个例子: function add(num1, num2){ var sum = num1 + num2; r

javascript 执行环境,变量对象,作用域链

前言 这几天在看<javascript高级程序设计>,看到执行环境和作用域链的时候,就有些模糊了.书中还是讲的不够具体. 通过上网查资料,特来总结,以备回顾和修正. 要讲的依次为: EC(执行环境或者执行上下文,Execution Context) ECS(执行环境栈Execution Context Stack) VO(变量对象,Variable Object)|AO(活动对象,Active Object) scope chain(作用域链)和[[scope]]属性 EC 每当控制器到达EC

VO、AO、执行环境和作用域链

1.变量对象(variable object) 原文:Every execution context has associated with it a variable object. Variables and functions declared in the source text are added as properties of the variable object. For function code, parameters are added as properties of

(翻译) How variables are allocated memory in Javascript? | scope chain | lexicial scope

总结: 阅读下面文章需要15分钟 提问者的问题是JavaScript中内存是怎么分配的,在介绍的过程作者涉及计到了JS中 Scope Chain和调用函数call生成lexicial environment和environment record(被作者合并称为 binding objects)的过程.非常值得一看     个人翻译: How variables are allocated memory in Javascript? It's actually a very interesting

深入理解javascript作用域链

之前作用域链在我眼里也只是在调用一个对象时一层一层向上找到自己所需的变量或是函数,若没有则返回undefined,其实大致上说却是这样的,但是我需要的是不断的深入. 在深入理解之前先记住两句话 1.js中一切皆对象,函数也是对象 2.函数运行在他们被定义的作用域内,而不是被执行的作用域内. 当定义函数时,会包含[[scope]]属性(因为js中一切皆对象,函数也是对象),此属性是函数内部属性,只允许js引擎访问,[[scope]]指向作用域链(scope lain),而此时仅包含 所有全局变量.

深入理解JavaScript系列(14):作用域链(Scope Chain)

前言 在第12章关于变量对象的描述中,我们已经知道一个执行上下文 的数据(变量.函数声明和函数的形参)作为属性存储在变量对象中. 同时我们也知道变量对象在每次进入上下文时创建,并填入初始值,值的更新出现在代码执行阶段. 这一章专门讨论与执行上下文直接相关的更多细节,这次我们将提及一个议题——作用域链. 英文原文:http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/ 中文参考:http://www.denisdeng.com/?p

作用域链 The Scope Chain

JavaScript is a lexically scoped language: the scope of a variable can be thought of asthe set of source code lines for which the variable is defined. Global variables are definedthroughout the program. Local variables are defined throughout the func

JavaScript作用域链

JavaScript作用域链 之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时候发现作用域和执行环境确实很重要,又很基础,对理解JavaScript闭包很有帮助,所以在写一篇对作用域和执行环境的理解. 作用域 作用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期,在JavaScript中变量的作用域有全局作用域和局部作用域. 单纯

作用域链

JavaScript 开发进阶:理解 JavaScript 作用域和作用域链 作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript. JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变