闭包

如果在内部函数使用了外部函数的变量, 就会形成闭包. 闭包保留了外部环境的引用

如果内部函数被返回到了外部函数的外面, 在外部函数执行完后, 依然可以使用闭包里的值

闭包的形成

在内部函数使用外部函数的变量, 就会形成闭包, 闭包是当前作用域的延伸。

1
2
3
4
5
6
7
8
function a() {
var aa = 100
function b() {
console.log(aa)
}
b()
}
a()

从代码的角度看, 闭包也是一个对象, 闭包里包含哪些东西呢?
在内部函数b中使用了外部函数a中的变量, 这个变量就会作为闭包对象的属性!!

1
2
3
4
5
6
7
8
function a() {
var aa = 100
function b() {
console.log(b)
}
b()
}
a()
  1. 会不会形成闭包?
  2. 如果形成, 闭包里有什么?
    会形成闭包, 由于b的声明是在外部函数a中的, 在内部函数b中使用了b, 会形成闭包。闭包里存放了一个属性, 就是b函数。
1
2
3
4
5
6
7
8
9
function a() {
var aa = 100
function b() {
var b = 200
console.log(b)
}
b()
}
a()

会不会形成闭包?
不会形成闭包, 由于在b函数内部定义了变量b, 打印时直接使用的是内部函数里的变量b, 不会形成闭包。

闭包的保持

如果希望在函数调用后, 闭包依然保持, 就需要将内部函数返回到外部函数的外部。

1
2
3
4
5
6
7
8
9
10
11
function a() {
var num = 0
function b() {
console.log(num++)
}
return b
}
var demo = a() // ①
console.dir(demo) // ②
demo()
demo()

代码注释①中, 调用a函数, 将内部函数b返回, 保存在函数a的外部。

代码注释②中, 调用demo函数, 实质上是调用内部函数, 在函数b的[[scopes]]属性中可以找到闭包对象, 从而访问到里面的值。

闭包要形成: 在内部函数使用外部函数的变量。
闭包要保持: 内部函数返回到外部函数的外面。

闭包的声明周期

  1. 产生:在嵌套内部函数定义执行完时就产生了(非调用时)。
  2. 死亡:在嵌套的内部函数称为垃圾对象时。

闭包的应用

  1. 在函数外部访问私有变量
  2. 实现封装
  3. 防止污染全局变量

在函数外部访问私有变量

1
2
3
4
5
6
7
8
9
10
function a() {
var num = 0
function b() {
console.log(num++)
}
return b
}
var demo = a()
console.dir(demo)
demo()

本来在函数a的外部(全局)不能直接访问内部变量num, 通过闭包就可以使用num变量了

面向对象的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Person() {
var uname
function setName(uname) {
this.uname = uname
}
function getName() {
return this.uname
}
return {
getName: getName,
setName: setName,
}
}

var xiaopang = Person()
xiaopang.setName('xiaopang')
var name = xiaopang.getName()
console.log(name)

定义了一个函数Person, 一个内部变量uname, 两个内部函数

返回内部函数, 也是使用了闭包特性

这样在Person函数的外部, 通过get和set方法对变量uname进行操作, 这就是面向对象里的封装的思想

自定义模块

闭包注意的问题

  1. 无意识的使用了闭包