JavaScript的闭包有两个用途:一个是访问函数内部的变量;另一个是让变量的值在作用域内保持不变。函数是JavaScript 中唯一有作用域的对象,因此JavaScript的闭包依赖于函数实现,下面结合两则示例对JavaScript的闭包做简单说明。
1. 访问函数内部的变量
JavaScript中没有严格意义上的类,所以我们常用函数来模拟类。我们可以像下面这样模拟一个Counter类,并在其中定义一个count私有变量:
function Counter() {
var count = 2;
return {
increment: function() {
count++;
},
get: function() {
return count;
}
}
}
var foo = new Counter();
foo.increment();
foo.get(); // -> 3
在JavaScript中,函数可以访问其外部定义的变量,而外部不能访问函数内部定义的变量。要访问函数内部定义的变量,需要在其内部分定义一个闭包函数,通过调用闭包函数达到访问函数内部变量的目的。
在上面示例中,count相当于一个“私有变量”,在Counter函数外部不能访问这个变量。在Counter中,还定义了increment 和 get两个“闭包函数”,这两个函数都保持着对Counter作用域的引用,因此可以访问到Counter作用域内定义的变量count。
2. 保持变量的作用域
ECMASceript 2015(ES6)之前,JavaScript中没有块级变量(ES6中可以使用let定义块级变量),使用var定义的变量都是全局或函数级变量。
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
在上面的代码中,我们期望的依次输出数字0到9,而实际是输出是十次10。这是因为JavaScript只有函数级作用域,没有块级作用域。要解决这个问题,就要为变量构建一个作用域。解决方案如下:
方案一:在循环中使用闭包
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
方案二:匿名函数中返回一个函数
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
以上两种方案都是通过闭包(匿名函数)实现的,外部函数执行后i作为参数传入,此时函数内e变量就拥有了i的一个拷贝。变量传入匿名函数后相当于有了一个闭包的作用域,然后就得到了我们想要的结果。
