关于闭包

词法作用域(lexical environment)

作用域链

  • 函数在执行的过程中,先从自己内部找变量
  • 如果找不到,再从创造当前函数所在的作用域(词法作用域)去找,以此往上
  • 注意找的是变量的当前状态

函数连同它作用域链上的要找的这个变量,共同构成闭包
一般情况下使用闭包住要是为了

  1. 封装数据
  2. 暂存数据

例子

1
2
3
4
5
6
7
8
9
10
var fnArr = [];
for (var i = 0; i < 2; i++){
fnArr[i] = (function(j){
return function(){
return j
}
})(i)
}

fnArr[1]() // 1

这个例子里有两个闭包,
相当于可以将for循环拆开,等效于以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fnArr[0] = (function(j){
return function(){
return j;
}
})(0)

fnArr[1] = (function(j){
return function(){
return j;
}
})(1)


fnArr[1]();

再简化一下,写成a,b 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = (function(j){
return function(){
return j;
}
})(0)

var b = (function(j){
return function(){
return j;
}
})(1)

b();

再同步一下,将var b里的立即执行函数单独拆出来,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = (function(j){
return function(){
return j;
}
})(0)

function fn2(j){
var j = arguments[0];
return function(){
return j;
}
}

var b = fn2(1);

b();

其他的等效方法

方法1:

1
2
3
4
5
6
7
var fnArr = [];

for(var i = 0; i < 2; i++){
fnArr[i] = (function(j){
return j;
})(i)
}

方法2:

1
2
3
4
5
6
7
8
var fnArr = [];
for(var i = 0;i < 2; i++){
(function(i){
fnArr[i] = function(){
return i;
}
})(i)
}

方法3:(使用ES6)

1
2
3
4
5
6
var fnArr = [];
for(let i = 0; i < 10; i++){
fnArr[i] = function(){
return i;
}
}

闭包例题

封装一个Car对象

可以将其封装成一个对象返回,由于形成闭包,当调用方法时,不会对原有的参数speed进行影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var Car = (function(){
var speed = 0;

function setSpeed (s) {
speed = s;
}

function getSpeed (){
return speed
}

function speedUp(){
speed++
}

function speedDown(){
speed --;
}

return {
setSpeed: setSpeed,
getSpeed: getSpeed,
speedUp: speedUp,
speedDown: speedDown
}
})()