javascript闭包介绍

概念

闭包(closure)是一个拥有任意变量以及绑定这些变量的环境(environment)的表达式(一般来说是就是function)

A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).

在作用域内且在function定义时被访问的变量,那么这个变量就一直能够被那个function访问。

variables that are in scope and accessed from a function declaration will stay accessible by that function.

下面这个例子就是闭包,displayName函数能够访问到不在其代码块里的name变量。

function init() {
var name = "Mozilla";
function displayName() {
alert(name);
}
displayName();
}
init();

函数的作用域 functional scoping

一个变量的作用域是以其所在的源代码的位置来定义的,嵌套在里面的function可以访问到声明在外层作用域的变量

The scope of a variable is defined by its location within the source code, and nested functions have access to variables declared in their outer scope.

还是拿刚才那个例子来说,displayName函数是嵌套在init函数里的,所以它能够访问到init函数里的变量

function init() {
var name = "Mozilla";
function displayName() {
alert(name);
}
displayName();
}
init();

闭包的组成

先看一下这个例子:

function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}

var myFunc = makeFunc();
myFunc();
按照java或C++的经验,局部变量name的生命周期在函数的执行后就结束了,所以会推断name在makeFunc()访问后应该就访问不到了。
然而事实恰恰相反,唯一的解释就是myFunc是一个闭包(closure)。

闭包由两部分组成:

  1. function

  2. 创建该function的环境(创建闭包时,作用域内的所有局部变量)

对应到上面的这个例子里:

  1. function: displayName

  2. 环境:name="Mozilla"

再看一个例子:

function makeAdder(x) {
return function(y) {
return x + y;
};
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

alert(add5(2)); // 7
alert(add10(2)); // 12
这个例子说明闭包的function可以是相同的,但是环境可以是不同的,因此就会有不同的结果。

归纳

因此可以将闭包归纳为:

  1. 定义时,确定可访问变量

  2. 执行时,确定变量的值

常见错误

下面这段代码实际上执行的时候并不是alert 0,1,2,3,4,而是alert 5次5。
这是为什么?因为i变量在for循环后变成了5,而在执行的时候我们才会确定闭包里i的值,在定义的时候不会记住i的值是什么的。

var funcs = [];
for(var i=0; i
正确的写法是:

var funcs = [];
function makeFunc(x) {
return function() { alert(x); }
}
for(var i=0; i

闭包实践

函数工厂

function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

私有变量

var Counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
})();

alert(Counter.value()); / Alerts 0 /
Counter.increment();
Counter.increment();
alert(Counter.value()); / Alerts 2 /
Counter.decrement();
alert(Counter.value()); / Alerts 1 /
在这个例子里:

  1. 外界不能访问: privateCounter,changeBy

  2. 外界间接访问: increment,decrement,value

私有变量+函数工厂

var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();
alert(Counter1.value()); / Alerts 0 /
Counter1.increment();
Counter1.increment();
alert(Counter1.value()); / Alerts 2 /
Counter1.decrement();
alert(Counter1.value()); / Alerts 1 /
alert(Counter2.value()); / Alerts 0 /
Counter1和Counter2绑定的环境相互独立。

性能问题

function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
this.getName = function() {
return this.name;
};

this.getMessage = function() {
return this.message;
};
}
执行一次,就会重新构造两个函数。

正确的做法应该是:

function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype = {
getName: function() {
return this.name;
},
getMessage: function() {
return this.message;
}
};

function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype.getName = function() {
return this.name;
};
MyObject.prototype.getMessage = function() {
return this.message;
};

参考资料

  1. Closures - MDN

  2. Explaning Javascript Scope and closures

  3. 深入理解JavaScript闭包(closure)

  4. 学习Javascript闭包(Closure) - 阮一峰

关键字:JavaScript

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部