闭包是一种特殊的对象,它由两部分组成。执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。
当B执行时,如果访问了A中变量对象的值(VO),那么就产生了闭包。
大多数人以B的名字代指在这里产生的闭包,chrome则以A的名字代指闭包。
本文将使用chrome的方式,将A的函数名代指这里产生的闭包。
// demo01
function foo() {
var a = 20;
var b = 30;
function bar() {
return a + b;
}
return bar;
}
var bar = foo();
bar();
在这个例子中,首先有执行上下文foo,foo中定义了函数bar,而通过return bar的方式让bar得以执行。当bar执行时,访问了foo内部的变量a,b。因此产生了闭包。
我们已经知道JS中一个值在内存中失去引用时,垃圾回收机制会根据特殊算法找到它,并将其回收,释放内存。
而函数的执行上下文在执行结束后,生命周期结束,就会失去引用。其空间很快就会被回收。而闭包会阻止这一过程。
var fn = null;
function foo() {
var a = 2;
function innnerFoo() {
console.log(a);
}
fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn
}
function bar() {
fn(); // 此处的保留的innerFoo的引用
}
foo();
bar(); // 2
比如上例中,foo执行完后,按照常理,应该被垃圾回收。但是通过fn = innerFoo
,函数innerFoo的引用被保存了下来。这个行为导致foo的VO也被保存了下来。于是fn在bar内运行的时候,依然可以访问到变量a的值。
这种情况,我们就可以称foo为闭包
下图展示了闭包foo的作用域链
闭包与作用域链
闭包的应用场景
- 函数柯里化-只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数
- 模块
(function () {
var a = 10;
var b = 20;
function add(num1, num2) {
var num1 = !!num1 ? num1 : a;
var num2 = !!num2 ? num2 : b;
return num1 + num2;
}
window.add = add;
})();
add(10, 20);
在这个例子中,使用立即执行函数创建了一个模块。add是模块对外暴露的一个公共方法,而a,b是私有变量。
闭包的一些练习
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i
};
}
console.log( fnArr[3]() ) // 10
因为var i定义了全局变量,在执行fnArr[3]的时候i已经是10,可以应用闭包对函数进行改写
//方法1
var fnArr = []
for (var i = 0; i < 10; i ++) {
fnArr[i] = (function(j){
return function(){
return j
}
})(i)
}
console.log( fnArr[3]() ) // 3
//方法2
var fnArr = []
for (var i = 0; i < 10; i ++) {
(function(i){
fnArr[i] = function(){
return i
}
})(i)
}
console.log( fnArr[3]() ) // 3
//这两个方法的原理类似,对fnArr[i]的赋值立即执行,产生了10个闭包
//匿名函数传入的参数本该在函数结束时被垃圾回收,但是因为fnArr的调用一直保存
//方法3
var fnArr = []
for (let i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i
}
}
console.log( fnArr[3]() ) // 3
//原理是es6的let命令
//let所声明的变量,只在let命令所在的代码块内有效。
上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是3。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
网友评论