闭包
- 闭包是一种机制,通过私有上下文来保护当中的私有变量;
保护:当前上下文中的变量与其他上下文中的变量互不干扰
保存:保存当前上下文中的数据(堆内存),被当前上下文以外的上下文中的变量所引用,这个数据就保存下来。
- 也可以认为当我们创建的某个私有上下文执行完毕后,不能被释放的时候就形成了闭包;
函数和其周围的状态的引用捆绑在一起就形成了闭包,简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。
可以在另一个作用域中(比如全局)调用这个函数内部的函数并访问到该函数作用域中的成员。
演示语法
首先我们定义一个makeFn的函数,在这个函数中定义一个变量msg,当这个函数调用之后,msg就会被释放掉。
function makeFn () {
let msg = 'Hello';
console.log(msg);
};
maknFn();
如果我们在makeFn中返回一个函数,在这个函数中又访问了msg,那这就是闭包函数。
和刚刚不一样的是,当我们调用完makeFn之后他会返回一个函数,接收的fn其实就是引用了makeFn返回的函数,也就意味着外部的fn对函数内部的msg存在引用。 所以当我们调用fn的时候,也就是调用了内部函数,会访问到msg,也就是makeFn中的变量。msg就不能被释放
所以闭包就是在另一个作用域(这里是全局),可以调用到一个函数内部的函数(makeFn内部返回的函数),在这个函数中可以访问到这个函数(makeFn)作用域中的成员。
所以说,闭包的核心作用就是把我们makeFn中内部成员的作用范围延长了,正常情况下makeFn执行完毕之后msg会被释放掉,但是这里因为外部还在继续引用msg,所以并没有被释放。
function makeFn () {
let msg = 'Hello';
return function() {
console.log(msg);
}
}
const fn = maknFn();
fn();
闭包的使用
这里有一个once函数,他的作用就是控制fn函数只会执行一次,那如何控制fn只能执行一次呢?这里就需要有一个标记来记录,这个函数是否被执行了,我们这里定义一个局部变量done,默认情况下是false,也就是fn并没有被执行。
在once函数内部返回了一个函数,在这个新返回的函数内部先去判断done,如果done为false,就把他标记为true,并且返回fn的调用。
当once被执行的时候,我们创建一个done,并且返回一个函数。然后我们通过pay变量指向这个函数, 也就是外部作用域对once函数内部有引用,once函数执行完毕后done不会被释放掉,当我们调用pay的时候,会访问到函数外部的done,判断done是否为false,如果是将done修改为true,并且执行fn。这样在下一次次调用pay的时候,由于done已经被 标记为true了,所以就不会再次执行了。
function once(fn) {
let done = false;
return function() {
if (!done) {
done = true;
return fn.apply(this, arguments);
}
}
}
let pay = once(function(money) {
console.log(`支付了${money}RMB`)
});
// 只会执行一次。
pay(10);
pay(20);
pay(30);
闭包的本质
函数在执行的时候会创建私有上下文然后入栈执行,当函数执行完毕之后会从执行栈上移除,但是堆上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问外部函数的成员。
这位老哥写的很不错,可以多看几遍http://caibaojian.com/js-closures-indeep.html