JS闭包

无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被 GC(Garbage Collector)回收,内存中仅保存全局作用域(全局执行环境的变量对象)。

var scope = "global scope";
function fa () {
  var scope = "local scope";
  function fb () { return scope; }
  return fb();
}
var fc = fa();
console.log(fc);	//local scope
console.log(scope);	//global scope

 

闭包:函数有权访问另一个函数作用域中的变量。

创建闭包的常见方式,就是在一个函数内部创建另一个函数。

var scope = "global scope";
function fa () {
  var scope = "local scope";
  function fb () { return scope; }
  return fb;
}
var fc = fa()();
console.log(fc);	//local scope

> 将函数内的一对圆括号移动到了 fa() 后,这样 fa() 返回的仅仅是函数内嵌套的一个函数对象,返回的并不是结果。可以让函数 fc() 在任何环境下随时调用 fb()。

由上边的例子可以将闭包表述为:函数 fa 的内部函数 fb 被 fa 外的变量 fc 引用了。

在定义函数 fa() 的时候创建了作用域链,嵌套的函数 fb() 定义在这个作用域链中,其中的变量 scope 为局部变量,不管在何时何地执行函数 fb() ,即使 fa() 执行完毕后被销毁,这种绑定在执行 fb() 时依然有效。即函数 fc() 可以捕捉到局部变量(和参数),并一直保存下来。

嵌套的函数如何能调用不存在的作用域链?如果想搞清楚这个问题,需要更深入地了解类似C语言这种更底层的编程语言,并了解基于栈的CPU架构:如果一个函数的局部变量定义在CPU的栈中,那么当函数返回时它们的确就不存在了。

 

 

作用:

当需要在模块中定义一些变量,并希望这些变量一直保存在内存中但又不“污染”全局变量时,可以使用闭包。

来看一个计数器的例子:

uniqueInteger.counter = 0;
function uniqueInteger () {
  return uniqueInteger.counter++;
}
console.log(uniqueInteger.counter);	//0
uniqueInteger();
console.log(uniqueInteger.counter);	//1

> 这个函数使用自身的一个属性来保存每次返回的值。但这样做恶意代码可能将计数器重置或赋值一个非整数,从而修改计数器。而闭包可以捕捉到单个函数调用的局部变量,并将这些局部变量用做私有状态。代码如下:

var uniqueInteger = (function () {
  var counter = 0;
  return function () { return counter++; }
})();    //定义了一个立即调用函数,两部分:(function(){})为一个表达式,紧跟的()表示立即执行这个函数
console.log(uniqueInteger());	//0
console.log(uniqueInteger());	//1

> 这个函数返回另外一个函数(嵌套的函数),将嵌套的函数的值返回赋值给uniqueInteger。嵌套的函数是可以访问作用域内的变量的,而且可以访问外部函数中定义的counter变量。当外部函数返回后,其他任何代码都无法访问counter变量,只有内部函数才能访问到它。

 

 

 

像counter一样的私有变量不是只能在一个单独的闭包内使用,在同一个外部函数内定义的多个嵌套函数也可以访问的到,多个嵌套函数都共享一个作用域链,代码如下:

function counter () {
  var n = 0;
  return {
    count: function () { return n++; },
    reset: function () { n = 0; }
  };
}
var c = counter(), d = counter();
console.log("c.count: " + c.count());    //0
console.log("d.count: " + d.count());    //0    互不干扰
console.log("c.reset: " + c.reset());    //reset() 和 count() 方法共享状态		
console.log("c.count: " + c.count());    //0    重置过c
console.log("d.count: " + d.count());    //1

> counter()函数返回了一个“计数器”对象,这个对象包含两个方法:count()计数,reset()重置。这两个方法都可以访问私有变量n。每次调用counter()都会创建一个新的作用域链和一个新的私有变量。因此调用两次counter()就会得到两个计数器对象,而且包含不同的私有变量,调用其中一个对象的count()或reset()不会影响另一个对象。

 

你可能感兴趣的:(JavaScript,JS,闭包)