什么是闭包
如果一个函数,用了它范围外的变量,那么(这个函数 + 这个变量)就叫做闭包,闭包有权访问其包含函数内部的所有变量,闭包的作用域链包含自己的作用域、包含函数的作用域和全局作用域。当函数返回一个闭包时,这个函数的作用域将会一直存在于内存中,直到闭包不存在。
闭包的作用
模仿块级作用域(匿名自执行函数)
1
2
3
4
5
6function countNumber(n) {
for(var i = 0; i < n; i++) {
console.log('i1',i)
}
console.log('i2',i)
} // i2 51
2
3
4
5
6
7
8function countNumber(n) {
(function(){
for(var i = 0; i < n; i++) {
console.log('i1',i)
}
})()
console.log('i2',i)
} // i2 i is not defined在第二个函数中,在 for 循环外部插入了一个块级作用域。原因是在匿名函数中定义的任何变量,都会在执行结束时被销毁,因此变量 i 只能在循环中使用,使用后即被销毁。这个块级作用域能访问变量 n,是因为这个匿名函数是一个闭包,它能够访问包含作用域中(也就是 countNumber 函数)中的所有变量。这个方法可以尽量避免在全局环境中添加过多变量和函数。也可以减少闭包占用内存的问题,因为没有指向匿名函数的引用。
访问私有变量
任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。1
2
3
4function add(n1, n2){
var sum = n1 + n2
return sum
}在这个函数内部,有3个私有变量:n1、n2 和 sum。函数内部可以访问这几个变量,但是函数外部则不能访问它们。如果在这个函数内部创建一个闭包,则可以通过闭包访问这些变量。
从外部读取函数内部的变量
1
2
3
4
5
6
7
8
9function fn1() {
var n = 1
function fn2() {
return n
}
return f2
}
var result = f1()
result() //1对象的继承
构造函数定义公有方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function MyObject() {
//私有变量和私有函数
var privateVariable = 10
function privateFunction() {
return false
}
//公有方法
this.variable = function() {
return privateVariable++
}
this.function = function() {
return privateFunction()
}
}
var obj = new MyObject()
obj.variable()// 10
obj.variable()// 11
obj.function()// false利用这个方法,可以隐藏不想被直接修改的数据,比如:
1
2
3
4
5
6
7
8
9
10
11
12
13function Person(name) {
this.getName = function() {
return name
}
this.setName = function(value) {
name = value
}
}
var person = new Person('yang')
person.getName() //yang
person.setName('zhang')
person.getName() //zhang在上述代码中,定义了两个公有方法:getName() 和 setName()。这两个函数都可以在函数外部使用,而且它们都是在构造函数内部定义的,作为闭包都可以访问私有变量
name
。在构造函数外部,没有任何办法访问到name
。需要注意的是这个方法的私有变量 name 在每个 Person 实例中都会不同,因为每次调用构造函数都会重新创建这两个方法。- 原型模式(静态私有变量)
结果缓存
1
2
3
4
5
6
7
8
9function count(n) {
return function () {
return n++;
};
}
var c1 = count(1);
c1() // 1
c1() // 2
c1() // 3闭包的一个特点就是只要存在引用函数内部变量的可能,JavaScript就需要在内存中保留这些变量。而且JavaScript运行时需要跟踪这个内部变量的所有外部引用,直到最后一个引用被解除(主动把外部引用赋为 null 或者页面关闭),JavaScript的垃圾收集器才能释放相应的内存空间,所以闭包不会释放外部的引用,从而函数内部的值可以得以保留。