闭包指的就是内部函数可以访问外部函数的作用域,也就是通过一个函数的子函数来访问这个函数作用域下的变量。
我们可以看一下下面的函数
function Closure() {
var number = 99;
function getNum() {
console.log(number);
}
return getNum;
}
var result = Closure();
result();
正常的函数在执行后会被销毁,但是像Closure这样,在执行完var result = Closure()后,Closure并没有被销毁,为什么没有被销毁呢?这就不得不提到另一个概念了——链式作用域。
JavaScript的函数在运行时,使用某个变量时会在当前作用域查找变量,如果没有找到,则会到其上层作用域中寻找。也就是说,父对象的所有变量对其子对象都是可见的,这就是链式作用域。
再来看上面的例子,在执行完var result = Closure()后,number 变量仍旧被getNum所引用,而number是在Closure作用域下的,所以Closure也不会被销毁,直到result()执行完之后,才会被销毁。也就是说,在执行result()前,局部变量number会常驻在内存中。
看到这里可能有些人还是有点迷糊,使一个变量常驻在内存中,创建一个全局变量不就好了?可是大家知道全局变量也是有弊端的,全局变量很容易被直接访问并且被修改,如果不小心被修改,可能会造成麻烦。
在一些语言中,会使用private来声明私有变量并且暴露一个访问接口来间接访问一个变量,但是JavaScript中没有private,怎么处理呢?答案就是用闭包。使用闭包可以创建类似私有变量的变量,而且不会造成全局变量的污染,但是仍旧会常驻在内存中。
下面是一个闭包私有化变量的示例,可以尝试一下
//闭包的私有化变量
function Person() {
var money = 100;
this.makeMoney = function () {
money ++ ;
console.log(money)
}
this.offer = function(){
money -- ;
}
return this;
}
var person = new Person();
person.makeMoney();
下面是一道闭包的经典面试题及解析,来自于https://blog.csdn.net/qq_24510455/article/details/101778471
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1); c.fun(2); c.fun(3);
//问:三行a,b,c的输出分别是什么?
解析:首先要明确出现的三个fun函数之间的关系,第一个fun是一个新创建具名函数,第二个fun是一个新创建的匿名函数,第三个被return出去是fun其实是第一个新建的具名函数。
a:fun(0)调用的第一个fun,后面三个a.fun(n)其实调用的都是第一次fun(0)的返回值-匿名fun函数
第一次fun(0) n为0,o没传入,所以undefined
a.fun(1) m为1 n还是0 所以匿名函数调用其实是function(1,0) 第三个fun调用第一个fun, o都是0
所以a输出 undefined,0 ,0, 0
b:fun(0).fun(1)根据a可知输出undefined ,0 ;后面继续调用fun(2) ,由于fun(0)(1)调用使得当前调用还处于第一个fun,所以fun(2)调用到了第二个匿名fun 所以return第三个fun是fun(2,1)也就是第一个fun(2,1);同理到fun(3)的时候第一个fun(3,2)
所以b输出 undefined,0 ,1, 2
c:根据a和b可知,fun(0).fun(1) 和fun(0).fun(1).fun(2) 此时fun(0).fun(1).fun(2) 只是改变了第二个匿名fun的m对于n并没有改变
所以c输出 undefined,0 ,1, 1