闭包是JavaScript中的一大重点,也是难点(敲黑板),

闭包简单的来说:定义在函数内部的函数。
内部函数作为返回值。
闭包是由函数以及创建该函数的词法环境组合而成,这个环境包含了这个闭包创建时所能访问的所有局部变量,所以是能够读取其他函数内部变量的函数。

特点:能够读取函数内部的变量,变量始终保存在内存中。

接下来从上面三句话理解闭包:

1.闭包是一个“函数”,是定义在函数内部的一个函数,并作为返回值输出,示例中fn即为闭包。

1
2
3
4
5
6
7
8
9
10
function wrapFn(){
var name = 'jode';
function insertFn(){
console.log(name);
}
return insertFn;
}

var fn = wrapFn();
fn(); // 'jode'

2.闭包是由函数以及创建该函数的词法环境组合而成

1
2
3
4
5
6
7
8
9
10
11
function makeAdd(x){
return function(y){
return x + y;
}
}
var fn1 = makeAdd(2);
var fn2 = makeAdd(42);
var res1 = fn(6);
var res2 = fn(26);
console.log(res1); // 8
console.log(res1); // 68

这里我们定义了makeAdder(x)函数,它接受一个参数x,并返回一个新的函数。返回的函数接受一个参数y,并返回x+y的值。

从本质上讲,makeAdder是一个函数工厂 — 他创建了将指定的值和它的参数相加求和的函数。在上面的示例中,我们使用函数工厂创建了两个新函数 — 一个将其参数和6求和,另一个和26求和。

fn1fn2都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在fn1的环境中,x为5。而在fn2中,x则为10。

注意:返回函数不要引用任何循环变量或者后续会发生改变的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function count(){
var arr = [];
for(var i = 0; i < 3; i++){
arr.push(function(){
return i * i;
})
}
return arr;
}

var fn = count();
var f1 = fn[0];
var f2 = fn[1];
var f3 = fn[2];
f1(); // 9
f2(); // 9
f3(); // 9

上面示例中,每次循环都追加一个函数到arr数组中,并返回数组,可结果并不是我们认为的0, 1, 4

结果都是9,原因在于返回的函数引用了循环变量i,我们输出fn的其中一个项可以看到和push方法内部的函数一样,当f1保存count()返回的函数时,函数并没有执行,而是在f1()函数调用时
执行i * i的计算,而此时for循环完成i的值为3。