闭包无时无刻不存在

前言

闭包是个老生常谈的概念,但是如果说在实际当中运用闭包,可能是你没有感知到的,因为很多情况下,你会写出使用到闭包的函数,但是你并没有意识到这就是闭包,这里为了搞清楚什么是闭包,以及闭包能为我们带来什么好处,一起来探究吧~

为什么需要闭包

局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。

  • 无法共享和长久保存:
1
2
3
4
5
6
7
function addOne(){
var a = 1;
return ++a
}
console.log(addOne()) // 2
console.log(addOne()) // 2
console.log(addOne()) // 2
  • 全局变量容易造成污染
1
2
3
4
5
6
7
8
var a = 1;
function addOne(){
return ++a
}
console.log(addOne()) // 2
console.log(addOne()) // 3
a = 333 // 被污染
console.log(addOne()) // 334

那么为了解决这两个问题,我们首先需要一个可以被共享和长久保存的变量,并且还不会污染全局变量,那么这个时候,我们就需要使用到了闭包。

什么是闭包

闭包是指在JavaScript中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,简单来说,闭包是指可以访问另一个函数作用域变量的函数,注意,这里指闭包是一个特殊的函数,一般是定义在外层函数中的内层函数,这句话有点绕,不慌,我们来看个最简单的例子:

1
2
3
4
5
6
7
8
9
10
11
var getNum;
function getCounter() {
var n = 1;
var inner = function () { return n++; }
return inner;
}

getNum = getCounter();
console.log(getNum()); // 1
n = 666; // 无法被污染
console.log(getNum()); // 2

缺点

  • 占用内存多
  • 不容易被垃圾回收机制释放
  • 容易造成内存泄漏(IE浏览器bug)

使用场景

我们在使用很多库的时候,内部的源码都会使用到闭包,因为一整个库当中必然会有很多变量,如果没有闭包,那么都放在全局作用域下,必然导致不好维护与非常容易导致变量混淆的问题,我们这里就以一个给对象设置私有属性并利用特权(Privileged)方法访问私有属性的例子来简述闭包的使用场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var foo = ( function() {
var secret = 'secret'
// 闭包内的函数可以访问secret变量,而secret变量对于外部确实隐藏的
return {
get_secret: function () {
// 通过定义的接口来访问secret
return secret;
},
new_secret: function ( new_secret ) {
// 通过定义接口来修改secret
secret = new_secret
}
}
} () )
foo.get_secret() //secret
foo.secret // error,无法直接访问
foo.new_secret('i am boy')
foo.get_secret() //i am boy

这里只是很简单的一个例子,如果要深入研究闭包,还需要阅读更多相关源码与实际实践。

总结

闭包就是由函数创造的一个词法作用域,里面创建的变量被引用后,可以在这个词法环境之外自由使用闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。作为一个前端新人,如果说完全理解闭包肯定是不切实际的,这里只是简单的理解什么是闭包以及一般使用场景,闭包的其他更深入的奥秘,必然需要进一步的实践与感悟。