闭包问题
Youky ... 2021-10-11 About 1 min
# 闭包问题
# 闭包的定义
有权访问另一个函数作用域中变量的函数。
# 闭包产生的原因
当前作用域中有对父级作用域的引用。
并不一定要通过返回函数的形式,也可以通过用外部变量保存对父级作用域的引用来体现。
# 闭包的使用场景
- 防抖节流函数 具体实现 (opens new window)
- 实现单例模式
function Single(con){
let flag = null;
return function(){
if(flag) return flag;
else{
flag = new con(...arguments);
return flag;
}
}
}
const Person = Single(function(name, age){
this.name = name;
this.age = age;
})
let foo = new Person('foo', 21);
let bar = new Person('bar', 20);
console.log(foo === bar); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 实现私有变量
const Foo = (function(){
let sym = Symbol();
return class Foo{
constructor(name){
this[sym] = name;
}
getName(){
return this[sym];
}
}
})()
let foo = new Foo('foo');
console.log(foo.getName()) // 只能通过定义的方法获取私有变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 如何解决因闭包产生的循环输出问题
for(var i = 1; i <= 5; i ++){
setTimeout(function timer(){
console.log(i)
}, 0)
}
1
2
3
4
5
2
3
4
5
输出结果都是6的原因:setTimeout作为宏任务加入任务队列。同步任务执行结束执行内部回调时,向父级作用域查找i,而i此时已经变成6了。
解决方案:
- 使用let替换var,因为let创建的是块级变量
- 将i作为setTimeout的第三个参数传入
for(var i=1;i<=5;i++){
setTimeout(function timer(j){
console.log(j)
}, 0, i)
}
1
2
3
4
5
2
3
4
5
- 使用IIFE(立即执行函数),把当前状态的i传入
for(var i = 1;i <= 5;i++){
(function(j){
setTimeout(function timer(){
console.log(j)
}, 0)
})(i)
}
1
2
3
4
5
6
7
2
3
4
5
6
7