js闭包

  在程序设计语言中,闭包(即词法闭包lexical closures或函数闭包function closures)是用一等函数(first class)实现词法作用域(lexically scoped)名称绑定(name binding)的一种技术。操作上说,一个闭包是一个用来存储一个函数及其环境的一条记录(record),这个环境会在闭包创建的时候,在函数的每个自由变量(free variable——在局部使用,但是定义在外部作用域)与其值或名称绑定的存储区之间建立一个映射。不同于一般普通函数(plain function),即使当函数在其作用域外被调用时,闭包仍然允许函数通过闭包的引用访问(存取)捕获的变量(captured variables)。

  以上释义比较抽象,我们先用JavaScript语言举一个简单的例子并加以说明(我在此采用维基百科中说给出的例子并加以改写),在段代码与以上概念之间建立一个映射,让大家有一个直观的概念。稍后,我们再来理解这些概念是什么、如何理解?

function   startAt(CAPTURED) {

  function incrementBy(i) {

      return CAPTURED + i;

  }

  return incrementBy;

}

  以上的代码片段定义了一个高阶函数(higher-order function)startAt,这个函数接收一个参数CAPTURED(我将由闭包捕获的变量大写。这并不是JavaScript的标准实践,也不鼓励这样做,这里只是为了方便说明[4])和一个内部函数(nested function) incrementBy。尽管CAPTURE变量不是incrementBy的局部变量,由于incrementBy处于CAPTURED变量的词法作用域中,这个内部函数可以访问变量CAPTURED。函数startAt返回了一个包含函数incrementBy的闭包,这个函数把变量i与CAPTURED相加然后和CAPTURED在当前(this)调用startAt的一个引用返回,所以当incrementBy被调用时会知道当前的CAPTURED的值。

应用以上函数得到运行结果如下:

var   startAt10 = startAt(10); // closure1

var   startAt100 = startAt(100); // closure2

startAt10(1);

>>   11

startAt100(1);

>>   101

  我们注意到,因为startAt返回一个函数,所以变量startAt10 和 startAt100 是属于函数类型。运行startAt10(1)会返回11,运行startAt100(1)会返回101。尽管startAt10和startAt100都关联到同一个函数incrementBy,相关的环境却不同,调用闭包会将CAPTURED与不同值的不同变量绑定,因此执行函数会得到不同的结果。

--------------------------------- 使用闭包 ---------------------------------

JavaScript经常使用下面这种模式,把捕获的亦是作为私有数据:

var pingpong = (function() {
	var PRIVATE = 0;

	return {
		inc: function( n ) {
			return PRIVATE += n;
		},
		dec: function( n ) {
			return PRIVATE -= n;
		}
	}
})();

pingpong.inc( 100 );
//=> 100;

pingpong.dec( 7 );
//=> 93

  对象pingpong是由作为块作用域的匿名函数构建,并包含两个闭包 inc 和 dec 。最有趣的部分是,捕获的变量 PRIVATE 是这两个闭包的私有变量,除了通过调用这两个函数之一,无法通过任何手段进行访问:

pingpong.inc( 100 );
//=> 100;

pingpong.dec( 7 );
//=> 93

  甚至添加其他函数也是安全的:

pingpong.div = function( n ) {return PRIVATE / n;};

pingpong.div( 3 );

//ReferenceError: PRIVATE is not defined

--------------------------------- 闭包的抽象 ---------------------------------

闭包为JavaScript提供了私有访问,这是一种提供抽象的好方法(例如闭包允许你在创建函数时做一些“配置”)。创建一个名为“plucker”的函数,接收一个键并将其传给一个关联结构,如数组或对象,返回键值的函数。具体实现如下:

function plucker( FIELD ) {
  return function( obj ) {
    return ( obj && obj[FIELD] );
  }
}

  

通过测试这个实现可以看出其行为:
var best = {title: "Infinite Jest", author: "BY"};
var getTitle = plucker( "title" );
getTitle( best );
//=> Infinite Jest

正如我所提到的,plucker 也可以操作数组:
var books = [{title: "BY"}, {stars: 5}, {title: "X"}];
var third = plucker( 2 );
third( books );
//=> {title: "X"}

原文地址:https://www.cnblogs.com/frontendBY/p/4770756.html