版權(quán)聲明
http://monw3c.blogbus.com/logs/31056019.html
這本來是翻譯Estelle Weyl的《15 JavaScript Gotchas》
變量名和函數(shù)名都是區(qū)分大小寫的。就像配錯的引號一樣,這些大家都知道。但是,由于錯誤是不作聲的,所以這是一個提醒。為自己選擇一個命名規(guī)則,并堅持它。而且,記住JavaScript中的原生函數(shù)和CSS屬性都是駱駝拼寫法(camelCase)
getElementById(’myId’) != getElementByID(’myId’); //它應(yīng)該是“Id”而不是“ID”getElementById(’myId‘) != getElementById(’myID‘); // “Id”也不等于“ID”document.getElementById('myId').style.Color; //返回 "undefined"
避免陷入不匹配的引號、圓括號或花括號陷阱的最好方式是編碼時一直同時寫出打開和關(guān)閉這兩個元素符號,然后在其中間加入代碼。開始:
var myString = ""; //在輸入字符串值之前寫入這對引號function myFunction(){if(){//關(guān)閉每個打開的括弧}}//統(tǒng)計所有的左括號和右括號數(shù)量,并且確保它們相等alert(parseInt(var1)*(parseInt(var2)+parseInt(var3))); //關(guān)閉每個打開的圓括號
每當你打開一個元素,請關(guān)閉它。當你添加了關(guān)閉圓括號后,你再把函數(shù)的參數(shù)放進圓括號中。如果有一串圓括號,統(tǒng)計所有打開的圓括號和所有關(guān)閉的圓括號,并且確保這兩個數(shù)字相等。
if(var1 == var2){//statement}
if(var1 = var2){} // 返回true。把var2賦值給var1
var myVar = 5; if(myVar == '5'){ //返回true,因為JavaScript是弱類型 alert("hi"); //這個alert將執(zhí)行,因為JavaScript通常不在意數(shù)據(jù)類型 } switch(myVar){ case '5': alert("hi"); //這個alert將不會執(zhí)行,因為數(shù)據(jù)類型不匹配 }
當心JavaScript中的硬換行。換行被解釋為表示行結(jié)束的分號。即使在字符串中,如果在引號中包括了一個硬換行,那么你會得到一個解析錯誤(未結(jié)束的字符串)。
var bad = '<ul id="myId"><li>some text</li><li>more text</li></ul>'; // 未結(jié)束的字符串錯誤var good = '<ul id="myId">' +‘<li>some text</li>‘ +‘<li>more text</li>‘ +‘</ul>’; // 正確
前面討論過的換行被解釋為分號的規(guī)則并不適用于控制結(jié)構(gòu)這種情況:條件語句關(guān)閉圓括號后的換行并不是給其一個分號。
一直使用分號和圓括號,那么你不會因換行而出錯,你的代碼易于閱讀,且除了那些不使用分號的怪異源碼外你會少一些顧慮:所以當移動代碼且最終導(dǎo)致兩個語句在一行時,你無需擔心第一個語句是否正確結(jié)束。
在任何JavaScript對象定義中,最后一個屬性決不能以一個逗號結(jié)尾。Firefox不會出錯,而IE會報語法錯誤。
var theObj = {city : "Boston",state : "MA",//IE6和IE7中有“缺少標識符、字符串或數(shù)字”的錯誤,IE8 beta2修正了它}
JavaScript DOM綁定(JavaScript DOM bindings)
<ul><li id="length">1</li><li id="thisLength">2</li><li id="thatLength">3</li></ul><script>var listitems = document.getElementsByTagName('li');var liCount = listitems.length; //IE下返回的是<li id="length">1</li>這個節(jié)點而不是所有<li的數(shù)量var thisLength = document.getElementById('thisLength');thatLength = document.getElementById('thatLength');//IE下會出現(xiàn)“對象不支持此屬性和方法”的錯誤,IE8 beta2下首次加載頁面會出錯,刷新頁面則不會//在IE中thisLength和thatLength直接表示以其為id值的DOM節(jié)點,//所以賦值時會出錯,當有var聲明時,IE會把其當著變量,這個時候就正常了。</script>
如果你要標記(X)HTML,絕不要使用JavaScript方法或?qū)傩悦鳛閕d的值。并且,當你寫JavaScript時,避免使用 (X)HTML中的id值作為變量名。
JavaScript中的許多問題都來自于變量作用域:要么認為局部變量是全局的,要么用函數(shù)中的局部變量覆蓋了全局變量。為了避免這些問題,最佳方案是根本沒有任何全局變量。但是,如果你有一堆,那么你應(yīng)該知道這些陷阱。
不用var關(guān)鍵字聲明的變量是全局的。記住使用var關(guān)鍵字聲明變量,防止變量具有全局作用域。在下面例子中,在函數(shù)中聲明的變量具有全局變量,因為沒有使用var關(guān)鍵字聲明:
anonymousFuntion1 = function(){globalvar = 'global scope'; //全局聲明,因為“var”遺漏了return localvar;}();alert(globalvar); //彈出“global scope”,因為函數(shù)中的變量是全局聲明anonymousFuntion2 = function(){var localvar = 'local scope'; //使用“var”局部聲明return localvar;}();alert(localvar); //錯誤 “localvar未定義”。沒有全局定義localvar
作為參數(shù)引進到函數(shù)的變量名是局部的。如果參數(shù)名也是一個全局變量的名字,像參數(shù)變量一樣有局部作用域,這沒有沖突。如果你想在函數(shù)中改變一個全局變量,這個函數(shù)有一個參數(shù)復(fù)制于這個全局變量名,記住所有全局變臉都是window對象的屬性。
var myscope = "global";function showScope(myscope){return myscope; //局部作用域,即使有一個相同名字的全局變量}alert(showScope('local'));function globalScope(myscope){myscope = window.myscope; //全局作用域return myscope;}alert(globalScope(’local’));
你甚至可以在循環(huán)中聲明變量:
for(var i = 0; i < myarray.length; i++){}
當你不止一次的聲明一個函數(shù)時,這個函數(shù)的最后一次聲明將覆蓋掉該函數(shù)的所有前面版本且不會拋出任何錯誤或警告。這不同于其他的編程語言,像Java,你能用相同的名字有多重函數(shù),只要它們有不同的參數(shù):調(diào)用函數(shù)重載。在JavaScript中沒有重載。這使得不能在代碼中使用JavaScript核心部分的名字極其重要。也要當心包含的多個JavaScript文件,像一個包含的腳本文件可能覆蓋另一個腳本文件中的函數(shù)。請使用匿名函數(shù)和名字空間。
(function(){// creation of my namespace 創(chuàng)建我的名字空間if(!window.MYNAMESPACE) {window['MYNAMESPACE'] = {};}//如果名字空間不存在,就創(chuàng)建它//這個函數(shù)僅能在匿名函數(shù)中訪問function myFunction(var1, var2){//內(nèi)部的函數(shù)代碼在這兒}// 把內(nèi)部函數(shù)連接到名字空間上,使它通過使用名字空間能訪問匿名函數(shù)的外面 window['MYNAMESPACE']['myFunction'] = myFunction;})(); // 圓括號 = 立即執(zhí)行// 包含所有代碼的圓括號使函數(shù)匿名
這個例子正式為了實現(xiàn)解決上一個陷阱“變量作用域”的最佳方案。匿名函數(shù)詳細內(nèi)容請看《Javascript的匿名函數(shù)》
一個常見錯誤是假設(shè)字符串替換方法的行為會對所有可能匹配都產(chǎn)生影響。實際上,JavaScript字符串替換只改變了第一次發(fā)生的地方。為了替換所有發(fā)生的地方,你需要設(shè)置全局標識。同時需要記住String.replace()的第一個參數(shù)是一個正則表達式。
var myString = "this is my string";myString = myString.replace("","%20"); // "this%20is my string"myString = myString.replace(/ /,"%20"); // "this%20is my string"myString = myString.replace(/ /g,"%20"); // "this%20is%20my%20string"
在JavaScript得到整數(shù)的最常見錯誤是假設(shè)parseInt返回的整數(shù)是基于10進制的。別忘記第二個參數(shù)基數(shù),它能是從2到36之間的任何值。為了確保你不會弄錯,請一直包含第二個參數(shù)。
parseInt('09/10/08'); //0parseInt(’09/10/08′,10); //9, 它最可能是你想從一個日期中得到的值
如果parseInt沒有提供第二個參數(shù),則前綴為 ‘0x’ 的字符串被當作十六進制,前綴為 ‘0′ 的字符串被當作八進制。所有其它字符串都被當作是十進制的。如果 numString 的前綴不能解釋為整數(shù),則返回 NaN(而不是數(shù)字)。
另一個常見的錯誤是忘記使用“this”。在JavaScript對象中定義的函數(shù)訪問這個對象的屬性,但沒有使用引用標識符“this”。例如,下面是錯誤的:
function myFunction() {var myObject = {objProperty: "some text",objMethod: function() {alert(objProperty);}};myObject.objMethod();}function myFunction() {var myObject = {objProperty: "some text",objMethod: function() {alert(this.objProperty);}};myObject.objMethod();}
有一篇A List Apart文章
對this使用最大的陷阱是this在使用過程中其引用會發(fā)生改變:
<input type="button" value="Gotcha!" id="MyButton"><script>var MyObject = function () {this.alertMessage = "Javascript rules";this.ClickHandler = function() {alert(this.alertMessage );//返回結(jié)果不是”JavaScript rules”,執(zhí)行MyObject.ClickHandler時,//this的引用實際上指向的是document.getElementById("theText")的引用}}();document.getElementById(”theText”).onclick = MyObject.ClickHandler</script>
其解決方案是:
var MyObject = function () {var self = this;this.alertMessage = “Javascript rules”;this.OnClick = function() {alert(self.value);}}();
類似問題的更多細節(jié)和解決方案請看《JavaScript作用域的問題》
當給函數(shù)增加一個參數(shù)時,一個常見的錯誤是忘記更新這個函數(shù)的所有調(diào)用。如果你需要在已經(jīng)被調(diào)用的函數(shù)中增加一個參數(shù)來處理一個特殊情況下的調(diào)用,請給這個函數(shù)中的這個參數(shù)設(shè)置默認值,以防萬一在眾多腳本中的眾多調(diào)用中的一個忘記更新。
function addressFunction(address, city, state, country){country = country || “US”; //如果沒有傳入country,假設(shè) “US”span>//剩下代碼}
你也能通過獲取arguments來解決。但是在這篇文章我們的注意力在陷阱上。同時在《Javascript風格要素(2)》
在JavaScript中關(guān)鍵字for有兩種使用方式,一個是for語句,一個是for/in語句。for/in語句將遍歷所有的對象屬性(attribute),包括方法和屬性(property)。決不能使用for/in來遍歷數(shù)組:僅在當需要遍歷對象屬性和方法時才使用for/in。
為了解決這個問題,大體上你可以對對象使用 for … in,對數(shù)組使用for循環(huán):
listItems = document.getElementsByTagName('li');for (var listitem in listItems){//這里將遍歷這個對象的所有屬性和方法,包括原生的方法和屬性,但不遍歷這個數(shù)組:出錯了!}//因為你要循環(huán)的是數(shù)組對象,所用for循環(huán)for ( var i = 0; i < listItems.length; i++) {//這是真正你想要的}對象的有些屬性以相同的方式標記成只讀的、永久的或不可列舉的,這些屬性for/in無法枚舉。實際上,for/in循環(huán)
會遍歷所有對象的所有可能屬性,包括函數(shù)和原型中的屬性。所有修改原型屬性可能對for/in循環(huán)帶來致命的危害,所以需要采用hasOwnProperty和typeof做一些必要的過濾,最好是用for來代替for/in。
switch語句
Estelle Weyl寫了一篇switch statement quirks
,其要點是:
- 沒有數(shù)據(jù)類型轉(zhuǎn)換
- 一個匹配,所有的表達式都將執(zhí)行直到后面的break或return語句執(zhí)行
- 你可以對一個單獨語句塊使用多個case從句
undefined ≠ null
null是一個對象,undefined是一個屬性、方法或變量。存在null是因為對象被定義。如果對象沒有被定義,而測試它是否是null,但因為沒有被定義,它無法測試到,而且會拋出錯誤。
if(myObject !== null && typeof(myObject) !== 'undefined') {//如果myObject是undefined,它不能測試是否為null,而且還會拋出錯誤}if(typeof(myObject) !== 'undefined' && myObject !== null) {//處理myObject的代碼}
Harish Mallipeddi
對undefined和null有一個說明。
事件處理陷阱
剛接觸事件處理時最常見的寫法就是類似:
window.onclick = MyOnClickMethod
這種做法不僅非常容易出現(xiàn)后面的window.onclick事件覆蓋掉前面的事件,還可能導(dǎo)致大名頂頂?shù)腎E內(nèi)存泄露問題。為了解決類似問題,4年前Simon Willison
就寫出了很流行的addLoadEvent():
function addLoadEvent(func) {var oldonload = window.onload;if (typeof window.onload != 'function') {window.onload = func;}else {window.onload = function() {oldonload();unc();}}}addEvent(window,'load',func1,false);addEvent(window,'load',func2,false);addEvent(window,'load',func3,false);
當然在JavaScript庫盛行的現(xiàn)在,使用封裝好的事件處理機制是一個很好的選擇,比如在YUI中就可以這樣寫:
YAHOO.util.Event.addListener(window, "click", MyOnClickMethod);