闭包(closure)是JS的一个难点,很多高级应用都要依赖它实现,这里参考了多篇博文对其总结,如有错误,欢迎指正!
我们先来看一段“试图在函数外部读取函数内部声明的变量”的代码。
function outer(){
var n = 15;
}
console.log(n);
// Uncaught ReferenceError: n is not defined.
由于JS的作用域链机制,内部变量n 无法读取。那么如果我们想在一个函数内部也有权限访问另一个函数内部的变量该怎么办呢?闭包就是用来解决这一需求的。
一、闭包的定义
先来看一个闭包的经典例子——计数器。
function counterCreator(){
var index = 1;
//闭包函数
function counter(){
console.log(index++);
}
return counter;
}
//变量被赋值为闭包函数
var counterA = counterCreator();
//使用闭包函数
counterA(); //1
外层函数counterCreator()的返回值是counter()函数。因为这个函数在counterCreator()的作用域内部,所以它可以获取counterCreator()作用域下变量index的值,从而实现了读取外层函数内部的变量。
1. 闭包的定义
闭包是一个自带执行环境的特殊函数,即内层的counter()函数。
它能够读取其他函数内部变量的函数。在JS中,只有函数内部的子函数才能读取局部变量,所以也可以理解为“定义在函数内部的函数”。
2. 闭包的形成
(1)外层函数嵌套内层函数:counterCreator()函数嵌套counter()函数。
(2)内层函数使用外层函数的局部变量:counter()函数使用index变量。
(3)把内层函数作为外层函数的返回值:外层函数的返回值是counter。
3. 闭包的本质
是将函数内部和函数外部连接起来的一座桥梁。
4.闭包的特点
闭包可以记住执行环境,让读取的外层函数内部的变量始终保持在内存中。
下面的例子,闭包使得内部变量记住上一次调用时的运算结果。
function counterCreator(){
var index = 1;
//闭包函数
function counter(){
console.log(index++);
}
return counter;
}
//变量被赋值为闭包函数
var counterA = counterCreator();
var counterB = counterCreator();
//使用闭包函数
counterA(); //1
counterA(); //2,闭包可以记住它的执行环境
counterA(); //3
counterB(); //1
counterB(); //2
可以看出,闭包使得函数counterCreator()的内部环境一直存在。原因是,闭包用到了外层变量Index,导致外层函数不能从内存释放。只要闭包没有被垃圾回收机制清除,外层环境提供的运行环境也不会被清除,它的内部变量就始终保存着当前值,供闭包读取。
二、闭包的作用
1.实现属性私有化
2.实现模块化
3.封装块级作用域
三、闭包的缺点
外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。
因此不能滥用闭包,否则会造成网页的性能问题。