JavaScript.The.Good.Parts閱讀筆記(二)作用域&閉包&減緩全局空間污染
更新時(shí)間:2010年11月16日 22:44:22 作者:
塊級(jí)作用域: 大多數(shù)使用c語(yǔ)言語(yǔ)法的語(yǔ)言都有塊級(jí)作用域,而JavaScript沒(méi)有塊級(jí)作用域。
如代碼塊
if (true) {
int i = 100;
}
print(i); //錯(cuò)誤,變量i沒(méi)有聲明
如上面例子所示,代碼塊外的函數(shù)是無(wú)法訪(fǎng)問(wèn)i變量的。
但在javaScript里,情況則完全不同。
if (true) {
var i = 100;
}
alert(i); //彈出框并顯示100
很多現(xiàn)代語(yǔ)言都推薦盡可能遲地聲明變量,但在Javascript里這是一個(gè)最糟糕的建議。由于缺少塊級(jí)作用域,最好在函數(shù)體的頂部聲明函數(shù)中可能用到的所有變量。
閉包特性:
雖然缺少塊級(jí)作用域,但是函數(shù)的作用域還是存在的。
這種作用域有一個(gè)好處,就是內(nèi)部函數(shù)可以訪(fǎng)問(wèn)定義它們的外部函數(shù)的參數(shù)和變量(除了this和argument)。
利用這種特性,則可以這樣來(lái)設(shè)計(jì)代碼。
var bankAccount = function () {
var value = 0;
return {
deposit: function (inc) {
value += inc;
},
getValue: function (){
return value;
}
}
}
var myAccount = bankAccount(); //新開(kāi)一個(gè)銀行賬戶(hù)
myAccount.deposit(1000); //存1000塊進(jìn)去
alert(myAccount.getValue()); //should alert(1000);
value由于在bankAccount這個(gè)function里,外部無(wú)法對(duì)它進(jìn)行直接操作,必須通過(guò)bankAccount function給他返回的對(duì)象來(lái)進(jìn)行操作,通過(guò)這樣來(lái)實(shí)現(xiàn)C#和java里的private的字段。
減緩全局變量污染全局空間:利用函數(shù)的作用域,我們?cè)趯?xiě)js庫(kù)的時(shí)候可以減少跟其他庫(kù)沖突。
(function () {
var hello = 'Hello World.';
})();
alert(hello); //error: hello no exist.
這里的語(yǔ)法很有點(diǎn)詭異,主要思想是定義一個(gè)匿名方法,并且馬上執(zhí)行。由于function開(kāi)頭這個(gè)litertal會(huì)被解釋作為函數(shù)定義,這里加上了一對(duì)括號(hào)包住它,然后再用一對(duì)括號(hào)表示調(diào)用此函數(shù)。外部的alert訪(fǎng)問(wèn)不到在函數(shù)內(nèi)部定義的hello。
陷阱一:var的陷阱
“減緩全局變量污染全局空間”的例子改成
(function () {
hello = 'Hello World.'; //remove var
})();
alert(hello); //alert ('Hello World.');
當(dāng)變量hello沒(méi)有用var顯式聲明時(shí),hello成為了一個(gè)全局變量!!
雖然利用這個(gè)特性,咱們可以提供一個(gè)對(duì)外接口,但不建議這樣做。
(function () {
var hello = 'Hello World.';
sayHello = function () { //不建議采用這種方式來(lái)提供接口,看起來(lái)很不明顯。
alert(hello);
}
})();
sayHello();
可以改進(jìn)為
(function (window) {
var hello = 'Hello World.';
window.$ = {
sayHello: function () {
alert(hello);
}
};
})(window);
$.sayHello(); //看起來(lái)像jQuery那么酷
或
var obj = (function () {
var hello = 'Hello World.';
return {
sayHello: function () {
alert(hello);
}
};
})();
obj.sayHello();
陷阱二: 閉包的陷阱
(function () { //函數(shù)a
var arr = [];
var i = 0;
var j;
for ( ; i < 3; i++) {
arr.push(function () { //函數(shù)b
alert(i * 10);
});
}
for (j in arr) {
arr[j]();
}
})();
原以為函數(shù)數(shù)組arr里各個(gè)函數(shù)執(zhí)行后,會(huì)彈出0,10,20,但是結(jié)果不是如此。結(jié)果是彈出30,30,30。
函數(shù)b訪(fǎng)問(wèn)的不是當(dāng)時(shí)的 i的值, 而是直接訪(fǎng)問(wèn)變量i(用于都是取i最新的值)。
原因是函數(shù)b是函數(shù)a的內(nèi)部函數(shù),變量i對(duì)函數(shù)b是可見(jiàn)的,函數(shù)b每次都從i處獲取最新的值。
這次改成:
(function () { //函數(shù)a
var arr = [];
var i = 0;
var j;
for ( ; i < 3; i++) {
arr.push((function (anotherI) { //函數(shù)m
return function () { //函數(shù)b
alert(anotherI * 10);
}
})(i)); // 此處為(function b(anotherI) {})(i)
}
for (j in arr) {
arr[j]();
}
})();
這次執(zhí)行后,終于彈出0,10,20。這是為什么呢。
函數(shù)b訪(fǎng)問(wèn)的是anotherI(當(dāng)時(shí)的i的值),而不是直接訪(fǎng)問(wèn)變量i。
每次在arr.push前,都會(huì)定義一個(gè)新匿名的函數(shù)m。本例中定義了3個(gè)匿名函數(shù)m0,m1,m2,每當(dāng)被調(diào)用后,他們的anotherI都得到當(dāng)前i的值。每個(gè)m函數(shù)執(zhí)行后都返回一個(gè)b函數(shù)。b0在m0里,b1在m1里,b2在m2里。b0只能訪(fǎng)問(wèn)m0的anotherI(為0),而b0訪(fǎng)問(wèn)不了m1的anotherI,因?yàn)閙0和m1為不同的函數(shù)。
復(fù)制代碼 代碼如下:
if (true) {
int i = 100;
}
print(i); //錯(cuò)誤,變量i沒(méi)有聲明
如上面例子所示,代碼塊外的函數(shù)是無(wú)法訪(fǎng)問(wèn)i變量的。
但在javaScript里,情況則完全不同。
復(fù)制代碼 代碼如下:
if (true) {
var i = 100;
}
alert(i); //彈出框并顯示100
很多現(xiàn)代語(yǔ)言都推薦盡可能遲地聲明變量,但在Javascript里這是一個(gè)最糟糕的建議。由于缺少塊級(jí)作用域,最好在函數(shù)體的頂部聲明函數(shù)中可能用到的所有變量。
閉包特性:
雖然缺少塊級(jí)作用域,但是函數(shù)的作用域還是存在的。
這種作用域有一個(gè)好處,就是內(nèi)部函數(shù)可以訪(fǎng)問(wèn)定義它們的外部函數(shù)的參數(shù)和變量(除了this和argument)。
利用這種特性,則可以這樣來(lái)設(shè)計(jì)代碼。
復(fù)制代碼 代碼如下:
var bankAccount = function () {
var value = 0;
return {
deposit: function (inc) {
value += inc;
},
getValue: function (){
return value;
}
}
}
var myAccount = bankAccount(); //新開(kāi)一個(gè)銀行賬戶(hù)
myAccount.deposit(1000); //存1000塊進(jìn)去
alert(myAccount.getValue()); //should alert(1000);
value由于在bankAccount這個(gè)function里,外部無(wú)法對(duì)它進(jìn)行直接操作,必須通過(guò)bankAccount function給他返回的對(duì)象來(lái)進(jìn)行操作,通過(guò)這樣來(lái)實(shí)現(xiàn)C#和java里的private的字段。
減緩全局變量污染全局空間:利用函數(shù)的作用域,我們?cè)趯?xiě)js庫(kù)的時(shí)候可以減少跟其他庫(kù)沖突。
復(fù)制代碼 代碼如下:
(function () {
var hello = 'Hello World.';
})();
alert(hello); //error: hello no exist.
這里的語(yǔ)法很有點(diǎn)詭異,主要思想是定義一個(gè)匿名方法,并且馬上執(zhí)行。由于function開(kāi)頭這個(gè)litertal會(huì)被解釋作為函數(shù)定義,這里加上了一對(duì)括號(hào)包住它,然后再用一對(duì)括號(hào)表示調(diào)用此函數(shù)。外部的alert訪(fǎng)問(wèn)不到在函數(shù)內(nèi)部定義的hello。
陷阱一:var的陷阱
“減緩全局變量污染全局空間”的例子改成
復(fù)制代碼 代碼如下:
(function () {
hello = 'Hello World.'; //remove var
})();
alert(hello); //alert ('Hello World.');
當(dāng)變量hello沒(méi)有用var顯式聲明時(shí),hello成為了一個(gè)全局變量!!
雖然利用這個(gè)特性,咱們可以提供一個(gè)對(duì)外接口,但不建議這樣做。
復(fù)制代碼 代碼如下:
(function () {
var hello = 'Hello World.';
sayHello = function () { //不建議采用這種方式來(lái)提供接口,看起來(lái)很不明顯。
alert(hello);
}
})();
sayHello();
可以改進(jìn)為
復(fù)制代碼 代碼如下:
(function (window) {
var hello = 'Hello World.';
window.$ = {
sayHello: function () {
alert(hello);
}
};
})(window);
$.sayHello(); //看起來(lái)像jQuery那么酷
或
復(fù)制代碼 代碼如下:
var obj = (function () {
var hello = 'Hello World.';
return {
sayHello: function () {
alert(hello);
}
};
})();
obj.sayHello();
陷阱二: 閉包的陷阱
復(fù)制代碼 代碼如下:
(function () { //函數(shù)a
var arr = [];
var i = 0;
var j;
for ( ; i < 3; i++) {
arr.push(function () { //函數(shù)b
alert(i * 10);
});
}
for (j in arr) {
arr[j]();
}
})();
原以為函數(shù)數(shù)組arr里各個(gè)函數(shù)執(zhí)行后,會(huì)彈出0,10,20,但是結(jié)果不是如此。結(jié)果是彈出30,30,30。
函數(shù)b訪(fǎng)問(wèn)的不是當(dāng)時(shí)的 i的值, 而是直接訪(fǎng)問(wèn)變量i(用于都是取i最新的值)。
原因是函數(shù)b是函數(shù)a的內(nèi)部函數(shù),變量i對(duì)函數(shù)b是可見(jiàn)的,函數(shù)b每次都從i處獲取最新的值。
這次改成:
復(fù)制代碼 代碼如下:
(function () { //函數(shù)a
var arr = [];
var i = 0;
var j;
for ( ; i < 3; i++) {
arr.push((function (anotherI) { //函數(shù)m
return function () { //函數(shù)b
alert(anotherI * 10);
}
})(i)); // 此處為(function b(anotherI) {})(i)
}
for (j in arr) {
arr[j]();
}
})();
這次執(zhí)行后,終于彈出0,10,20。這是為什么呢。
函數(shù)b訪(fǎng)問(wèn)的是anotherI(當(dāng)時(shí)的i的值),而不是直接訪(fǎng)問(wèn)變量i。
每次在arr.push前,都會(huì)定義一個(gè)新匿名的函數(shù)m。本例中定義了3個(gè)匿名函數(shù)m0,m1,m2,每當(dāng)被調(diào)用后,他們的anotherI都得到當(dāng)前i的值。每個(gè)m函數(shù)執(zhí)行后都返回一個(gè)b函數(shù)。b0在m0里,b1在m1里,b2在m2里。b0只能訪(fǎng)問(wèn)m0的anotherI(為0),而b0訪(fǎng)問(wèn)不了m1的anotherI,因?yàn)閙0和m1為不同的函數(shù)。
相關(guān)文章
詳解webpack和webpack-simple中如何引入css文件
這篇文章主要介紹了詳解webpack和webpack-simple中如何引入css文件,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-06-06使用flow來(lái)規(guī)范javascript的變量類(lèi)型
這篇文章主要介紹了使用flow來(lái)規(guī)范javascript的變量類(lèi)型,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09js 第二代身份證號(hào)碼的驗(yàn)證機(jī)制代碼
在盛大某網(wǎng)站注冊(cè)的時(shí)候,身份證必填,但我又不想填真實(shí)身份證號(hào)碼,于是隨便編了串自認(rèn)為合法的身份證號(hào)碼,但是卻馬上被提示號(hào)碼錯(cuò)誤2011-05-05document.documentElement && document.documentElement
document.documentElement && document.documentElement.scrollTop...2007-12-12OfflineSave離線(xiàn)保存代碼再次發(fā)布使用說(shuō)明
OfflineSave離線(xiàn)保存代碼再次發(fā)布使用說(shuō)明...2007-05-05jsonp的簡(jiǎn)單介紹以及其安全風(fēng)險(xiǎn)
JSONP原理就是動(dòng)態(tài)插入帶有跨域url的script標(biāo)簽,然后調(diào)用回調(diào)函數(shù),把我們需要的json數(shù)據(jù)作為參數(shù)傳入,通過(guò)一些邏輯把數(shù)據(jù)顯示在頁(yè)面上,這篇文章主要給大家介紹了關(guān)于jsonp的簡(jiǎn)單介紹以及其安全風(fēng)險(xiǎn)的相關(guān)資料,需要的朋友可以參考下2022-01-01javascript實(shí)現(xiàn)的使用方向鍵控制光標(biāo)在table單元格中切換
最近公司開(kāi)發(fā)ERP項(xiàng)目,要求商品入庫(kù)選擇貨架號(hào)時(shí)支持使用方向鍵快速選擇,以提高入庫(kù)效率。2010-11-11