初步学习JS中的闭包

JS高级程序设计(3rd)中对闭包的定义就是一句话,首先闭包是一个函数,怎样的函数呢?有权访问另一个函数作用域中的变量 的函数。而创建闭包的常见方式就是在一个函数的内部创建另一个函数,就是嵌套函数。

闭包会涉及到的点主要有

①     作用域链(这个原理让我们明白内部嵌套的函数是能够访问外部父函数里定义的变量的,而对于嵌套函数来说引用了非自己作用域内定义的这些变量通常又被称为自由变量)

②     函数执行的机制(这个又涉及到执行环境execution context ,环境栈(注意环境栈的底部永远是全局上下文global context),活动对象,变量对象等)。

先说执行环境相关的:

当函数执行时,函数的环境会被推入到环境栈的顶部,理论上函数执行完毕后会将其环境弹出pop,将控制权返回给之前的执行环境,但是由于有了闭包,情况又会有所不同。

再来说两个相关概念:活动对象(activation object)和变量对象(variable object)

变量对象:每个执行环境都会有自己的变量对象,里面存储着在该执行环境中定义的变量和函数;

如果这个执行环境是函数Function Context,那么则将其活动对象作为变量对象,活动对象中还包含了arguments(形参类数组)、formal parameters(形参的值),活动对象还包含了Argument 对象,该对象具有callee,length等属性

理解了以上两点,现在可以来说一下函数执行的整体流程了:

1、执行代码,全局执行环境

 创建global . variable object

2、全局变量的赋值 or 调用函数

 调用函数时,会得到当前函数的活动对象activation object,该活动对象中包含了该函数内部变量的声明,函数的声明,形参

3、进入所调用的函数的上下文

进行该函数所在作用域上的变量的赋值及各种运算(此时的作用域包括全局的variable object和当前函数执行环境的activation object)

4、分为三种情况

a、函数正常return或结束,该函数执行环境被弹出,回到step2继续执行其他代码;

b、若在函数中有内部函数的调用,执行step 3;

c、若函数返回了另一个函数,且该函数有对自由变量的引用,则形成闭包。此时作用域链机制仍然有效,当前的执行环境Function Context不会被弹出环境栈,函数的活动对象也留在了内存中,不会在调用结束后被垃圾回收机制回收。回到step2继续执行其他代码;

5、所有代码执行完毕,程序关闭,释放内存

③     垃圾回收机制

一般来说,一个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以便后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了,对应的内存空间也就被回收了。下次再执行此函数的时候,所有变量又回到了最初状态,重新赋值使用。

但是如果一个函数parent内部又嵌套了另一个函数child,而这个child函数又是可能在外部被调用到的,并且这个内部嵌套函数child又使用了外部函数parent中的某些变量,这个时候就形成了闭包,此时的内存回收机制就会与前面一般情况有所不同。

在外部函数parent执行返回后 又直接调用了内部嵌套函数,如果按一般回收情况这时候parent已经执行完毕被回收了,那么内部嵌套函数就没法读取已经被回收的变量,所以在遇到闭包时,JS解析器实际上会将内部嵌套函数本身和父级和祖先级的变量(自由变量)一起保存起来,保存在该闭包中。并且这些变量不会被内存回收器回收。只有当这些内部函数不可能被调用之后(比如被删除了或者没有了指针)才会销毁这个闭包,同时那些不再被该闭包引用的变量才会在下一次内存回收启动时被回收。

由于闭包会使得函数中的变量一直都被保存在内存中,使得内存消耗很大,影响网页性能。所以我们有必要在函数执行完毕后对其进行手动销毁。


下面我们来用例子说明闭包的一些特性,闭包常见的有两种形式--函数作为返回值,函数作为参数传递

看个例子:



 1 function test() {
 2     var num = 1;
 3     return function() {
 4         num++;
 5         console.log(num)
 6     }
 7 }
 8 var anotherTest = test();
 9 anotherTest();//2
10 anotherTest();//3

以上可以看出闭包让其引用的变量一直存在在内存中(注意虽然活动对象没有被销毁,其包含的变量函数依然存在在内存中,但是却不能直接调用哦)

再来个例子:



 1 var result = [];
 2 function test() {
 3     var num = 0;
 4     for (; num < 3; num++) {
 5         result[num] = function() {
 6             console.log(num);
 7         }
 8     }
 9 }
10 test();
11 result[0](); // 3
12 result[1](); // 3
13 result[2](); // 3

上面这段代码中,本意是想让test中的变量 i 被内部匿名函数循环使用并依次输出索引0 1 2,但结果却与预想不同。为什么呢?因为闭包中记录的自由变量只是对自由变量的一个引用,也就说只能取得该变量最后状态保留的那个值。本例中执行完for循环后 i 变量最后的值是3,所以所有引用 i 值的结果都将是3。

