定义
一个函数和对其周围状态(词法环境)的引用捆绑在一起,这样的组合就是闭包。也就是说,闭包让你可以再一个内层函数中访问到其外层函数的作用域。在js中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
词法作用域
function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
displayName();
}
init();
闭包
例一:
function makeFunc(){
var name = 'hahaha';
function displayName(){
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
思考: makeFunc()执行完毕,代码仍然按照预期运行,myFunc
函数相当于displayName
函数绑定着var name = 'hahaha'
被返回出来了。
例二:
function add(x){
return function(y){
return x + y;
}
}
var add5 = add(5);
var add10 = add(10);
// (function(1){
// return 5 + 1
// })()
console.log(add5(1)); //6 = 5 + 1
// (function(2){
// return 10 + 2
// })()
console.log(add10(2)); // 12 = 10 + 2
例三:闭包模拟私有方法
var counter = (function(){
var private = 0;
function change(val){
private += val;
}
return {
increment:function(){
change(1);
},
decrement:function(){
change(-1);
},
value:function(){
return private;
}
}
})();
console.log(counter.value()); //0
counter.increment();
console.log(counter.value()); //1
counter.decrement();
console.log(counter.value()); //0
思考:每个闭包都有它自己的词法环境,例子中我们只创建了一个词法环境,为三个函数共享:counter.increment
, counter.decrement
, counter.value
,该环境创建于一个立即执行的函数内部,有一个私有变量private
,一个私有函数change
, 他们不能在函数外面访问,只能通过 return出去的三个函数访问,这个三个return出去的函数是共享同一个环境的闭包。
例三的扩展:
var counter = function(){
var private = 0;
function change(val){
private += val;
}
return {
increment:function(){
change(1);
},
decrement:function(){
change(-1);
},
value:function(){
return private;
}
}
};
var counter1 = counter();
var counter2 = counter();
console.log(counter1.value()); //0
counter1.increment();
counter2.increment();
console.log(counter1.value()); //1
counter1.decrement();
// 说明了两个闭包的环境相互独立,各自维护自己的词法作用域
console.log(counter1.value()); //0
console.log(counter2.value()); //1
例四:使用闭包解决for循环问题
for (var i = 0; i < 5; i++) {
// 每个setTimeout里面的回调函数都有共同的环境变量i
setTimeout(() => {
console.log(i)
},0)
}
// 5,5,5,5,5,5
思考:因为var没有块级作用域,使用setimeout后,宏任务执行console.log的时候,i在主线程结束时候已经变成了5。
解决方法
// 使用闭包的方法
for (var i = 0; i < 5; i++) {
let time = function(x){
// 每个setTimeout里面的回调函数都有自己的环境变量x,维护各自的环境变量
setTimeout(() => {
console.log(x)
},0)
};
time(i);
}
//使用块级作用域
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(x)
},0)
}
// 1,2,3,4,5
例五 理解迭代器原理
function iterator (items) {
let i = 0;
return function () {
return items[i++]
}
}
var next = iterator( [1, 2, 3, 4] );
//以下的next都共用一个词法环境,共用一个变量i
console.log(next()); // 1
console.log(next()); // 2
console.log(next()); // 3
console.log(next()); // 4
console.log(next()); // undefined