`
liukemin
  • 浏览: 8060 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

理解 Javascript 的闭包

 
阅读更多

前言:还是一篇入门文章。 Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包 对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点 ECMAScript语言规范来使读者可以更深入的理解闭包。

注:本文是入门文章,例子素材整理于网络,如果你是高手,欢迎针对文章提出技术性建议和意见。本文讨论的是Javascript,不想做语言对比,如果您对Javascript天生不适,请自行绕道。

什么是闭包

闭包是什么?闭包是Closure,这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西,简而言之,闭包就是:

  • 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。
  • 闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配
  • 当在一个函数内定义另外一个函数就会产生闭包

上面的第二定义是第一个补充说明,抽取第一个定义的主谓宾——闭包是函数的‘局部变量’集合 。只是这个局部变量是可以在函数返回后被访问。(这个不是官方定义,但是这个定义应该更有利于你理解闭包)

做为局部变量都可以被函数内的代码访问,这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味 着函数必须返回一个指向闭包的“引用”,或将这个”引用”赋值给某个外部变量,才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个 对象,因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是,ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变 量。但是在ECMAScript中,函数对象中定义的内部函数() inner function 是可以直接访问外部函数的局部变量,通过这种机制,我们就可以以如下的方式完成对闭包的访问了。

 	function greeting(name) {
 	      var text = 'Hello ' + name; // local variable
 	      // 每次调用时,产生闭包,并返回内部函数对象给调用者
 	      return function () { alert(text); }
 	}
	var sayHello=greeting( "Closure" );
	sayHello()  // 通过闭包访问到了局部变量text

 

上述代码的执行结果是:Hello Closure,因为sayHello()函数在greeting函数执行完毕后,仍然可以访问到了定义在其之内的局部变量text。

好了,这个就是传说中闭包的效果,闭包在Javascript中有多种应用场景和模式,比如Singleton,Power Constructor等这些Javascript模式都离不开对闭包的使用。

ECMAScript闭包模型

ECMAScript到底是如何实现闭包的呢?想深入了解的亲们可以获取ECMAScript 规范 进行研究,我这里也只做一个简单的讲解,内容也是来自于网络。

在ECMAscript的脚本的函数运行时,每个函数关联都有一个执行上下文场景(Execution Context) ,这个执行上下文场景中包含三个部分

  • 文法环境(The LexicalEnvironment)
  • 变量环境(The VariableEnvironment)
  • this绑定

其中第三点this绑定与闭包无关,不在本文中讨论。文法环境中用于解析函数执行过程使用到的变量标识符。我们可以将文法环境想象成一个对象,该对 象包含了两个重要组件,环境记录(Enviroment Recode),和外部引用(指针)。环境记录包含包含了函数内部声明的局部变量和参数变量,外部引用指向了外部函数对象的上下文执行场景。全局的上下文 场景中此引用值为NULL。这样的数据结构就构成了一个单向的链表,每个引用都指向外层的上下文场景。

例如上面我们例子的闭包模型应该是这样,sayHello函数在最下层,上层是函数greeting,最外层是全局场景。如下图:
 
因此当sayHello被调用的时候,sayHello会通过上下文场景找到局部变量text的值,因此在屏幕的对话框中显示出”Hello Closure”
变量环境(The VariableEnvironment)和文法环境的作用基本相似,具体的区别请参看ECMAScript的规范文档。

闭包的样列

前面的我大致了解了Javascript闭包是什么,闭包在Javascript是怎么实现的。下面我们通过针对一些例子来帮助大家更加深入的理解闭包,下面共有5个样例,例子来自于JavaScript Closures For Dummies( 镜像 )
例子1:闭包中局部变量是引用而非拷贝

 	function say667() {
 	     // Local variable that ends up within closure
 	     var num = 666;
 	     var sayAlert = function () { alert(num); }
 	     num++;
 	     return sayAlert;
 	}
 	   
 	var sayAlert = say667();
 	sayAlert()

 

因此执行结果应该弹出的667而非666。

例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。

 	function setupSomeGlobals() {
 	     // Local variable that ends up within closure
 	     var num = 666;
 	     // Store some references to functions as global variables
 	     gAlertNumber = function () { alert(num); }
 	     gIncreaseNumber = function () { num++; }
 	     gSetNumber = function (x) { num = x; }
 	}
 	setupSomeGolbals(); // 为三个全局变量赋值
 	gAlertNumber(); //666
 	gIncreaseNumber();
 	gAlertNumber(); // 667
 	gSetNumber(12); //
 	gAlertNumber(); //12

 

例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包

 	function buildList(list) {
 	     var result = [];
 	     for ( var i = 0; i < list.length; i++) {
 	         var item = 'item' + list[i];
 	         result.push( function () {alert(item + ' ' + list[i])} );
 	     }
 	     return result;
 	}
 	   
 	function testList() {
 	     var fnlist = buildList([1,2,3]);
 	     // using j only to help prevent confusion - could use i
 	     for ( var j = 0; j < fnlist.length; j++) {
 	         fnlist[j]();
 	     }
 	}

 

testList的执行结果是弹出item3 undefined窗口三次,因为这三个函数绑定了同一个闭包,而且item的值为最后计算的结果,但是当i跳出循环时i值为4,所以list[4]的结果为undefined.

例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。

 

 
 	function sayAlice() {
 	     var sayAlert = function () { alert(alice); }
 	     // Local variable that ends up within closure
 	     var alice = 'Hello Alice' ;
 	     return sayAlert;
 	}
 	var helloAlice=sayAlice();
 	helloAlice();
 

执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后,局部变量仍然可以被访问到。

例子5:每次函数调用的时候创建一个新的闭包

 	function newClosure(someNum, someRef) {
 	     // Local variables that end up within closure
 	     var num = someNum;
 	     var anArray = [1,2,3];
 	     var ref = someRef;
 	     return function (x) {
 	         num += x;
 	         anArray.push(num);
 	         alert( 'num: ' + num +
 	         '\nanArray ' + anArray.toString() +
 	         '\nref.someVar ' + ref.someVar);
 	     }
 	}
 	closure1=newClosure(40,{someVar: 'closure 1' });
 	closure2=newClosure(1000,{someVar: 'closure 2' });
 	   
 	closure1(5); // num:45 anArray[1,2,3,45] ref:'someVar closure1'
 	closure2(-10); // num:990 anArray[1,2,3,990] ref:'someVar closure2'

 

闭包的应用

 

Singleton 单件:

 	var singleton = function () {
 	     var privateVariable;
 	     function privateFunction(x) {
 	         ...privateVariable...
 	     }
	   
	     return {
 	         firstMethod: function (a, b) {
	             ...privateVariable...
	         },
 	         secondMethod: function (c) {
 	             ...privateFunction()...
 	         }
 	     };
 	}();

 

这个单件通过闭包来实现。通过闭包完成了私有的成员和方法的封装。匿名主函数返回一个对象。对象包含了两个方法,方法1可以方法私有变量,方法2访 问内部私有函数。需要注意的地方是匿名主函数结束的地方的’()’,如果没有这个’()’就不能产生单件。因为匿名函数只能返回了唯一的对象,而且不能被 其他地方调用。这个就是利用闭包产生单件的方法。

 

转载出处:http://www.oschina.net/question/28_41112?from=20120311

分享到:
评论

相关推荐

    理解 JavaScript 闭包

    要成为高级 JavaScript 程序员,就必须理解闭包。 本文结合 ECMA 262 规范详解了闭包的内部工作机制,让 JavaScript 编程人员对闭包的理解从“嵌套的函数”深入到“标识符解析、执行环境和作用域链”等等 ...

    深入理解_JavaScript_闭包

    总之,深入理解JavaScript闭包对于编写高效、可维护的代码至关重要。掌握闭包不仅有助于解决实际问题,还能提高编程技巧和对JavaScript语言机制的理解。在日常开发中,无论是用于实现模块化、数据封装,还是处理异步...

    深入浅出理解JavaScript闭包的功能与用法

    主要介绍了深入浅出理解JavaScript闭包的功能与用法,结合实例形式从变量、函数的内部属性与作用域链分析了javascript闭包的相关概念、功能与使用技巧,需要的朋友可以参考下

    理解javascript闭包

    主要帮助大家理解javascript闭包概念优缺点及应用,帮助大家全面的学习javascript闭包,需要的朋友可以参考下

    JavaScript闭包

    Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包 对于那些使用传统静态...本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点 ECMAScript语言规范来使读者可以更深入的理解闭包。

    深入理解javascript原型和闭包

    深入理解javascript原型和闭包(01)——一切都是对象 深入理解javascript原型和闭包(02)——函数和对象的关系

    javascript闭包详解中文word版

    本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点 ECMAscript语言规范来使读者可以更深入的理解闭包。闭包是Closure, 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。

    轻松理解JavaScript闭包

    JavaScript闭包是编程语言中一个核心的概念,尤其在JavaScript中,它是理解和编写高效代码的关键。闭包的本质是函数能够访问并操作在其外部定义的变量,即使该函数在外部作用域执行完毕后仍然能够保持对这些变量的...

    深入理解Javascript闭包 新手版

    最近在网上查阅了不少Javascript闭包(closure)相关的资料,写的大多是非常的学术和专业。对于初学者来说别说理解闭包了,就连文字叙述都很难看懂。撰写此文的目的就是用最通俗的文字揭开Javascript闭包的真实面目。

    深入理解JavaScript 闭包究竟是什么

    JavaScript 闭包是一种重要的编程概念,它涉及到函数和作用域之间的关系。闭包是指一个函数能够访问并操作其外部作用域中的变量,即使这个函数在外部作用域...理解和熟练掌握闭包对于JavaScript开发者来说至关重要。

    理解_JavaScript_闭包

    本文结合 ECMA 262 规范详解了闭包的内部工作机制,让 JavaScript 编程人员对闭包的理解从“嵌套的函数”深入到“标识符解析、执行环境和作用域链”等等 JavaScript 对象背后的运行机制当中,真正领会到闭包的实质。

    用最通俗易懂的代码帮助新手理解javascript闭包 推荐

    我同样也是个javascript新手,怎么说呢,先学的jquery,精通之后发现了javascript的重要性,再回过头来学javascript面向对象编程

    最简单的例子让你轻松理解JavaScript闭包

    在理解闭包之前,为了让大家更好的理解闭包,我举一个形象的例子。在JS里有着作用域的概念,那么作用域是什么呢?有什么用呢?下面这个例子能形象的体现 比如每一座城市,城市里乡镇啊有公园,超市等,这些建筑设施...

    JavaScript闭包函数

    闭包是ECMAScript (JavaScript)最强大的特性之一,但用好闭包的前提是必须理解闭包。闭包的创建相对容易,人们甚至会在不经意间创建闭包,但这些无意创建的闭包却存在潜在的危害,尤其是在比较常见的浏览器环境下...

    JavaScript知识点总结(十六)之Javascript闭包(Closure)代码详解

    有什么用,今天在网上看到了一篇讲JavaScript闭包的文章(原文链接),讲得非常好,这下算是彻底明白了JavaScript的闭包到底是个神马东东以及闭包的用途了,在此写出来和大家分享一下,希望不理解JavaScript闭包的朋友...

Global site tag (gtag.js) - Google Analytics