闭包及应用场景

闭包是什么

在 JS 忍者秘籍(P90)中对闭包的定义:闭包允许函数访问并操作函数外部的变量。红宝书上对于闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数。
MDN 对闭包的定义为:闭包是指那些能够访问自由变量的函数。这里的自由变量是外部函数作用域中的变量。

概述上面的话,闭包是指有权访问另一个函数作用域中变量的函数

形成闭包的原因

内部的函数存在外部作用域的引用就会导致闭包。从上面介绍的上级作用域的概念中其实就有闭包的例子 return f就是一个表现形式。

1
2
3
4
5
6
7
8
9
var a = 0
function foo(){
var b =14
function fo(){
console.log(a, b)
}
fo()
}
foo()

这里的子函数 fo 内存就存在外部作用域的引用 a, b,所以这就会产生闭包

闭包的作用

保护函数的私有变量不受外部的干扰。形成不销毁的栈内存。
保存,把一些函数内的值保存下来。闭包可以实现方法和属性的私有化

使用闭包需要注意什么

容易导致内存泄漏。闭包会携带包含其它的函数作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,所以要谨慎使用闭包。

闭包应用

1.函数防抖

比如要缩放窗口 触发onresize 事件 需要在这时候做一件事情,但是我们希望拖动的时候只触发一次,比如

1
2
3
window.onresize = function(){
console.log('onresize')//只想触发一次
}

一般方法vs闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
window.onresize = function(){
debounce(fn,1000)
}
var fn = function(){ console.log('fn')
}
var time = ''
function debounce(fn,timeLong){
if(time){
clearTimeout(time)
time = ''
}

time =setTimeout(function(){
fn()
},timeLong)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
window.onresize = debounce(fn,500)

function debounce(fn){
var timer = null
return function(){
if(timer){ //timer第一次执行后会保存在内存里 永远都是执行器 直到最后被触发
clearTimeout(timer)
timer = null
}
timer = setTimeout(function(){

fn()
},1000)

}

}
var fn = function(){

console.log('fn')
}

2.使用闭包设计单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class CreateUser {
constructor(name) {
this.name = name;
this.getName();
}
getName() {
return this.name;
}
}
// 代理实现单例模式
var ProxyMode = (function() {
var instance = null;
return function(name) {
if(!instance) {
instance = new CreateUser(name);
}
return instance;
}
})();
// 测试单体模式的实例
var a = ProxyMode("aaa");
var b = ProxyMode("bbb");
// 因为单体模式是只实例化一次,所以下面的实例是相等的
console.log(a === b); //true

3.为多个组件独立属性

假如我现在要在页面中使用echarts画6个线状图,需要6个容器

需要为每个容器元素声明一个独立id,不然会混乱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
constructor(){
this.state = {id: "EchartsLine"+Util.clourse()};
}
componentDidMount() {


this.myEChart =echarts.init(document.getElementById(this.state.id));//不同id

}

<div
id={this.state.id}

className='echarts-line'>
</div>


clourse(){
let clourse = (function(){
var a = 1;
return function(){
return a++;
}
})(this);
this.clourse = clourse;
}
//使用数字命名 不用害怕被篡改复制代码

4.设置私有变量

内部属性 在java里使用private就可以,但是js 还没有这个东东

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let _width = Symbol();

class Private {
constructor(s) {
this[_width] = s
}

foo() {
console.log(this[_width])
}

}

var p = new Private("50");
p.foo();
console.log(p[_width]);//可以拿到复制代码
//赋值到闭包里
let sque = (function () {
let _width = Symbol();

class Squery {
constructor(s) {
this[_width] = s
}

foo() {
console.log(this[_width])
}
}
return Squery
})();

let ss = new sque(20);
ss.foo();
console.log(ss[_width])

5.拿到正确的值

1
2
3
4
5
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i)//10个10
},1000)
}

遇到这种问题 如何用解决呢

1
2
3
4
5
6
7
for(var i=0;i<10;i++){
((j)=>{
setTimeout(function(){
console.log(j)//1-10
},1000)})(i)

}

原理是 声明了10个自执行函数,保存当时的值到内部

Buy me a cup of coffee,thanks!