盒子
目录

闭包

文章取自我的 Github repos: Learning-JavaScript, 作者:@paddingme
原文链接:https://github.com/paddingme/Learning-JavaScript/issues/29

闭包的两个特点:

  1. 闭包作为与函数成对的数据,在函数执行过程中处于激活(即可访问)状态;
  2. 闭包在函数运行结束后,保持运行过程的最终数据状态。

总的来说,函数闭包决定了:闭包所对应的函数代码如何访问数据,以及闭包内的数据何时销毁。

1
2
3
4
5
6
7
8
// 没有函数实例产生
function myFunc(){
}

var f1 = myFunc;
var f2 = myFunc;

alert(f1 === f2)
1
2
3
4
5
6
7
8
9
10
11
12

function MyObject(){
}

MyObject.prototype.method = function(){}

var obj1 = new MyObject();
var obj2 = new MyObject();

alert( obj1.method === obj2.method)

// 对象的实例只持有原型中的方法的一个引用,因为也不产生(方法)函数的实例。
1
2
3
4
5
6
7
8
function MyObject(){
this.method = function(){}
}

var obj1 = new MyObject;
var obj2 = new MyObject;
alert( obj1.method === obj2.method)
//false
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
27
28
29
30
31
32
33
34
35
36
37
//构造器函数

function MyObject(){
var instance_data = 100;
this.getInstanceData = function(){
return instance_data;
}

this.setInstanceData = function(v) {
instance_data = v;
}
}

// 使用一个额匿名函数去修改构造器的原型 MyObject.prototype,以访问该匿名函数中的 upvalue

void function(){
var class_data = 5;

this.getClassData = function(){
return class_data;
}

this.setClassData = function(v){
class_data = v;
}
}.call(MyObject.prototype);

var obj1 = new MyObject();
var obj2 = new MyObject();

// obj1 与 obj2 的 getInstance 是不同函数实例,因此访问的是不同闭包的 upvalue
obj1.setInstanceData(10);
console.log(obj2.getInstanceData());

// obj1 与 obj2 的 getClassData 是同一个函数实例,因此在访问相同 的 upvalue.
obj1.setClassData(20);
console.log(obj2.getClassData());
1
2
3
4
5
6
7
8
9
10

function aFunc(){
function MyFunc(){}
return myFunc;
}

var f1 = new aFunc();
var f2 = new aFunc();
console.log(f1===f2)
//FALSE
1
2
3
4
5
6
7
8
9
10
11
//foo & bar 产生函数实例
function foo(){
var MyFunc = function(){}
return MyFunc;
}

function bar(){
return function(){

};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//返回同一个实例

var aFun3 = function(){
var foo = function(){
console.log(111)
};

return function(){
return foo;
}
}()

var f3 = aFun3();
var f4 = aFun3();
console.log(f3 === f4)

调用对象:

  1. 对象属性与变量没有本质属性;
  2. 全局变量其实是“全局对象”的属性;
  3. 局部变量其实是“调用对象”的属性

“调用对象”的局部变量维护规则

  1. 在函数开始执行时,varDecls 中所有值将被置为 undefined。 因此我们无论如何访问函数,变量初始值总为 undefined。
  2. 函数执行结束并退出时,varDecls 不会被重置,即有了函数能够提供“在函数内保存数据”。
  3. 函数内部数据持续的生存周期,取决于该函数实例是否存在活动引用——如果没有,则调用对象被销毁。

“全局对象”的变量维护规则

  1. 由于该函数从来不被再次进入,因此不会被重新初始化;
  2. 由于该函数仅有一个个被系统持有的实例,因此他自身和内部数据总不被销毁。

函数闭包 与 “调用对象”的生存周期

在运行期改函数实例有一个函数闭包,在执行时,引擎会:

  • 创建一个函数实例;
  • 为该函数实例创建一个闭包;
  • 为改函数实例(及其闭包)的运行环境从 ScriptObject(调用对象) 复制一个调用对象。