闭包是前端JavaScript中一个较难的知识点,也是很重要的知识点,如果想要深入理解前端知识架构,执着于对技术的探知,闭包一定要搞懂。
在学习闭包之前要了解一些预编译和作用域链的知识,这之间都是有联系的。
作用域链是AO(Activation Object)和GO(Global Object)构成的链。
AO是从函数调用开始创建,到函数调用结束时销毁。GO是从见到JS代码开始创建,到网页关闭时销毁。
作用域链每个函数都有,每个函数在定义(函数声明\函数表达式)时会拷贝其父函数的作用域链,在函数被调用时,生成AO然后将AO压入作用域链的栈顶。
闭包就是用到了作用域链的特性。
闭包说直白点就是能够读取其他函数内部变量的函数,本质上就是函数外部和函数内部连接的桥梁。
先看一个示例:
function createCounter(){
var count = 0;
function counterAdd(){
count++;
console.log(count);
return count;
}
return counterAdd;
}
var counter = createCounter();
counter();
这就是个典型的闭包示例,counterAdd函数拷贝的是父函数createCounter的作用域链,指向的一直是createCounter的AO,它一直没有被销毁。
函数的AO通过作用域链相互连接起来,使得函数体内的变量都可以保存在函数的AO中,这就是闭包最明显的特性。
闭包的危险:
闭包会造成原有的AO不释放,产生内存泄漏。
内存泄漏是很严重的错误,但是闭包有坏处就有好处,所谓得于斯者毁于斯者。
闭包的应用:
- 实现共有变量
- 缓存存储结构
- 封装,实现属性私有化
- 模块化开发,防止污染全局变量
我们以累加器为例,用代码看一下闭包的应用:
1.实现共有变量
function createCounter(){
var count = 0;
function counterAdd(){
count++;
console.log(count);
return count;
}
return counterAdd;
}
var counter = createCounter();
counter();
counter();
counter();
2.缓存存储结构
function createCounter(){
var count = 0;
function counterAdd(){
count++;
console.log(count);
return count;
}
function counterAddTwo(){
count += 2;
console.log(count);
return count;
}
function clearAction(){
count = 0;
console.log(count);
return count;
}
return [counterAdd,counterAddTwo,clearAction];
}
var counterAction = createCounter();
counterAction[0]();
counterAction[0]();
counterAction[1]();
counterAction[0]();
counterAction[2]();
3.模块化
function createCounter(){
var count = 0;
var counter = {
counterAdd:function(){
count++;
console.log(count);
return count;
},
counterAddTwo:function(){
count += 2;
console.log(count);
return count;
},
clearAction:function(){
count = 0;
console.log(count);
return count;
}
}
return counter;
}
var counter = createCounter();
counter.counterAdd();
counter.counterAddTwo();
counter.counterAdd();
counter.clearAction();
这是闭包三种应用场景,闭包在框架中应用尤为常见,也是面试必问的一环,如果想对技术深耕的同学,最好多加练习,深入理解。闭包的关键还是作用域链的特性造就的。