閉包(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; };