一些应该掌握的知识点
当内部函数被保存到外部时,会生成闭包,闭包将会导致原有作用链不释放,造成内存泄漏
闭包原理案例分析
function a () {
function b () {
var bb = 10;
console.log(aa + bb); //控制台显示30
}
var aa = 20;
return b;
}
var newB = a();
newB();
分析:
如上例所示,当函数a声明时会产生一个[[scope]]属性,其中有一个类数组,在这个类数组内会产生一个GO,此时[[scope]]应为{0: GO};当函数a执行时会产生一个自己的AO(暂命名为a.AO),这时[[scope]]中的GO会被往后顶,此时[[scope]]应为{0: a.AO, 1: GO};当函数a中的函数b声明时,函数b会产生一个自己的AO(暂命名为b.AO);当函数b执行时,a.AO和GO如前面情况一样被顺序往后顶,此时[[scope]]应为{0: b.AO, 1: a.AO, 2: GO}。
当函数a时执行完成后,最终返回函数b,此时函数a已经执行完毕,而函数b中的AO为{bb: 10,aa: undefined},这时若按之前JavaScript的预编译这一节理解来看理应认为a.AO应该已经被清除,同时b.AO应该也不复存在,控制台应该会报错,但示例代码最终执行结果却是控制台显示30,这是为什么呢?
实际上,当函数a执行完毕时,函数b通过函数a被赋予到了一个外部变量newB,此时原有的作用域链并没有释放,变量newb形成了一个闭包,在闭包内,当语句aa+bb在执行时,因为aa值为undefined,所以此时aa就会沿着原有的作用域链往下查找,aa在原作用域链查找时在a.AO中查到了值,值为20,语句aa+bb得以执行,因此示例代码最终执行结果为控制台显示30。
闭包的几个作用
- 实现公有变量计算
示例:函数累加器
function a () { var num = 0; function b () { console.log(++num); } function c () { console.log(--num); } return [b, c]; //返回一个数组 } var getcounter = a(); getcounter[0](); //控制台显示1 getcounter[0](); //控制台显示2 getcounter[1](); //控制台显示1 getcounter[1](); //控制台显示0
做缓存
示例:设定一个吃货函数,能买能吃,并且买什么就吃什么
function eater () { var food = ''; function eat () { console.log(food); } function buy (foods) { food = foods; } return {eat: eat, buy: buy}; } var person = eater(); person.buy('pork'); person.eat(); //控制台显示pork person.buy('mango'); person.eat(); //控制台显示mango
实现封装,属性私有化(注:即不希望内部变量在外部被操作)
- 示例:私有化函数$
JavaScript
function wrapper () {
var slice = Array.prototype.slice;
function $ () {
slice;
function add () {}
function show () {}
return {add: add,show: show};
}
}