什么是闭包?
闭包是指那些能够访问自由变量的函数。
通俗讲就是在函数中访问另一个函数中的的变量。只要某个变量在另外一个函数中还存在引用,那么这个变量的值在内存中就不会被释放,除非这个函数不会再执行。
内部的函数被保存到外部。
之所以可能通过这种方式在 JavaScript 种实现公有,私有,特权变量正是因为闭包,闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后
在JavaScript中,即使调用完函数,该函数内部定义的变量和方法仍会保留在内存中。
在函数退出后,闭包还能够保留对所有局部函数变量的引用。
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。 — 你不知道的JavaScript(上卷)
闭包的构成:
闭包由两部分构成:函数,以及创建该函数的环境。
环境由闭包创建时在作用域中的任何局部变量组成。
闭包产生原因:js函数作用域的特性产生的
本质:JavaScript函数作用域的副作用产品。
通常情况下,函数作用域及其所有变量都会在函数执行后被销毁,但是,创建闭包后,这个函数的作用域就会一直保存到闭包不存在为止。
function add(x) {
return function b(y) {
return x + y;
}
}
var add1 = add(2);
console.log(add1(4)); //6
add1 = null;
console.log(add1(6)); //Uncaught TypeError: add1 is not a function
释放空间之前,可以继续使用内存空间中定义的变量。
闭包的应用:通过闭包模拟私有方法。
私有方法只能被类中的其他方法所调用。
私有方法有利于限制对代码的访问,避免非核心的方法干扰代码的公共接口,减少全局污染。
var calculator = (function() {
var a = 1;
function add(val) {
a += val;
}
return {
add1: function() {
add(-5);
},
add2: function() {
add(100);
},
result: function() {
return a
}
}
})()
console.log(calculator.result()); //1
calculator.add1();
console.log(calculator.result()); //-4
calculator.add2();
console.log(calculator.result());//-100
使用闭包的注意事项
闭包使函数中的变量保存在内存中,造成内存消耗,所以不应滥用。
闭包深层次理解:
作用域链:保存在隐式的属性中【scope】,用户访问不到,js引擎在预编译时访问,存储的就是作用域链。
预编译:有函数预编译AO,全局预编译GO
作用域是预编译时产生的AO GO,作用域链是AO和GO的集合
var global;
function a() {
function b() {
var bb = 123;
aa = 0;
}
var aa = 123
b()
}
a();
a函数的定义
a定义的时候产生全局预编译GO,
定义b函数的时候,可以看到a函数的函数预编译AO。
在函数内部使用函数外部的变量就是闭包。
[[scope]]放的是作用域链的集合
作用域的顶端(放的是最近的东西)放的就是GO,全局预编译产生的全局作用域。
只有a执行b才会定义
当a函数执行的时候:它的作用域链的顶端是自己的函数预编译AO,
当b函数定义的时候
b函数执行时
执行完后,作用域链会销毁。
函数只有在自己执行的时候才有自己的AO
a执行时,b定义
当a执行完毕后,销毁作用域链时,b产生的作用域链不会销毁。b定义的时候还是能访问到。