`
muyu
  • 浏览: 222694 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JavaScript closures 闭包概要

阅读更多

原文地址:http://www.javascriptkit.com/javatutors/closures.shtml

以下文字,与原文不同,有改编。

 

先看常用的函数:

function sayHello(name) {
  var text = 'Hello ' + name;
  var sayAlert = function() { alert(text); }
  sayAlert();
}
sayHello('Bob');

 

闭包的例子

 

一句话描述:

  • 闭包是函数的局部变量,在函数return之后,仍然有值, 或者
  • 闭包是stack-frame,在函数return的时候,它不会被释放。(就好像'stack-frame'是内存分配、而非处于堆栈!)

下面的代码返回一个function的引用:

 

function sayHello2(name) {
  var text = 'Hello ' + name; // local variable
  var sayAlert = function() { alert(text); }
  return sayAlert;
}
var say2 = sayHello2('Jane');
say2(); //hello Jane

 

 

多数JavaScript程序员能够理解上面代码的函数引用如何返回给变量。请在学习闭包之前理解它。C程序员把函数看做返回的一个函数指针,变量sayAlert和say2分别是函数指针。


C函数指针和JavaScript的函数引用有着本质的不同。在JavaScript,函数引用变量既是一个函数指针,又是一个隐藏的闭包指针。

 

上面代码拥有闭包,因为函数体内又声明了一个匿名函数 function() { alert(text); } ,参看例子中的sayHello2()。如果你在另一个函数内使用function关键字,闭包就产生了。

 

在C和其他多数语言,当函数return之后,stack-frame就被销毁了,所有的局部变量也就不能访问了。

 

在JavaScript中,如果你在函数里声明了函数,在你调用的函数renturn之后,局部变量仍然可以访问。请注意上面的例子,我们调用了变量text,它是函数sayHello2的局部变量。

 

Example 3

 

这个例子表明局部变量不是拷贝传递,而是引用传递。在外层函数退出时,它把stack-frame保存在内存。

 

function say667() {
  // Local variable that ends up within closure
  var num = 666;
  var sayAlert = function() { alert(num); }
  num++;
  return sayAlert;
}
var sayNumba = say667();
sayNumba(); //667,而不是666
alert(sayNumba.toString());

 

Example 4

三个函数对某个闭包使用同一个引用,因为它们均在setupSomeGlobals()里声明的。

var gAlertNumber = gIncreaseNumber = gSetNumber = null;
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; }
}
setupSomeGlobals();
//任意、多次 运行下面的函数
gAlertNumber();
gIncreaseNumber();
gSetNumber(5); //把num重新设为 5
gSetNumber(-8888); //把num重新设为 -8888

 

重新运行setupSomeGlobals(); 就会重新产生一个新的闭包。在JavaScript中,当你在函数里又声明一个函数,外部函数每调用一次,内部函数将再被重新产生一次。

 

Example 5

 

当心下面例子的循环:闭包中的局部变量可能和你最初想的不一样。

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();  //输出3次:'item3 undefined'

 

Example 6

 

下面的例子表明,闭包包含了 在外部函数退出之前、定义的任何局部变量。注意,变量alice实际上在匿名函数之后声明的。匿名函数先被声明:当函数被调用时,它可以访问alice,因为alice在闭包里。

function sayAlice() {
  var sayAlert = function() { alert(alice); }
  // Local variable that ends up within closure
  var alice = 'Hello Alice';
  return sayAlert;
}
sayAlice()(); //Hello Alice
alert(alice); //错误:alice不是全局变量,它在函数体内var了

 

Example 7


下面的例子表明,每次调用会产生各自的闭包。

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'}); 
closure1(5);

closure2 = newClosure(1000, {someVar : 'closure 2'});
closure2(-10);

 

小结


读一些说明要比理解上面的例子难得多。我对于闭包的说明以及stack-frame等等在技术上可能不正确 --- 但是它们的确有助于理解。

 

观点

  • Whenever you use function inside another function, a closure is used.
  • Whenever you use eval() inside a function, a closure is used. The text you eval can reference local variables of the function, and within eval you can even create new local variables by using eval('var foo =
  • When you use Function() inside a function, it does not create a closure. (The new function cannot reference the local variables of the function calling Function()).
  • A closure in JavaScript  is like keeping a copy of the all the local variables, just as they were when a function exited.
  • It is probably best to think that a closure is always created just on entry to a function, and the local variables are added to that closure.
  • A new set of local variables is kept every time a function with a closure is called (Given that the function contains a function declaration inside it, and a reference to that inside function is either returned or an external reference is kept for it in some way).
  • Two functions might look like they have the same source text, but have completely different behaviour because of their 'hidden' closure. I don't think JavaScript code can actually find out if a function reference has a closure or not.
  • If you are trying to do any dynamic source code modifications ( for example: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); ), it won't work if myFunction is a closure (Of course, you would never even think of doing source code string substitution at runtime, but...).
  • It is possible to get function declarations within function declarations within functions - and you can get closures at more than one level.
  • I think normally a closure is the term for both the function along with the variables that are captured. Note that I do not use that definition in this article!
  • I suspect that closures in JavaScript differ from those normally found in functional languages.
  •  

    分享到:
    评论
    2 楼 muyu 2010-03-22  
    下面代码的闭包和Example 5类似:
        var bankList = $('ul.bankList li');
        var len = bankList.length;
        for(i=0; i<len; i++) {
        $(bankList[i]).click(function(j) {
            return function() {$(bankList[j]).find('input').attr("checked",true);};
            }(i));
        }

    要实现的效果,就是点击<li>的时候,字段选中radio:
    <ul class="bankList">
            <li>
            <input type="radio" class="radio" id="bankCMB" name="bank" value="CMB">
            <a href="javascript:void(0);"><img src="zs-yh.png" alt="招商银行"></a>
            </li>
            <li>

            <input type="radio" class="radio" id="bankICBC" name="bank" value="ICBC">
            <a href="javascript:void(0);"><img src="gs-yh.png" alt="中国工商银行"></a>
            </li>
            <li>
    1 楼 zzhonghe 2009-11-07  
    很不错,有两点是以前没注意的:

    1. 闭包中的环境变量如果是在循环中,那么使用闭包变量的时候是循环结束后的值
    2. 闭包环境变量可以定义在方法后,不影响使用

    相关推荐

      secrets_of_javascript_closures.pdf

      secrets_of_javascript_closures.pdf

      javascript 闭包

      JavaScript 闭包是一种重要的编程概念,它涉及到函数和作用域的深入理解。闭包的本质是函数内部能够访问并保持对外部变量的引用,即使在函数执行完毕后,这些变量仍然可被内部函数访问和操作。这使得闭包成为实现...

      Advanced JavaScript (closures,prototype,inheritance)

      JavaScript,作为一种广泛应用于Web开发的脚本语言,其高级特性如闭包(closures)、原型(prototype)和继承(inheritance)是理解其精髓的关键。本文将深入探讨这些概念,帮助开发者更好地掌握JavaScript的核心。 ...

      scope-chains-closures, Javascript作用域链和闭包 workshop.zip

      scope-chains-closures, Javascript作用域链和闭包 workshop 范围链和闭包 workshop正在启动$ npm install -g scope-chains-closures$ scope-chains-closures # or, shorter: sccjs使用箭头

      深入理解JavaScript系列(16) 闭包(Closures)

      介绍 本章我们将介绍在JavaScript里大家经常来讨论的话题 —— 闭包(closure)。闭包其实大家都已经谈烂了。尽管如此,这里还是要试着从理论角度来讨论下闭包,看看ECMAScript中的闭包内部究竟是如何工作的。 正如...

      javascript-4-下午:一个下午的项目,可帮助巩固JavaScript的闭包和构造函数

      在这个项目中,我们将提供实践JavaScript问题,以帮助您更好地了解closures和constructors 。 设置 Fork此存储库。 Clone你的叉子。 用浏览器打开./index.html 。 方向 完成closures.js和constructors.js内部的...

      深入理解JavaScript系列

      深入理解JavaScript系列(16):闭包(Closures) 深入理解JavaScript系列(17):面向对象编程之一般理论 深入理解JavaScript系列(18):面向对象编程之ECMAScript实现 深入理解JavaScript系列(19):求值策略...

      深入理解JavaScript系列(.chm)

      深入理解JavaScript系列(16):闭包(Closures) 深入理解JavaScript系列(17):面向对象编程之一般理论 深入理解JavaScript系列(18):面向对象编程之ECMAScript实现 深入理解JavaScript系列(19):求值策略...

      JavaScript函数式编程.pdf

      3. 闭包(Closures) 在JavaScript函数式编程中,闭包是一个非常重要的概念。闭包是指有权访问另一个函数作用域中变量的函数。由于JavaScript的作用域链,闭包能够访问到函数定义时的外部变量,即使外部函数已经...

      aprendendo-Closures-criando-uma-calculadora:我将创建一个计算器,以详细了解javascript中的闭包

      在JavaScript编程语言中,闭包是一个非常重要的概念,它对于理解和编写高级代码至关重要。闭包是一种特殊的作用域机制,允许函数访问并操作其外部作用域的变量,即使在其外部作用域已经结束之后。通过创建计算器的...

      JavaScript高级_javascript_zip_

      1. **闭包(Closures)**:闭包是一种特殊的函数,它能够记住并访问其定义时的作用域,即使这个作用域在函数执行时已经不存在了。利用闭包可以实现数据封装和私有变量,是JavaScript中实现模块化的重要手段。 2. **...

      [高性能JavaScript编程].(High.Performance.JavaScript).Nicholas.C.Zakas.文字版

      同时,合理利用函数作用域和闭包(closures)能够减少全局变量的污染,进而提高代码的执行速度。 在性能调优的过程中,作者还提到了JavaScript引擎的工作原理,包括如何编译和执行JavaScript代码。理解这些底层细节...

      JavaScript高级与设计模式.zip

      1. **闭包(Closures)**:闭包是JavaScript中的一个重要概念,允许函数访问并操作外部作用域的变量,即使在其外部函数已经执行完毕后。这种特性在模块化、数据封装和异步编程中非常有用。 2. **原型链(Prototype ...

      传智播客JavaScriptL4.rar

      1. **闭包(Closures)**:JavaScript中的闭包是一种强大的特性,它允许函数访问和操作其外部作用域的变量,即使在其定义的函数已经执行完毕后。通过理解闭包,你可以更好地管理内存,创建私有变量,并实现高效的...

      Advanced JavaScript(高级JavaScript编程)

      1. **闭包(Closures)**:JavaScript中的闭包是一种强大的特性,它允许函数访问并操作其词法作用域内的变量,即使在函数执行完毕后仍然保留这些变量。闭包在内存管理、数据封装和模块化编程中起着关键作用。 2. **...

    Global site tag (gtag.js) - Google Analytics