闭包(closure)是一个拥有任意变量以及绑定这些变量的环境(environment)的表达式(通常来讲是就是function)javascript
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).html
在做用域内且在function定义时被访问的变量,那么这个变量就一直可以被那个function访问。java
variables that are in scope and accessed from a function declaration will stay accessible by that function.express
下面这个例子就是闭包,displayName
函数可以访问到不在其代码块里的name变量。闭包
function init() { var name = "Mozilla"; function displayName() { alert(name); } displayName(); } init();
一个变量的做用域是以其所在的源代码的位置来定义的,嵌套在里面的function能够访问到声明在外层做用域的变量ide
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)。ui
闭包由两部分组成:
function
建立该function的环境(建立闭包时,做用域内的全部局部变量)
对应到上面的这个例子里:
function: displayName
环境: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能够是相同的,可是环境能够是不一样的,所以就会有不一样的结果。
所以能够将闭包概括为:
定义时,肯定可访问变量
执行时,肯定变量的值
下面这段代码实际上执行的时候并非alert 0,1,2,3,4,而是alert 5次5。
这是为何?由于i变量在for循环后变成了5,而在执行的时候咱们才会肯定闭包里i的值,在定义的时候不会记住i的值是什么的。
var funcs = []; for(var i=0; i < 5; i++) { funcs[i] = function() { alert(i); } } for(var j=0; j < funcs.length; j++) { funcs[j](); }
正确的写法是:
var funcs = []; function makeFunc(x) { return function() { alert(x); } } for(var i=0; i < 5; i++) { funcs[i] = makeFunc(i) } for(var j=0; j < funcs.length; j++) { funcs[j](); }
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 */
在这个例子里:
外界不能访问: privateCounter,changeBy
外界间接访问: 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; };