JavaScript.The.Good.Parts閱讀筆記(二)範圍閉包減緩全域空間汙染

來源:互聯網
上載者:User

如代碼塊 複製代碼 代碼如下:if (true) {
int i = 100;
}
print(i); //錯誤,變數i沒有聲明

如上面例子所示,代碼塊外的函數是無法訪問i變數的。
但在javaScript裡,情況則完全不同。 複製代碼 代碼如下:if (true) {
var i = 100;
}
alert(i); //彈出框並顯示100

很多現代語言都推薦儘可能遲地聲明變數,但在Javascript裡這是一個最糟糕的建議。由於缺少塊級範圍,最好在函數體的頂部聲明函數中可能用到的所有變數。

閉包特性:
雖然缺少塊級範圍,但是函數的範圍還是存在的。
這種範圍有一個好處,就是內建函式可以訪問定義它們的外部函數的參數和變數(除了this和argument)。
利用這種特性,則可以這樣來設計代碼。 複製代碼 代碼如下:

var bankAccount = function () {
var value = 0;
return {
deposit: function (inc) {
value += inc;
},
getValue: function (){
return value;
}
}
}

var myAccount = bankAccount(); //新開一個銀行賬戶
myAccount.deposit(1000); //存1000塊進去
alert(myAccount.getValue()); //should alert(1000);

value由於在bankAccount這個function裡,外部無法對它進行直接操作,必須通過bankAccount function給他返回的對象來進行操作,通過這樣來實現C#和java裡的private的欄位。

減緩全域變數汙染全域空間:利用函數的範圍,我們在寫js庫的時候可以減少跟其他庫衝突。 複製代碼 代碼如下:(function () {
var hello = 'Hello World.';
})();
alert(hello); //error: hello no exist.

這裡的文法很有點詭異,主要思想是定義一個匿名方法,並且馬上執行。由於function開頭這個litertal會被解釋作為函數定義,這裡加上了一對括弧包住它,然後再用一對括弧表示調用此函數。外部的alert訪問不到在函數內部定義的hello。

陷阱一:var的陷阱

“減緩全域變數汙染全域空間”的例子改成 複製代碼 代碼如下:(function () {
hello = 'Hello World.'; //remove var
})();
alert(hello); //alert ('Hello World.');

當變數hello沒有用var顯式聲明時,hello成為了一個全域變數!!

雖然利用這個特性,咱們可以提供一個對外介面,但不建議這樣做。 複製代碼 代碼如下:(function () {
var hello = 'Hello World.';
sayHello = function () { //不建議採用這種方式來提供介面,看起來很不明顯。
alert(hello);
}
})();
sayHello();

可以改進為 複製代碼 代碼如下:(function (window) {
var hello = 'Hello World.';
window.$ = {
sayHello: function () {
alert(hello);
}
};
})(window);
$.sayHello(); //看起來像jQuery那麼酷

複製代碼 代碼如下:var obj = (function () {
var hello = 'Hello World.';
return {
sayHello: function () {
alert(hello);
}
};
})();
obj.sayHello();

陷阱二: 閉包的陷阱

複製代碼 代碼如下:(function () { //函數a
var arr = [];
  var i = 0;
var j;
for ( ; i < 3; i++) {
arr.push(function () { //函數b
alert(i * 10);
});
}

for (j in arr) {
arr[j]();
}
})();

原以為函數數組arr裡各個函數執行後,會彈出0,10,20,但是結果不是如此。結果是彈出30,30,30。
函數b訪問的不是當時的 i的值, 而是直接存取變數i(用雩都是取i最新的值)。
原因是函數b是函數a的內建函式,變數i對函數b是可見的,函數b每次都從i處擷取最新的值。

這次改成: 複製代碼 代碼如下:(function () { //函數a
var arr = [];
var i = 0;
  var j;
for ( ; i < 3; i++) {
arr.push((function (anotherI) { //函數m
return function () { //函數b
alert(anotherI * 10);
}
})(i)); // 此處為(function b(anotherI) {})(i)
}

for (j in arr) {
arr[j]();
}
})();

這次執行後,終於彈出0,10,20。這是為什麼呢。

函數b訪問的是anotherI(當時的i的值),而不是直接存取變數i。
每次在arr.push前,都會定義一個新匿名的函數m。本例中定義了3個匿名函數m0,m1,m2,每當被調用後,他們的anotherI都得到當前i的值。每個m函數執行後都返回一個b函數。b0在m0裡,b1在m1裡,b2在m2裡。b0隻能訪問m0的anotherI(為0),而b0訪問不了m1的anotherI,因為m0和m1為不同的函數。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.