JavaScript闭包

有权访问另外一个函数作用域中的变量的函数,可以理解为能够读取其他函数内部变量的函数。

闭包

ES5中变量只有两种作用域:函数外部,全局变量;函数内部,局部变量。且变量提升机制还会将变量声明提升到它所在作用域的顶端去执行。

这样,变量一多,难免会产生冲突。使用闭包,可以解决这一问题。

函数内部的变量都是局部变量,在函数外部是无法访问的。闭包其实就是充当一个桥梁的作用,从外部可以间接地访问到内部的变量。

简单来讲,就是在一个函数内部返回另外函数,通过返回的函数去获取主函数内部的局部变量。

正常函数执行完毕后,里面声明的变量被垃圾回收处理掉,但是闭包可以让作用域里的变量,在函数执行完之后依旧保持没有被垃圾回收处理掉,从而被外部访问。因此闭包也可以将临时变量“缓存”在内存中,这一特性导致也决定了闭包将会消耗更多的内存,因此谨慎使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function user(name) {
var age;
return {
getName: function() {
return name;
},
setName: function(newName) {
name = newName;
},
getAge: function() {
return age;
},
setAge: function(newAge) {
age = newAge;
}
}
}

var zhaoo = user('zhaoo');
var name = zhaoo.getName();
zhaoo.setAge(20);
var age = zhaoo.getAge();

自触发函数 (IIFE)

再来看看自触发函数。

js的全局变量其实是定义在一个window对象下的,例如var a = 1;其实是var window.a = 1;。然而,window对象又有许多的内置属性,如果我们自定义的属性刚好和内置属性重名的话,就会起冲突。

所以,这里用到匿名函数,在函数定义后立即触发,而不产生函数名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Type 1, 没有匿名,但是立即触发
start();
function start() {
//...
}

//Tpye 2
(function () {
//...
})();

//Type 3
!function () {
//...
}();

Tpye 4
;(function() {
//...
})();

总结

结合闭包立即执行函数,我们就可以很好的封装一个模板了。闭包用来限定作用域(命名空间),立即执行函数避免全局变量名字冲突。

例如,jQuery就是用于以上两种机制,暴露出了一个$,来操作内部方法。

此外,ES6中的letconst可以产生一个块级作用域,其实也是用到了该原理。用babel编译后是如下ES5代码,是不是一目了然。

1
2
3
4
5
// ES6
{
let a = "abc";
};
console.log(a); // Error, a is not defind
1
2
3
4
5
// ES5
(function(){
var a = "abc";
});
console.log(a); // Error, a is not defind
查看评论