说起闭包 就不得不提到作用域和作用域链了
作用域:
- 全局作用域
函数外部定义的作用域
- 函数作用域(也称局部作用域)
在函数内部定义的,函数外部不能访问函数内部定义的变量
但函数内部可以访问函数外部定义的变量
- 块级作用域(let const声明的)
只能在定义的 花括号 { } 中访问定义的变量,例如:if或for循环
作用域链:类似于原型链,但又不同
先在当前范围内部查找是否有变量定义和赋值,有则使用,没有由往上一级函数去查找,有则使用,没有则往父级的父级去查找,以此类推......
现在引入闭包的概念
闭包是什么?
- 广义上的闭包:只要函数内部访问父级作用域的变量,就已经形成闭包
- 狭义的闭包(通俗来讲):
函数套函数
内部函数引用到外部函数的变量
内部函数通过return返回到外部
闭包优缺点:
- 优点:
缓存数据
隔离作用域,防止变量污染
- 缺点:
数据溢出,导致内存泄露(解决:将不用的闭包引用设置为null )
代码演示:
//外部函数
function fn1() {
let i = 1;
const fn2 = function() {
console.log(i++)
}
return fn2
}
let res1 = fn1()
res1() //1 //类似于vue计算属性
res1() //2
res1() //3
res1() //4
应用场景
- 数组for循环
var arr=[]
for (var i = 0; i < 5; i++) {
arr[i]=function(){
return i
}
}
console.log(arr[0]());//5
console.log(arr[1]());//5
console.log(arr[2]());//5
console.log(arr[3]());//5
console.log(arr[4]());//5
现在还是想打印0,1,2,3,4
方法1:可以使用let(因为let是块级作用域且有缓存性)
var arr=[]
for (let i = 0; i < 5; i++) {
arr[i]=function(){
return i
}
}
console.log(arr[0]());//0
console.log(arr[1]());//1
console.log(arr[2]());//2
console.log(arr[3]());//3
console.log(arr[4]());//4
方法2:可以使用闭包
var arr=[]
for (let i = 0; i < 5; i++) {
(function(i){
arr[i]=function(){
return i
}
})(i)
}
console.log(arr[0]());//0
console.log(arr[1]());//1
console.log(arr[2]());//2
console.log(arr[3]());//3
console.log(arr[4]());//4
- 定时器中使用
for (var index = 0; index < 5; index++) {
setTimeout(function(){
console.log(index); //5个5
},0)
}
现在还是想打印0,1,2,3,4 (也可以使用let 上面有) 并且每隔一秒打印一次
for (var i = 0; i < 5; i++) {
(function(i){
setTimeout(function(i){
console.log(i);//0 1 2 3 4
},i*1000)
})(i)
}
- dom操作使用(利用闭包得到当前li的索引号)
for (var i = 0; i < lis.length; i++) {
lis[i].onclick=function(){
console.log(i); //点击每一个li都打印的是5
}
}
解决:
var lis = document.querySelectorAll("li");
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].onclick = function () {
console.log(i); //0 1 2 3 4
};
})(i);
}