1.定义
闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
2.实例
实例1
function init () {
let name = 'gaodda' // name 是一个被 init 创建的局部变量
function showName () { // showName() 是内部函数,一个闭包
alert(name) // 使用了父函数中声明的变量
}
showName()
}
init()
/**
* init() 创建了一个局部变量 name 和一个名为 showName () 的函数。
* showName () 是定义在 init() 里的内部函数,仅在该函数体内可被获取。
* 请注意,showName () 内没有自己的局部变量,然而它可以访问到外部函数的变量,
* 所以 showName () 可以使用父函数 init() 中声明的变量 name 。
*/
实例2
function makeAdder (x) {
return function (y) {
return x + y
}
}
var add5 = makeAdder(5)
var add10 = makeAdder(10)
console.log(add5(2)) // 7
console.log(add10(2)) // 12
/**
* 定义了 makeAdder(x) 函数,它接受一个参数 x ,并返回一个新的函数。返回的函数接受一个参数 y,并返回x+y的值。
* 从本质上讲,makeAdder 是一个函数工厂 — 他创建了将指定的值和它的参数相加求和的函数。
* 在上面的示例中,我们使用函数工厂创建了两个新函数 — 一个将其参数和 5 求和,另一个和 10 求和。
* add5 和 add10 都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。
* 在 add5 的环境中,x 为 5。而在 add10 中,x 则为 10。
*/
用闭包模拟私有方法
var Counter = (function() {
var privateCounter = 0
function changeBy(val) {
privateCounter += val
}
return {
increment: function() {
changeBy(1)
},
decrement: function() {
changeBy(-1)
},
value: function() {
return privateCounter
}
}
})()
console.log(Counter.value()) /* 0 */
Counter.increment()
Counter.increment()
console.log(Counter.value()) /* 2 */
Counter.decrement()
console.log(Counter.value()) /* 1 */
/**
* 共享:Counter.increment,Counter.decrement 和 Counter.value。
* 共享环境创建于一个立即执行的匿名函数体内。这个环境中包含两个私有项:名为 privateCounter 的变量和名为 changeBy 的函数。
* 这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。
*
*/
/**
* 创建多个计数器
*/
var makeCounter = function() {
var privateCounter = 0
function changeBy(val) {
privateCounter += val
}
return {
increment: function() {
changeBy(1)
},
decrement: function() {
changeBy(-1)
},
value: function() {
return privateCounter
}
}
}
var Counter1 = makeCounter()
var Counter2 = makeCounter()
console.log(Counter1.value()) /* logs 0 */
Counter1.increment()
Counter1.increment()
console.log(Counter1.value()) /* logs 2 */
Counter1.decrement()
console.log(Counter1.value()) /* logs 1 */
console.log(Counter2.value()) /* logs 0 */
/**
* 每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境
* 然而在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。
*/
在循环中创建闭包:一个常见错误
/** 不能正常输出想要结果 */
for (var i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i + ' ')
}, 100);
}
/** 正常输出想要结果 */
for (var i = 0; i < 5; ++i) {
(function (i) {
setTimeout(function () {
console.log(i + ' ')
}, 100)
}(i))
}