前言
说到闭包真的是让我头大的东西,一直以来都只是一个概念,平常也几乎不用,所以一直都没太搞懂。都是最近在看防抖,节流函数的写法,推荐使用闭包来写,所以才重新来理解闭包,防抖和节流之后也会单独写一篇文章来阐述吧。
什么是闭包
MDN中对闭包的定义如下:
函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)
上述句子换句话说就是:闭包是由函数及其引用的变量构成的。那么就有一个问题了,那岂不是所有函数只要内部引用外部的变量都能构成一个闭包,例如:
let a = 1;
function printA(){
console.log(a);
}
按照定义确实上述例子也构成闭包,但怎么跟我们平时见到的闭包不一样呢?
《JavaScript权威指南中》确实也写道:
从技术角度讲,所有的JavaScript都是闭包。
但从实际应用考虑,这样的闭包就没有存在的意义,所以MDN补充道:
也就是说,闭包可以让你从内部函数访问外部函数作用域。
所以引用的变量应该为函数作用域内的变量,换句话说,闭包应存在于函数作用域内部,例如:
//a与printA构成一个闭包
function func(){
let a = 1;
function printA(){
console.log(a);
}
printA();
}
func();
看到这个例子有人可能会想,怎么我看到的闭包都会return一个函数呀?实际上是一样的,return出去只是为了外部调用而已,本质上一样的嘛。
关于为什么能够在内部函数中引用外部函数的变量,对作用域稍有了解的人就会知道:函数中引用变量时,会先从自身函数中读取,如果内部没有就逐层往上读取,直至读取到。这里特别提一点,函数依赖于变量作用域,这个作用域是在函数定义是决定的,而不是函数调用时决定的。(来源《JavaScript权威指南》
) 所以寻找变量时应该从定义时的函数中往上寻找,而非调用时,举个例子:
function func(){
let a = 1;
console.log(a);
}
let a = 2;
func(); //1
闭包的简单应用
前面说了那么多,那么闭包究竟有什么用?闭包的常见用法就是提供间接访问一个变量的方法,有时候我们需要在某些地方用到一个变量,但是又不能定义为全局变量,存在着篡改的风险,那么我们就可以用定义位私有变量,然后通过闭包暴露出去,例如通过闭包写一个计数器,代码如下:
function count(){
let num = 0;
return {
add: function () {
num++;
return num;
},
sub: function () {
num--;
return num;
},
reset: function () {
num = 0;
return num;
}
}
}
let counter = count();
counter.add(); //1
counter.add(); //2
counter.sub(); //1
counter.reset(); //0
counter.add(); //1