Javascript闭包的一些研究

原文:Javascript闭包的一些研究

 

 

本文不谈闭包的概念,因为概念容易把人搞晕,本文希望通过几个鲜活的例子来探究闭包的性质,相信对理解闭包会有所帮助。

 

程序1

var f = (function() {
     var n = 10;
     return function() {
          ++n;
          console.log(n);
     }
})();

f();

输出:

11

结论:

闭包函数可以访问外层函数中的变量。

 

程序2

var arr = [];
(function() {
     var n = 0;
     for (var i=0; i<3; ++i) {
          arr[i] = function() {
                 ++n;
                 console.log(n);
               }
     }
})();

for (var i=0; i<3; ++i) {
     var f = arr[i];
     f();
}

 

输出:

1
2
3

 

结论:

一个函数内有多个闭包函数,那么这些闭包函数共享外层函数中的变量。可以看出,例子中3个闭包函数中的n是同一个变量,而不是该变量的3个副本。 

 

程序3

var f0 = function() {
  ++n;
  console.log(n);
}

var f = (function() {
     var n = 10;
     return f0;
})();

f();

 

输出:

错误指向“++n”这一行。

 

结论:

闭包函数的作用域不是在引用或运行它的时候确定的,看起来好像是在定义它的时候确定的。

 

说明:
虽然该程序与“程序1”看起来一样,但由于函数f0一个在内部定义一个在外部定义,尽管它们都是从函数内部返回,但在这个例子中f0却无法访问变量n。

 

程序4

var f = (function() {
     var n = 10;
     return new Function('++n;console.log(n);');
})();

f();

 

输出同“程序3”:

 

结论:

该程序是对“程序3”的进一步印证和补充。由该程序可以得出的结论是:闭包函数的作用域是在编译它的时候确定的。

 

说明:
使用Function构造器可以创建一个function对象,函数体使用一个字符串指定,它可以像普通函数一样执行,并在首次执行时编译。因此,虽然在匿名函数内部定义了此function对象,但一直要到调用它的时候才会编译,即执行到“f()”时,而此时原函数的作用域已经不存在了。

 

再看一个例子:

程序5

var f = (function() {
     var n = 10;
     return eval('(function(){++n;console.log(n);})');
})();

f();

 

输出:

11

 

结论:无

 

说明:
这个例子是对上面两个程序的补充。这个例子之所以能够和“程序1”一样打印出11,是因为eval( )函数会立即对传递给它字符串进行解析(编译、执行),因此使用eval定义的函数和直接定义的效果是等价的。

(注意:eval( )中的“function(){...}”必须用括号扩起来,否则会报错)

 

程序6

var f = (function() {
     var n = 10;
     var s = 'hello';
     return function() {
          ++n;
          console.log(n);
     }
})();

f();

 

运行时在“console.log(n);”这一行加个断点,查看作用域中的值,其中只有n没有s:

结论:
外层函数中那些未在闭包函数中使用的变量,对闭包函数是不可见的。在这个例子中,闭包函数没有引用过变量s,因此其作用域中只有n。也就是说,对闭包函数来说,其可以访问的外层函数的变量实际上只是真正的外层函数变量的一个子集。

 

程序7

这个程序用来通过数据证明“程序6”的结论。程序稍微有点复杂,后面会先对其做简单说明。

var funArr = [];
var LENGTH = 500;
var ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

function getStr( ) {
     var s = '';
     for (var i=0; i<LENGTH; ++i) {
          s += ALPHABET[Math.floor(Math.random( ) * 62)];
     }
     return s;
}

function getArr( ) {
     var a = new Array(LENGTH);
     for (var i=0; i<LENGTH; ++i) {
          a[i] = Math.random( );
     }
}

var f = function( ) {
     var n = 10;
     var s = getStr( );
     var a = getArr( );

     funArr.push(function( ) {
          console.log(n, s, a);  // --- 1
          console.log(n);        //  --- 2
     })
}

for (var i=0; i<2000; ++i) {
     f( );
}

 

程序分析:

本程序的重点是for循环和函数f。for循环中调用了函数f 2000次,每次调用都会创建一个数字和两个长度为500的字符串和数组,所以2000次函数调用所创建的局部变量的规模还是比较可观的,程序用这种方法以便于后面做分析时对结果进行比较。

f中的局部变量会被一个闭包函数所引用,以此观察未被引用的局部变量是否会被回收。

分别运行该程序两次,第一次使用语句1(引用了f中的所有局部变量),第二次使用语句2(只引用了数字变量n)。对运行所得到的结果1和结果2分别采集堆快照(Heap Snapshot):

 

可以看到所占内存差别巨大,从这里就可以初步得出“未被闭包函数引用的局部变量会被回收”的结论。

不过为了严谨性,需要做更细致地分析。首先是结果1和结果2的统计图:

 

 

 

可以看到,第二次运行后内存中的对象数量明显要比第一次的少很多(二者产生的中间对象数量是相同的),这进一步说明了第二次运行后大部分对象都被回收了。

最后我们将结果2与结果1进行细致的比较,结果如下:

