背景了解
-
es5作用域的问题
变量作用域分为局部作用域和全局作用域,它们的规则是:
①函数外部不能访问到函数内的局部变量
②函数内部可以读取全局变量 -
js的垃圾回收机制导致函数内部变量出了函数就会自动销毁
现实需求
人们有时在函数外部的时候,需要能够访问到函数内部的变量
解决方法引入:闭包
1.概念
在《javascript权威指南》里面,闭包被定义为:函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内
简单一点来说,就是我们在函数内部重新定义一个函数,新的函数由于在函数的内部,可以读取函数的变量,把函数的变量作为一个返回结果调出函数内部,而不受垃圾回收机制的影响。
2.举例说明
function f1() {
var n = 100; //局部变量n
function f2() { //引入的新函数
alert(n); //获取到对于f2而言的全局变量n
}
return f2; //返回f2获取到的变量n
}
var result = f1(); //定义一个变量等于f1返回结果n,即发f1的局部变量
console.log(result())// 100
3.疑惑点
- 有人问为什么不能直接返回n?因为局部变量出来函数会被销毁
function f1() {
var n = 100;
return n; //n=100
}//此时n已销毁
- 为什么需要再定义一个变量?因为我们需要得到的就是一个变量,便于后面的继续使用。
并且在使用时需要()调用,否则得到的会是一整个函数(如下图)
var result = f1();
console.log(result())// 100
console.log(result)//f2(){alert(n)}
4.应用
for循环里面定义了一个点击函数,但是因为i是在函数外部定义和循环遍历的,它的值给不到函数内部的aler()
var list=document.querySelectorAll('li')
for(var i=0;i<list.length;i++){
list[i].onclick=function(){
alert(i)
}
}
采用闭包方法:点击第一个li,跳出它的索引值0
for(var i=0;i<list.length;i++){
function bibao(i){
list[i].onclick=function(){
alert(i)
}
}
bibao(i)
}
5.总结闭包的特征
* 1、需要函数嵌套函数
* 2、函数外部可以引用内部参数和变量。
* 3、参数和变量不会被垃圾回收机制所收回。
6.总结闭包的优点和缺点
优点:
* 1、可以将一个数据和变量长期的存储在内存中
* 2、避免全局变量的污染。
* 3、私有成员的存在。
缺点:
* 1、常驻内存,增加内存的使用量。
* 2、使用不当会造成内存泄露。--解决方法是:在退出函数之前,将不使用的局部变量全部删除。
7.注意
由于闭包方法存在着一定的缺陷,除了上述缺点之外,它还会 在父函数外部,改变父函数内部变量的值,这一点也相当值得小心!!!
因此,在es6新语法中,引入了let变量。
8.为什么let可以解决闭包?(尚待继续了解)
es6新语法里面引入块级作用域,每个作用域互不干扰。
var声明的变量,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变。
而let声明的变量,仅在块级作用域内有效,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量。而JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
我们可以继续看这个例子: