理解Javascript_15_作用域分配与变量访问规则,再送个闭包 【转】

在阅读本博文之前,请先阅读《理解Javascript_13_执行模型详解》

在‘执行模型详解‘中讲到了关于作用域分配的问题,这一篇博文将详细的说明函数对象、作用域链与执行上下文的关系。

作用域分配与变量访问规则

  在 ECMAScript 中,函数也是对象。函数对象在变量实例化过程中会根据函数声明来创建,或者是在计算函数表达式或调用 Function 构造函数时创建。(关于‘函数对象‘请见《理解Javascript_08_函数对象》)。每个函数对象都有一个内部的 [[scope]] 属性,这个属性也由对象列表(链)组成。这个内部的[[scope]] 属性引用的就是创建它们的执行环境的作用域链,同时,当前执行环境的活动对象被添加到该对象列表的顶部。当我们在函数内部访问变量时,其实就是在作用域链上寻找变量的过程。

理论性太强了(总结死我了!),还是让我们来看一段代码吧:


1

2

3

4

5

6

7

8

9

10

11

12

13

<script type="text/javascript">

function outer(){

    var i = 10;

    function inner(){

        var j = 100;

        alert(j);//100

        alert(i);//10

        alert(adf);

    }

    inner();

}

outer();

</script>

下图清晰的展现了上述代码的内存分配与作用域分配情况:

下面来解释一下:

1.载入代码,创建全局执行环境,此时会在可变对象(window)中添加outer变量,其指向于函数对象outer,此时作用域链中只有window对象.

2.执行代码,当程序执行到outer()时,会在全局对象中寻找outer变量,成功调用。

3.创建outer的执行环境,此时会新创建一个活动对象,添加变量i,设置值为10,添加变量inner,指向于函数对象inner.并将活动对象压入作用域链中.并将函数对象outer的[[scope]]属性指向活动对象outer。此时作用域链为outer的活动对象+window.

4.执行代码,为 i 成功赋值。当程序执行到inner()时,会在函数对象outer的[[scope]]中寻找inner变量。找到后成功调用。

5.创建inner的执行环境,新建一个活动对象,添加变量j,赋值为100,并将该活动对象压入作用域链中,并函数对象inner的[[scope]]属性指向活动对象inner.此时作用域链为:inner的活动对象+outer的活动对象+全局对象.

6.执行代码为j赋值,当访问i、j时成功在作用域中找到对应的值并输出,而当访问变量adf时,没有在作用域中寻找到,访问出错。

注:通过内存图,我们会发现作用域链与prototype链是如此的相象。这说明了很多问题...(仁者见仁智者见智,自己探寻答案吧!)

闭包原理

在我们了解了作用域的问题之后,对于闭包这个问题已经很简单了。什么是闭包?闭包就是封闭了外部函数作用域中变量的内部函数。

我们来看一个典型的闭包运用:生成increment值


1

2

3

4

5

6

7

8

9

10

11

<script type="text/javascript">

var increment = (function(){

    var id = 0;

    return function(){

        return ++id;

    }

})()

alert(increment());//1

alert(increment());//2

</script>

外层匿名函数返回的是一个内嵌函数,内嵌函数使用了外层匿名函数的局部变量id。照理外层匿名函数的局部变量在返回时就超出了作用域因此increment()调用无法使用才对。这就是闭包Closure,即函数调用返回了一个内嵌函数,而内嵌函数引用了外部函数的局部变量、参数等这些应当被关闭(Close)了的资源。这是怎么一回事呢?让我们来寻找答案:

根据Scope Chain的理解可以解释,返回的内嵌函数已经持有了构造它时的Scope Chain,虽然outer返回导致这些对象超出了作用域、生存期范围,但JavaScript使用自动垃圾回收来释放对象内存: 按照规则定期检查,对象没有任何引用才被释放。因此上面的代码能够正确运行。

参考:


1

2

3