结论:
函数中的局部变量如果没有被任何闭包函数所引用(这里不考虑被全局变量所引用的情况),则这些局部变量在函数运行完成后就可以被回收,否则,这些变量会作为其闭包函数的作用域的一部分被保留,直到所有闭包函数也执行完毕为止。

该结论同时也印证了“程序6”的结论:闭包函数对外层函数作用域的引用是外层函数真实作用域的一个子集。

另外从这个实验还能推测出一点,即外层函数执行结束后是会被回收掉的。因为既然函数内部变量已被回收,那函数本身也没有存在的意义了。

 

时间: 2024-08-01 11:49:00

Javascript闭包的一些研究的相关文章

JavaScript 闭包环境很奇特 - 相当于类与实例的关系?!

JavaScript 闭包环境很奇特 - 相当于类与实例的关系?! 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 又一个疑问贴! 不过我相信,问题并不是难在如何解决,最终就是个能解决与不能解决

javascript闭包

前言 闭包对于初学者而言一直是一个不太好理解的概念.最近在学习javascript的时候碰巧看到了关于这方面的讲解,自己才明白了许多,所以把它写出来分享给大家.当然,本文也是参考了很多blog和书籍,加上自己的理解写出来的,文章末尾会附上对应的参考文档. 基础知识  变量作用域 //javascript的变量作用域可以分为两种:全局变量和局部变量. //在函数内声明的变量就是局部变量,这个变量在函数体内可访问,在函数外部无法直接读取局部变量. //例如: var globalVariable =

javascript闭包的高级使用方法实例

这篇文章介绍了javascript闭包的高级使用方法实例,有需要的朋友可以参考一下   扩展 Code: 复制代码 代码如下: var blogModule = (function (my) {  my.AddPhoto = function () { //添加内部代码  };  return my; }(blogModule)); Say: 将自身传进方法,然后实现了方法的扩展,有点象零件组装啊 Code: 复制代码 代码如下: var blogModule = (function (my)

基于javascript 闭包基础分享

如果对作用域,函数为独立的对象这样的基本概念理解较好的话,理解闭包的概念并在实际的编程实践中应用则颇有水到渠成之感. 在DOM的事件处理方面,大多数程序员甚至自己已经在使用闭包了而不自知,在这种情况下,对于浏览器中内嵌的JavaScript引擎的bug可能造成内存泄漏这一问题姑且不论,就是程序员自己调试也常常会一头雾水. 用 简单的语句来描述JavaScript中的闭包的概念:由于JavaScript中,函数是对象,对象是属性的集合,而属性的值又可以是对象,则在函数内 定义函数成为理所当然,如果

javascript闭包的理解

 1.首先我们要知道变量作用域链 变量的作用域分两种:全局变量和局部变量.没有定义到任何函数中的变量为全局变量,在函数中定义的变量为局部变量,注意在函数内部定义变量时一定要使用var关键字,不带var关键字的变量为全局变量. javascript中每一段代码都有与之关联的作用域链,这个作用域链是一个对象列表或者链表,定义了这段代码"作用域"中的变量.顶层代码的作用域由全局变量组成:不包含嵌套的函数的作用域链有两个对象:一个是定义的函数参数和局部变量的对象,一个是全局变量对象:而嵌套函数

javascript 闭包详解

 这篇文章主要详细介绍了javascript 闭包的相关资料,十分详尽,需要的朋友可以参考下     javascript 闭包是一个很有趣的东东.看了些相关资料http://www.jb51.net/article/29472.htm,对其印象最深刻的是:实现了public 和private. 创建一个非匿名闭包最简单的语法是:   代码如下: var obj = (function(){//各种代码 });   闭包最经典的例子:   代码如下: var makeCounter = (fun

关于javascript闭包的一点疑问

问题描述 关于javascript闭包的一点疑问 闭包存储局部变量的机理是什么?它如何存储的?比如如下一个闭包 function test(){ var num = 10; return function(){ num++; return num; }; }; var n = test(); alert(n()); //返回11 alert(n()); //返回12,实现了累加 alert(n()); //继续累加 第二次alert(n())通过闭包实现了累加,但是这个局部变量num是如何存储在

函数-关于javascript闭包的一点疑问

问题描述 关于javascript闭包的一点疑问 function create(){ var arr = new Array(); for (var i=0; i<10; i++){ arr[i] = function(num){ return function(){ return num; }; }(i);// (i)有是什么意思?} 解决方案 匿名函数的参数,可以这么理解 var f = function(num){ return function(){ return num; };};a

Javascript闭包简单理解

原文:Javascript闭包简单理解 提到闭包,想必大家都早有耳闻,下面说下我的简单理解.说实话平时工作中实际手动写闭包的场景并不多,但是项目中用到的第三方框架和组件或多或少用到了闭包.所以,了解闭包是非常必要的.呵呵... 一.什么是闭包简而言之,就是能够读取其他函数内部变量的函数.由于JS变量作用域的特性,外部不能访问内部变量,内部可以外部变量. 二.使用场景1. 实现私有成员.2. 保护命名空间,避免污染全局变量.3. 缓存变量. 先看一个封装的例子: var person = func