首先引用 MDN 文档的一句话做为开头git
闭包是函数和声明该函数的词法环境的组合。
当一个函数被 return
的时候,这个函数内部的词法做用域中的变量是能够被外界访问到的,外层函数执行完毕时被销毁,但因为内部函数做为值返回出去,这些值得以保存下来,存储在内存中,也就是私有性。github
一个基本的例子:闭包
// 来自 MDN function makeFunc() { var name = "DOG"; function displayName() { alert(name); } return displayName; } var myFunc = makeFunc(); myFunc();
闭包是由函数以及建立该函数的词法环境组合而成。这个环境包含了这个闭包建立时所能访问的全部局部变量。执行 makeFunc
时建立的 displayName
函数实例的引用,而 displayName
实例仍可访问其词法做用域中的变量,便可以访问到 name
。由此,当 myFunc
被调用时,name
仍可被访问。函数
在 JavaScript 中,是没有原生支持私有属性的(听说如今已经有了提议),可是咱们可使用闭包来建立一个私有属性。code
// 来自 MDN var makeCounter = function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } }; var counter = makeCounter(); console.log(counter.privateCounter); // undefined console.log(counter.value()); // 0
function func() { var x = 100; return { function() { return x; } } } var m = func(); //运行函数,变量 x 被 m 引用
此时 m
引用了变量 x
,因此函数执行后 x
不会被释放,能够把比较重要或者计算耗费很大的值存在 x
中,只须要第一次计算赋值后,就能够经过 m
函数引用 x
的值,没必要重复计算,同时也不容易被修改。ip
看一个例子:内存
var a = []; for(var i = 0; i < 10; i++) { a[i] = () => { return i; }; } a[6](); //10
在这个简单的函数中,变量 i
是在 for
循环中定义的,若是是在 C++ 或者 Java 中,这样定义的变量,一旦循环结束,变量也就随之销毁,i
的做用范围只在循环这个小块,就称为块级做用域。在 JavaScript中,没有这样的块级做用域。因此上一段代码其实至关于:作用域
var a = []; var i = 0; for(; i < 10; i++) { a[i] = () => { return i; }; } a[6](); //10
这样就很好理解了。因为匿名函数里面没有 i
这个变量,在函数执行的时候,这个 i
他要从父级函数中寻找,而父级函数中的 i
在for
循环中,当找到这个 i
的时候,是 for
循环已经循环完毕,因此所得与预想不一致。rem
改进:文档
var a = []; for(var i = 0; i < 10; i++) { a[i] = (() => { return i; })(); } a[6]; // 6
看到了一段颇有用的话:
当一个子函数被建立时,是父函数的执行致使的,因此当子函数建立时,父函数已经处于执行阶段,因此父函数的执行上下文已经建立了。同时,由于子函数也在父函数的局部变量做用域内,因此,子函数在建立的时候,除了要引用全局上下文,也须要引用父函数的执行上下文。当一个子函数执行时,由于它一样是函数,因此它一样须要建立本身的执行上下文,当它返回的时候,一样也只解除属性中对自身执行上下文的引用,对父函数的执行上下文的引用并无解除,这意味着,父函数的执行上下文与子函数自己共存亡了。只要子函数还存在引用,垃圾收集器就不会销毁它们所在的执行上下文。另外,由于父函数的局部变量并不在全局上下文中,因此它只能在子函数的变量解析中被访问,天然而然就至关于它们是子函数私有的了。