精华分享:深爱—— javascript 闭包

上次一篇简单的干货分享,只想让你分分钟学会javascript中的闭包,这就好比一辆车摆在你面前让你束手无策,我只想让你简单的学会驾驶它,仅此而已。而这次我们将从原理层面分析 javascript 引擎如何执行一段代码、一个函数,以及如何形成一个闭包,让你从“爱的初体验” 变得 “深爱” 。

 1 var a = 1;
 2
 3 function f1(b){
 4    var a = 2;
 5    function f2(c){
 6        console.log(a+b+c);
 7    }
 8    return f2;
 9 }
10 var fn = f1(1);
11 fn(1); >>> 4

这是一个很简单的javascript闭包,执行结果是4.

下面我们将从全局初始化、执行f1、执行fn 三个阶段来分析这段代码的执行过程:

1 全局初始化

JS引擎在进入一段可执行的代码时,需要完成以下3步初始化工作:

(1)创建一个全局对象(Global Object) , 这个对象全局只存在一份,它的属性在任何地方都可以访问,它的存在伴随着应用程序的整个生命周期,全局对象在创建时,将Math,String,Date,document等常用的JS对象作为其属性,由于这个全局对象不能通过名字直接访问,因此还添加了另外一个属性window,并将window指向了自身,这样就可以通过window访问这个全局对象了,如图所示:

(2)构建一个执行环境栈( Execution Context Stack)  , 创建一个全局执行环境(Execument Context)EC ,并将EC压入栈中。执行环境栈的作用是为了保证程序能够按照正确的顺序被执行,每个函数都有自己的执行环境,当执行进入一个函数时,函数的执行环境就会被推入一个执行环境栈的顶部并获取执行权。当这个函数执行完毕,它的执行环境又从这个栈的顶部被删除,并把执行权并还给之前执行环境。如图所示:

(注:本段引用 http://www.cnblogs.com/pigtail/archive/2012/07/19/2570988.html )

本例中,在执行环境栈中只有全局执行环境。

(3)创建一个全局变量对象(Varibale Object) VO,  并将VO指向全局对象,VO中不仅包含了全局对象的属性,还包括在全局定义的变量 a 和函数 f1,于此同时,在定义f1的时候,为 f1 添加了一个内部属性[[scope]],并指向了VO。

[[scope]] 是函数的作用域,每个函数在定义的时候,都会创建一个与之相关的作用域,函数的作用域总是指向定义函数时所在的环境,即f1的作用域指向了VO。 如图所示:

 2 执行f1

当执行进入f1(1) 时,JS引擎需要完成以下4 步操作:

(1)创建 f1 的执行环境EC,然后EC推入执行环境栈的顶部并获取执行权。 此时执行环境栈中有两个执行环境,分别是全局执行环境和f1执行环境,f1执行环境在栈顶,全局执行环境在栈的底部。

(2)创建一个作用域链(Scope Chain) ,每个执行环境都有自己的作用域链,用于标识符解析,当执行环境被创建时,它的作用域链初始化为当前运行函数的[[scope]]所包含的对象,即全局对象,如图所示:

(3)创建一个函数的活动对象(Activation Object) AO, 函数中的活动对象就是前文中提到的变量对象,只是命名不同而已(你可以认为,变量对象是一个抽象概念,活动对象是它的一个具体实现), 该对象包含了函数的形参、arguments、this、以及局部变量和内部函数的定义,然后此对象会被推入作用域链的前端。如图所示:

(4)在定义f2的时候,同样也为f2添加了一个[[scope]],并指向了定义f2时所在的环境AO<f1>, 而AO<f1> 位于链表的前端,由于链表具有首尾相连的特点,因此f2的作用域指向了f1的整个作用域链,如图所示:

 3 执行fn

f1被执行以后,返回了f2的引用,并赋值给了fn,执行 fn(1) 就相当于执行f2(1), JS引擎需要完成以下4步操作:

(1)创建 f2 的执行环境EC,然后EC推入执行环境栈的顶部并获取执行权。 此时执行环境栈中有两个执行环境,分别是全局执行环境和f2执行环境,f2 执行环境在栈顶,全局执行环境在栈的底部。

(2)创建f2的作用域链,初始化为 f2 的[[scope]]所包含的对象,即f1的作用域链。

(3)创建f2的活动对象,该对象包含了f2的形参c, arguments 和 this,如图所示:

(4) 执行 a+b+c, f2 在解析a、b、c三个标识符时,遵守变量查找规则:先查找自身是否存在该变量,如果存在,则停止查找;如果不存在,会沿着作用域链依次查找,直到找到为止,如果整个原型链上都未找到该变量,则返回undefined。

话说f2的作用域链到底是什么样呢? 根据过程(3)的示意图,可以得出 f2 的作用域链示意图:

如图所示:变量 c 将在AO<f2> 中被找到,值1; 变量b和a 都会在AO<f1>中被找到,其值分别为1和2,此时停止查找,返回并进行计算,因此输出结果为:1+1+2=4

简单的总结语

以上便是JS引擎定义和执行一个闭包的全过程,从 f2 形成的作用域链可以看出,f2的执行依赖于 f1 的AO和全局VO,而f1的AO中保存着f1中的所有变量,从而 f2 间接的引用了f1中的变量。因此,为了保证f2的正确执行,f1 中的变量们 “不得不” 停留在内存中,为 f2 提供“服务”,这样就形成了闭包。

finish!

 

时间: 2024-10-28 23:33:23

精华分享:深爱—— javascript 闭包的相关文章

javascript闭包详解(内容为转载的,觉得不错就分享一下)

一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function f1(){ var n=999; } alert(n); // error 这里有一个地方需要注意,函数

javascript闭包如何实现函数的重载?

本文和大家分享的主要是妙用javascript闭包实现函数重载相关内容,一起来看看吧,希望对大家学习javascript有所帮助. 1.准备知识 1.1 闭包 闭包是一个函数在创建时,允许该自身函数访问并操作该自身函数以外的变量时所创建的作用域.闭包可以让函数访问所有存在于该函数声明时的作用域内的变量和函数. <script>         var outerValue = "ninja";         var later;         function oute

那些年,我们误解的 JavaScript 闭包

说到闭包,大部分的初始者,都是谈虎色变的.最近对闭包,有了自己的理解,就感觉.其实我们误解闭包.也被网上各种说的闭包的解释给搞迷糊. 一句话:要想理解一个东西还是看权威的东西. 下面我来通俗的讲解一个闭包的知识.(建议大家去读JavaScript权威指南) 我们先弄明白几个问题: 1.作为命名空间的函数: 在函数中声明的变量在整个函数体内都是可见(包括嵌套的函数)在函数的外部不是可见的.不在任何函数内声明的是全局变量 ===>也就是说:一个函数它就是一个作用域,不管你里面有什么东西.他们都是一家

javascript闭包基础

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.很早就接触过闭包这个概念了,但是一直糊里糊涂的,没有能够弄明白JavaScript的闭包到底是什么,有什么用,今天在网上看到了一篇讲JavaScript闭包的文章(原文链接),讲得非常好,这下算是彻底明白了JavaScript的闭包到底是个神马东东以及闭包的用途了,在此写出来和大家分享一下,希望不理解JavaScript闭包的朋友们看了之后能够理解闭包!以下内容大部分是来自原文,我在原文的基础

javascript——闭包

1 <script type="text/javascript"> 2 //什么是闭包: 3 //是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落. 4 //这些外部执行域的非持久型变量神奇地保留它们在闭包最初定义(或创建)时的值(深连结). 5 //简单来说,闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对), 6 //而这些键值对是不会随上一级函数的执行完成而销毁. 7 //周爱民说

JavaScript学习总结(十六)——Javascript闭包(Closure)

原文地址: http://www.cnblogs.com/xdp-gacl/p/3703876.html 闭包(closure)是Javascript语言的一个难点,也是它的特色, 很多高级应用都要依靠闭包实现.很早就接触过闭包这个概念了,但是一直糊里糊涂的,没有能够弄明白JavaScript的闭包到底是什么,有什么用,今天 在网上看到了一篇讲JavaScript闭包的文章(原文链接), 讲得非常好,这下算是彻底明白了JavaScript的闭包到底是个神马东东以及闭包的用途了,在此写出来和大家分

我从来不理解JavaScript闭包,直到有人这样向我解释它...

正如标题所述,JavaScript闭包对我来说一直有点神秘,看过很多闭包的文章,在工作使用过闭包,有时甚至在项目中使用闭包,但我确实是这是在使用闭包的知识. 最近看国外的一些文章,终于,有人用于一种让我明白方式对闭包进行了解释,我将在本文中尝试使用这种方法来解释闭包. 准备 在理解闭包之前,有个重要的概念需要先了解一下,就是 js 执行上下文. 这篇文章是执行上下文 很不错的入门教程,文章中提到: 当代码在JavaScript中运行时,执行代码的环境非常重要,并将概括为以下几点: 全局代码--第

深入理解javascript闭包

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function

JavaScript 闭包

1.词法作用域: 简单地说子集能访问父级的变量, 说人话就是变量拿来就用不用传入 2.函数局部变量: 在函数体中以var 声明变量的为局部变量 + 函数传入的参数, 直接写变量名声明的变量是全局变量 3.局部变量生存期: 局部变量在函数函数的执行期间可用,  一旦执行过后,局部变量将不再可用 4.延长局部变量生存期: 现在问题来了,我想要延长局部变量的生存期,怎么办.(因为调用函数不仅仅是为了return, 有时候还需要保存函数中的状态, 或者实现类等等) 5.使用全局变量不好吗: 不好.有时函