关于自由变量的取值来看两个例子:

 1 var test = 10;
 2 function fun() {
 3     var test = 100;
 4     return function foo(num) {
 5         if (num < test) {
 6             console.log(num + ‘<‘ + test);
 7         }
 8     }
 9 }
10 var f1 = fun();
11 f1(15);
12 //15<100

可以看到这个例子中test的取值是100,这个100来自于创建foo函数的作用域fun中,fun也是foo函数的父级函数;那么自由变量到底是来自创建它的作用域还是其父级作用域呢?在看一个例子:

 1 var test = 10;
 2 parameter = function (num) {
 3     if (num > test) {
 4         console.log(num+‘>‘+test);
 5     }
 6 };
 7 (function (fun) {
 8     var test = 100;
 9     fun(15);
10 })(parameter);
11 //15>10

上面这个例子中,test的取值为10,而这个10是来自全局作用域的。这证明了自由变量实际上是在创建这个函数的作用域中取值而不是其父级作用域中取值。

时间: 2024-11-06 22:54:42

初步学习JS中的闭包的相关文章

浅谈JS中的闭包

今天 大年初一,祝各位小伙伴们狗年旺旺啊,闲来也没事,只能钻研一下自己的分内之事,也就是作为一个前端码农的身份,得时刻保持学习的态度,温故而知新,每天都给自己一个小目标去完成,日积月累,所想达到的状态,都会有所见长. JS中的闭包,想必,做web开发的程序猿们都有一定的认识吧,不仅仅是js里有这种特性,而且弱语言类型的譬如python里也是有闭包这么一个强大的特性的,对于老司机们来说,闭包可真的是一个很好的东西,但是对于新入门的菜鸟们来说,闭包却是一个难以搞得清楚的特性,很多情况下,会用,但是却

js中的闭包之我理解

闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的理解以及js内部解释器的运作方式的描述,都是可以看出你js实际水平的.即使你没答对,也能让考官对你的水平有个评估.那么我先来说说我对js中的闭包的理解. 闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等. 在理解闭包以前.最好能

(转)js中的闭包问题

闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的理解以及js内部解释器的运作方式的描述,都是可以看出你js实际水平的.即使你没答对,也能让考官对你的水平有个评估.那么我先来说说我对js中的闭包的理解. 闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等. 在理解闭包以前.最好能

js中的闭包理解

闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的理解以及js内部解释器的运作方式的描述,都是可以看出你js实际水平的.即使你没答对,也能让考官对你的水平有个评估.那么我先来说说我对js中的闭包的理解. 闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等. 在理解闭包以前.最好能

js中的闭包理解一

闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的理解以及js内部解释器的运作方式的描述,都是可以看出你js实际水平的.即使你没答对,也能让考官对你的水平有个评估.那么我先来说说我对js中的闭包的理解. 闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等. 在理解闭包以前.最好能

转载 - 读博客 - 笔记 - JS中的闭包(closure)

原博客标题:JS中的闭包(closure)原博客地址:http://www.cnblogs.com/jingwhale/p/4574792.html 笔记如下: 1. 函数内部声明变量的时候,一定要使用var命令.如果不用的话,实际上是声明了一个全局变量. function outer(){ localVal = 30; return localVal; } outer(); alert(localVal);//30 2. Javascript语言特有的“链式作用域”结构(chain scope

js中的闭包和c#中的闭包

我们 关于闭包,一个老僧长谈的话题:js的闭包俺将的比较多了,而且很详细,俺就不说了,可以看看之前的文章: 我们来对比一下c#中的闭包和js中的闭包: 先看我们的c#代码: static List<Action> fn0() { int result = 0; List<Action> list = new List<Action>(); for (int i = 0; i < 10; i++) { result = i + 1; //这样result相当于一个全

通俗易懂地解释JS中的闭包

1. "闭包就是跨作用域访问变量." [示例一] ? 1 2 3 4 5 6 7 8 9 var name = 'wangxi' function user () {  // var name = 'wangxi'  function getName () {  console.log(name)  }  getName() } user() // wangxi 在 getName 函数中获取 name,首先在 getName 函数的作用域中查找 name,未找到,进而在 user 函

详解js中的闭包

前言 在js中,闭包是一个很重要又相当不容易完全理解的要点,网上关于讲解闭包的文章非常多,但是并不是非常容易读懂,在这里以<javascript高级程序设计>里面的理论为基础.用拆分的方式,深入讲解一下对于闭包的理解,如果有不对请指正. 写在闭包之前 闭包的内部细节,依赖于函数被调用过程所发生的一系列事件为基础,所以有必要先弄清楚以下几个概念: 1. 执行环境和活动对象 ** - 执行环境(execution context)定义了变量或者函数有权访问的其他数据,每个执行环境都有一个与之关联的