<a href="http://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.htmlhttp://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html">http://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.html

http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html

</a>

时间: 2024-10-26 09:05:03

理解Javascript_15_作用域分配与变量访问规则,再送个闭包 【转】的相关文章

我之理解js作用域,作用域链与变量提升

function test4(){ console.log("test4:"+a) } test4(); var a=1; function test(){ var test1=function () { var test2=function () { console.log("test>test1>test2:"+a) } test2(); } test1(); } function test3(){ console.log("test3:&

深入理解javascript作用域系列第一篇——内部原理

× 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域貌似简单,实则复杂,由于作用域与this机制非常容易混淆,使得理解作用域的原理更为重要.本文是深入理解javascript作用域系列的第一篇——内部原理 内部原理分成编译.执行.查询.嵌套和异常五个部分进行介绍,最后以一个实例过程对原理进行完整说明 编译 以var a = 2;为例,说明javasc

Objective-C之成魔之路【9-类构造方法和成员变量作用域、以及变量】

重开发者的劳动成果,请勿转载 构造方法 出于初始化类中的成员变量的需要, 可以提供一个方法用于此目的, 这个方法就叫构造方法或构造方法(Constructor). 与C++和Java不同, Objective-C命名是没有限制的, 并且有返回值本身类型指针. 以音乐类举例: Song.h文件 @interface Song : NSObject { NSString *title; NSString *artist; long int duration; } //操作方法 - (void)sta

深入理解javascript作用域系列第二篇——词法作用域和动态作用域

× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作模型导致的,作用域分为词法作用域和动态作用域,分清这两种作用域模型就能够对变量查找过程有清晰的认识.本文是深入理解javascript作用域系列第二篇——词法作用域和动态作用域 词法作用域 第一篇介绍过,编译器的第一个工作阶段叫作分词,就是把由字符组成的字符串分解成

深入理解javascript作用域系列第四篇——块作用域

× 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀.简洁的代码,比如块作用域.随着ES6的推广,块作用域也将用得越来越广泛.本文是深入理解javascript作用域系列第四篇——块作用域 let for (var i= 0; i<10; i++) { console.log(i); } 上面这段是很熟

JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

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

什么是对象?对象命名和访问规则,主要是数字和特殊字这种键名

傻瓜式的理解,对象就是包在大括号{}里面的东西,对象有键/值                      key/value //简而言之,js对象只要不符合js变量命名规则的都需要加上引号,无论是在定义的时候还是引用的时候,且对象不能像数组那样使用下标访问,只能使用键名key(属性名)访问 var obj = {'1':3,'@**^%*':'我是符号哦',3:4,'tom ni hao':2}; console.log(obj[1]);//3 ,虽然这里新版Chrome没有报错,但因其不符合j

内部类(嵌套类、内置类)访问规则、定义原则、静态内部类、匿名内部类

一.内部类 1.内部类的访问规则: 1.内部类可以直接访问外部类中的成员,包括私有.之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式为:外部类明.this 2.外部类要访问内部类,必须创建内部类对象. 2.访问格式 1.当内部类定义在外部类的成员位置上是,而且非私有,可以在外部其他类中 可以直接建立内部类对象 格式: 外部类明.内部类名 变量名=外部类对象.内部类对象: Outer.Inter in=new Outer().new Inner(); 2.当内部类在成员

C++变量命名规则

转自:http://www.cnblogs.com/finallyliuyu/archive/2010/09/25/1834301.html 浅谈C++变量命名规则 不知道别的公司如何,反正我现在的公司对变量命名并没有一定的规范,唯一要求就是能简单易懂,但是,我想,这个多个程序员,大概每个人都有自己习惯的一套 命名规则吧,不过,要是并不通用的话,大概看别人的程序会很头疼吧,SO 为了别人看偶的程序不至于太头疼,偶决定找个通俗的命名法来参考下,于是,搜到了匈牙利命名法... 匈牙利命名法的来历和介