闭包
闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常用方式,就是在一个函数内部创建另一个函数。
魂斗罗例子
现在假设我们在玩魂斗罗。通过秘籍调出了 30 条命,每当我们死亡一次就会减一条命。
有个原则就是尽量不要创建全局变量,并且我们的 lives 变量不能暴露出来防止被别人改动,所以我们用函数封装起来。
function person() {
let lives = 30
}
然后再给它添加一个 die 函数,触发后生命减 1。由于不能写在全局作用域里,所以我们把它写到 person 里面。
function person() {
let lives = 30
function die() {
lives -= 1
return lives
}
}
但是问题来了,根据 js 的作用域原理,我们在函数外面是没办法调用函数里面的变量的。这时候就要用到闭包了。
函数特性
在讲闭包之前我们先仔细探究一下函数的运行机制。
我们都知道函数接受两个参数对象,一个是隐藏的 this 对象(只有在 call 和 apply 方法里可以当做第一个参数传入),另一个是 arguments 类数组对象(里面保存着除 this 之外的所有参数)。
可以把函数具象成一个房间。这个房间有一扇前门,一扇后门。前门就是 arguments 对象,所有的实参值都在这里赋给形参,然后进入函数。后门就是 return,所有返回值都从这里出去。
现在再看上面的例子,我们要从 person 函数外调用里面的 die 函数,理所当然的应该把 die 函数当做返回值送出 person 这个房间。
魂斗罗例子续
在经过一些判断(被子弹打到、碰到敌人、掉到沟里)后,我们的生命减 1 了。
function person() {
let lives = 30
return die = function () {
lives -= 1
return lives
}
}
...
if ( ... ) {
person()() // 29
person()() // 29
}
...
但是这样又有个问题,每次调用都会重置 lives 为 30,导致 die 触发后 lives 一直是 29。
可以添加一个中间变量 die,跟 die 函数同名是为了增加可读性。
let die = person()
die() // 29
die() // 28
这样每次死亡只要调用一下 die() 就可以了。
面向对象写法
上面的功能也可以用面向对象来写
let person = {
lives: 30,
die: function () {
this.lives -= 1
return this.lives
}
}
person.die() // 29
person.die() // 28
用这种方法写例子可以。但是我们每次进游戏,都会创建一个新的人物,这就要用到构造函数来创建对象,涉及到了构造函数的私有变量和特权方法。另一篇博客会详细介绍函数的私有变量和特权方法。
本篇博客转载自http://www.lclscofield.com