您的位置:首页 > 其它

到底什么是闭包

2017-02-25 22:13 197 查看
一开始接触闭包有些问题一直绕不过去,可了看其他的资料,也从网上查了查,下面是我总结的一些东西:

“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

通俗的讲:就是函数a的内部函数b,被函数a外部的一个变量引用的时候,就创建了一个闭包。

(这样在执行完var c=a()后,变量c实际上是指向了函数b,再执行c()后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b)

   function a(){
           var i=0;
           function b(){
              alert(++i);
           }
              return b;
       }

 var c = a();
        c();

 

闭包的特性:

①.封闭性:外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口;
②.持久性:一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,闭包结构依然保存在
系统中,闭包中的数据依然存在,从而实现对数据的持久使用。

优点:

① 减少全局变量。

② 减少传递函数的参数量

③ 封装;

   缺点:
 使用闭包会占有内存资源,过多的使用闭包会导致内存溢出等.

最简洁、直击要害的回答,我能想到的分别有这么三句

1、闭包是一个有状态(不消失的私有数据)的函数。

2、闭包是一个有记忆的函数。

3、闭包相当于一个只有一个方法的紧凑对象
(a compact object)[b]。

[/b]上面这三句话是等价的,而其中第 3 句最精妙,可以指导何时、如何用好闭包,后面我会详细分析。

澄清概念

MDN(Mozilla Developer Network)上的闭包定义是这样的:

Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope). In other words, the function defined in the closure 'remembers' the environment in which it was created.

Closures - JavaScript

关于什么是闭包以及有哪些典型的用法(实例)、注意事项,MDN 其实已经解释得很清楚了,建议英文好的同学先耐心读完这篇好文。

我换个角度来谈谈。

首先,很明确——闭包是一个函数,一种比较特殊的函数。什么是函数?函数就是一个基本的程序运行逻辑单位(模块),通常有一组输入,有一个输出结果,内部还有一些进行运算的程序语句。所以,那些仅仅说闭包是作用域(scopes)或者其它什么的,是错误的,至少不准确。

MDN 还有一段解释:

A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.

理解了以上这些概念,关于“什么是闭包”您的大脑中是否出现了下面这张图(用 UML 组成结构图来表示):


有实例有真相

让我们先回顾下传统函式的机理。

我们说普通函式自身是没有状态的(stateless),它们所使用的局部变量都是保存在函式调用栈(Stack)上,随着函式调用的结束、退出,这些临时保存在栈上的变量也就被清空了,所以普通函式是没有状态、没有记忆的。

例如下面的普通函式 inc(),不管执行多少次都只返回 1:

var inc = function () {
var count = 0;
return ++count;
};

inc(); // return: 1
inc(); // return: 1


为什么这样?这是因为这里的 count 只是一个普通函式的局部变量,每次执行函式时都会被重新初始化(被第一条语句清零),它不是下面例子中可以保持状态的闭包变量。

再来看闭包的例子。

这可以说是一个最简单的 JavaScript 闭包的例子,这里的 inc() 是一个闭包(函式),它有一个私有数据(也叫闭包变量) count(即函式中的第 2 个 count)。

var inc = (function () { // 该函数体中的语句将被立即执行(IIFE)
var count = 0; // 局部变量 count 初始化
return function () { // 父函式返回一个闭包(函式引用)
return ++count; // 当父函式 return(即上一个 return)后,这里的 count 不再是父函式的局部变量,而是返回结果闭包中的一个闭包(环境)变量。
};
}) ();

inc(); // return: 1
inc(); // return: 2


我还未研究过任何 JavaScript 引擎(解释器)的源码,所以只好根据常识与逻辑作些合理的推测。

在本例中第 2 个 count 作为闭包的私有数据,很可能是被 JS 引擎存放到了堆(Heap)上,而且是按引用(byref)来访问,所以可以保持状态,实现计数累加;而第 1 个 count 只是存放在函式调用栈(Stack)上的局部变量,于是那个 IIFE 父函式一退出它就被销毁了,它的作用主要是用来初始化(赋值)给担任闭包变量的第 2 个 count。

可见两个 count 虽然同名,却是两个截然不同的变量!

这点恐怕正是许多 JS 初学者(包括当年的我)屡屡见到闭包时,感到最为大惑不解的地方吧。我们以为父子函式里外两个同名的变量是一回事,而其实它们不是,也不知道这背后究竟发生了哪些变化。

关于上面提到的内存管理模型中栈与堆的区别,建议不熟悉的同学可以参考下图和文章:


MDN:
Concurrency model and Event Loop

闭包 vs. 对象

实现同样的计数功能,不用闭包怎么写?同样以 JavaScript 为例,用传统的 OOP 来写:

var obj = {
count: 0,
inc: function () {
return ++this.count;
}
};
obj.inc(); // count: 1
obj.inc(); // count: 2


用闭包与用对象,区别在哪?

其实主要区别就一个:这里用的是普通对象 obj 的方法(函数)inc,让 count 作为 obj 的成员变量来保存数据。而前面第一个例子直接用闭包函数 inc 的话,连 obj 这个对象也可以省掉,让 count 直接成为 inc 闭包内部所保存的状态(环境)变量,这样写起来就比传统的 OOP 更为紧凑,前者用 inc(),而后者用 obj.inc(),尽管两者最终实现的功能和效果基本是一致的。

通过以上这两个小例子的比较,你可以充分体会到在 JavaScript 中,函数(functions)作为首席/头等公民(first-class object)的地位。由于有了闭包,加上在 JavaScript 中函数也是对象——一个函数可以像一个传统的对象那样拥有自己的属性、私有数据和状态(不会随着栈而清空),许多简单功能的实现可能无需再借助 objects 了。

关于闭包与对象之间的联系,MDN 也说得很清楚:

A closure lets you associate some data (the environment) with a function that operates on that data. This has obvious parallels to object oriented programming, where objects allow us to associate some data (the object's properties) with one or more
methods.

Consequently, you can use a closure anywhere that you might normally use an object with only a single method.

你看,闭包是不是相当(近似)于一个只有一个方法的对象?只有一个公开方法,而且成员数据几乎全私有(闭包)的对象,自然是一个紧凑版的对象了。

实际应用

读完本文,估计您对“到底什么是闭包”已经有了比较准确、细致的了解。下一个关心的问题,一定是:闭包在 Web 和 JavaScript 开发中有哪些实际的应用呢?

最后,推荐您继续阅读我的这篇文章,看看闭包作为紧凑对象的一个经典应用,如何利用一个非常简单的闭包来实现前后端开发中普遍存在的二元状态切换(toggle)功能:

JavaScript 设计模式之开关闭包(Closure Toggle)

参考

MDN: Closures - JavaScript

Wikipedia: Closure (computer programming)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: