闭包是 JavaScript 的难点之一。大多数教程只告诉你,闭包就是一个函数中的另外一个函数,但这只是闭包的表象。本篇文章就带你透过表象,看看闭包的本质。javascript
写 JavaScript 而不知道什么是闭包,就如同写 Java 而不知道什么是类 --- JSON之父 Douglas Crockfordjava
首先看看下面这一段代码:浏览器
// 这段代码理论上来说,是一个闭包
// 可是并不纯正
var x = 0;
function addNumber() {
return 1 + x;
}
复制代码
上面这段代码并非纯正的闭包。x
定义在全局做用域中,因此并不能保证它不被修改。说到这里要提一下,JavaScript 是一个词法做用域的语言。这意味着在函数外定义的变量能够在函数内使用,而反过来则不行。你不能在函数外部使用一个在函数内定义的变量。微信
下面的代码是一个闭包,x
定义在函数外部。并且在函数执行完以后 addNumber
仍然能够访问到 x
的值。闭包
function closedFunction(x) {
// 当咱们把代码用函数包裹起来时
// 就建立了一个函数做用域
// 传递给函数的变量都是独立的
function addNumber() {
return 3 + x;
}
return addNumber;
}
console.dir(closedFunction(3));
复制代码
在浏览器中打印出来的值证实了 x
是一个闭包模块化
那么问题来了,若是消耗函数外部的变量就是闭包,那为何下面这段代码不是闭包呢?函数
function closedFunction(x) {
var numberItem = x + 3;
return numberItem;
}
console.dir(closedFunction)
复制代码
当咱们调用函数时,会建立一个函数做用域并为它分配内存。这个过程会一直持续到函数执行结束,内存被释放为止。在函数执行结束后,做用域中的值也将永远消失。ui
变量的做用域不在函数外部,因此不构成闭包spa
可是若是用一个函数包裹另外一个函数,它将建立另外一个做用域 --- 它旨在告诉 JavaScript,在这个函数执行完以后不要当即销毁它。3d
下面这段代码中的变量 counter
是一个闭包,由于它在被调用的函数以外( Increment
) 。
变量counter
是上述代码片断中的闭包
在某种程度上,闭包只是具备保留数据的函数。建立闭包实际上是在告诉JavaScript记住函数中事物的状态 --- 只有被使用的变量才会被视为 闭包
由于闭包是有状态函数,它们在被调用以后会记住其私有变量数据。因为变量是私有的,外部函数没法经过显示调用来访问这些私有变量。这样能够使整个函数自成一体,而且能够保护其变量免受没必要要的更改。
词法做用域
所以,JavaScript 中的闭包是一种在无需显示建立类的状况下,就能将代码模块化且自包含的方式方法。使用闭包能够实现代码的可复制性,并且不用担忧污染全局做用域。
闭包不单单是将一个函数放在另外一个函数中的行为,它是一种用于建立可防止外部更改私有变量的技术,这些变量与程序的其余部分隔离,而且具备持久性。