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.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值