javascript編程容易出現的11個錯誤

來源:互聯網
上載者:User

javascript是比較容易學的。但是,對於這門語言需要有一些值得注意的地方。本文將指出javascript編程中可能犯過的10個錯誤

錯誤1-使用全域變數

如果你剛開始javascript編程,可能會覺得全域變數很好用。事實上,剛開始javascript編程,你可能不知道使用全域變數會帶來什麼麻煩。在同一個頁面中,全域變數可以在任何內嵌的javascript程式碼片段中或是該頁面載入的不同的js檔案中,都能訪問到。這聽起來很強大,是嗎?這就使得全域變數可以隨時隨地的被修改賦值。
事實上這樣很糟!
這樣做會導致變數在意料之外被修改重寫。假設有一個網店,需要用javascript計算並顯示購物車所有商品的價格總和(當然,伺服器端還會進行重新計算,這裡只是為了增強使用者的體驗)。可能會編寫代碼如下:

var total = 0, // total pricetax = 0.05; // 5%

現在,還需要用javascript代碼在網站上展示一些資訊,或則是做一個商品倉庫。代碼如下:

var total = 15; // number of tweets pulled from twitter

或則是如下代碼:

var tax = function () { /* ... */ }; // Trigger Animation eXperience function

現在,出現問題了:兩個重要的變數被重寫,但可能還沒被意識到。這樣代碼運行會出錯,會花費很多時間來跟蹤和修複該錯誤。
那該如何解決呢?簡言之—“封裝”:當然封裝有很多方法可以實現。第一種做法是將代碼放入一個匿名的自調函數中。代碼如下:

(function () { var total = 0, tax = 0.05;  // other code }());

這樣做,在函數外部是絕對不能訪問到函數內部定義的變數。這就形成了個人的代碼空間,但是這樣就不能公開部分方法或屬性。例如,想要建立一個購物車,定義一個總價的變數,作為公用屬性,這種情形下可以採用模組式編程。

var cartTotaler = (function () { var total = 0; tax = 0.05;  // other code  return { addItem : function (item) { }, removeItem : function (item) { }, calculateTitle : function () { } }; }());

關於全域變數有一點值得注意,如果不用關鍵詞var來聲明建立變數,那麼javascript引擎會預設將該變數定義為全域變數。

(function () { tax = 0.05; }());  var totalPrice = 100 + (100 * tax); // 105

這裡的變數tax在函數外部也是可以被訪問的,因為在定義tax的時候沒有使用var關鍵詞。

錯誤2-不加分號

每句javascript語句必須以分號結尾。在編程時如果忘了加分號,這時javascript編解析器會自動加上。那我們在編程的時候是不是就可以完全不用浪費時間去加分號呢?
但是在一些語句中,分號是必不可少的。如for迴圈語句中的分號是必不可少的,否則會報語法錯誤。那麼語句末尾的分號呢?
Javascript社區已經討論過該問題。下面是本文的看法:你的代碼,只要是被javascript解析器修改過(即便是很小的修改,如添加分號),就可能會出現一些你意料之外的結果。看看下面這段javascript代碼:

function returnPerson (name) { return { name : name }; }

該方法看起來會返回一個對象,但事實上,javascript解析器會在return後面緊接著添加一個分號,這就導致該函數返回undefined。Return後的對象會被忽略。解決方案很簡單,代碼如下:

return { name : name };

在javascript編程中應嚴格要求自己添加分號,這個習慣並不難。當然,作為一名web開發人員,你可能也會用到其他的語言(如php),這些語言都是嚴格要求以分號結尾的。你可以把這些程式設計語言的習慣帶到javascript編程中來。
作者註解:除了你完全肯定可以被忽略的地方,你都不可以忽略添加分號。

錯誤3-使用==

如果你問一個javascript編程者,在javascript編程中通常會犯什麼錯誤。他/她很可能會說,使用= = =來代替= =。這是什麼意思呢?
試試下面的代碼:

if (1 == 1) { console.log("it's true!"); }

代碼如你所願的輸出了“it’s true!”那再試試下面的代碼:

if (1 == '1') { console.log("it's true!"); }

這段代碼在控制台中盡然也輸出了“it’s true!”,但其實這並不是你所期望的輸出。這裡的==運算子轉換了運算數的類型,從而使得兩個運算數相等了。這裡if語句中的==運算子使得右邊string類型的“1”變成了number型的1。
想要得到你想要的輸出,這裡應該用= = =運算子來代替= =。===不會強制轉換運算數的類型,這樣才能如你所期望的。同樣地,用!= =運算子來替換!=。下面是用==來做比較,得出的結果令人意外。

'' == '0' // false '0' == '' // true false == '0' // true ' \t\r\n ' == 0 // true

錯誤4-使用資料類型的封裝對象

Javascript提供了各個資料類型的封裝對象。

new Number(10); new String("hello"); new Boolean(true); new Object(); new Array("one", "two", "three");

首先,它們並不好用。上面的代碼可以用更少的代碼來實現,如下:

10; "hello"; true; {}; ["one", "two", "three"];

但是這兩種方式還是有所不同的。下面是douglas crockford的觀點:
例如用new Boolean(false)建立一個對象,該對象有一個方法valueOf,調用該方法會返回構造器的值。
這意味著,如果運行typeof new Number(10)或者是typeof new String(‘hello’),將返回‘object’,而不是’number’或’string’.另外,用資料類型的封裝還會引發一些意料之外的結果。
那麼為什麼javascript要提供資料類型的封裝對象呢?這是因為javascript解析器內部會調用。簡單的資料類型是沒有方法的(因為它們不是對象),所以當調用簡單類型的資料的方法時(如’hello’.replace(‘ello’, ‘i’)),javascript會調用String封裝對象來建立一個臨時的string對象,然後調用該對象的方法,完成調用後會刪除這個臨時對象。
所以不要用資料類型的封裝對象來建立簡單類型的資料。
注意:本來不用這麼說明的,但本文還是想明確的告訴初學者:不是說不使用它們和new(儘管有些是推薦使用的),這裡需要特別指出的是,這個建議特別針對這些資料類型,如:number、string、Boolean、array和Null 物件。

錯誤5-在使用for-in時不對屬性檢查

我們都很熟悉遍曆數組,但是你可能還希望能遍曆對象的屬性。(題外話:array事實上是屬性名稱為數位對象)。這是可以用for-in迴圈語句,代碼如下:

var prop, obj = { name: "Joe", job: "Coder", age: 25 };  for (var prop in obj) { console.log(prop + ": " + obj[prop]); }

運行上面的代碼,輸出如下:

name: Joe job: Coder age: 25

但是,瀏覽器中for-in遍曆對象屬性和方法時會包括對象原型鏈上的所有屬性和方法。但絕大多數屬性是不希望被枚舉出來的。可以用hasOwnProperties方法來檢測屬性是否屬於對象。代碼如下:

Function Dog (name) { this.name = name; } Dog.prototype.legs = 4; Dog.prototype.speak = function () { return "woof!"; };  var d = new Dog("Bowser");  for (var prop in d) { console.log( prop + ": " + d[prop] ); }  console.log("=====");  for (var prop in d) { if (d.hasOwnProperty(prop)) { console.log( prop + ": " + d[prop] ); } }  // Output  // name: Bowser // legs: 4 // speak: function () { return "woof!"; // } // ===== // name: Bowser

有時,只希望枚舉列出對象的的屬性,不包括方法。可以用typeof方法,代碼如下:

for (var prop in d) { if (typeof d[prop] !== 'function') { console.log( prop + ": " + d[prop] ); } }

不管怎麼樣,在用for-in迴圈時要確保對屬性進行檢測,以避免得到你意料之外的結果。

錯誤6-使用with或eval

幸運的是,現在大部分javascript教程都不會教你使用with或eval。但是一些老教程或名氣不大的資料時(因為有時候好的資料在網上很難找到),可能會發現有使用with或eval。
下面是兩個不用with的主要原因:
1、 它會降低代碼效能
2、 不易於代碼閱讀

第一點是它與生俱來的。第二點,看看下面的代碼,這裡用with訪問person對象的name、age屬性。

var person = { name: "Joe", age : 10 };  with (person) { console.log(name); // Joe console.log(age); // 10 }

但是,若果有一個變數和對象其中一個屬性同名,那用with會發生什麼呢?事實上,這種情況下,訪問變數會引用那個變數而不是對象的屬性。另一個值得注意的是,在with語句中,如果訪問的屬性不存在或對象不存在,就不能給對象添加屬性,同時會使得範圍鏈上with範圍後的那個範圍中建立一個變數。

var person = { name: "Joe", age : 10 }, name = "Billy";  with (person) { console.log(name); // Billy job = "Designer"; }  console.log(person.job); // undefined; console.log(job); // Designer

那eval呢?它可以接受一個字串參數,並且解析執行改字串。

這聽起來沒有什麼不好,甚至覺得很棒,對嗎?但問題就是這太棒了!與其將一連串字串將給它來解析執行,為什麼不直接編寫在程式中呢?不該這麼做的原因如下:

  1. 完全可以直接編寫在代碼中。
  2. eval解析很慢的,效能跟with差不多。

eval的用法是在非運行時運行環境。可以從伺服器端或用戶端擷取代碼。難道真的想你的網站使用者來底控制你的代碼?這樣不就意味著你的網站向無數的駭客敞開了大門。用eval就好比,離開了家,並告訴大家鑰匙就是門口墊子下面。如果你愛自己或你的使用者,就不要用eval。

錯誤7-在用parseInt時不用基數

Javascript提供了一個非常有用的方法parseInt,它可以將字串轉換為數值。

parseInt("200"); // 200 parseInt("043"); // 35

結果是不是令人覺得意外?第二句為什麼不是43?事實上,parseInt方法不僅僅是只能把字串當做十進位數來轉換。當parseInt的第一個參數是以0開頭,它就會把字串當作是八位元來轉換。這就是不使用基數出現的意料之外結果。第二個參數–基數,會指定parseInt方法把字串當做什麼進位的數來轉換。(當然,它的傳回值永遠是十進位數)

parseInt("020", 10); // 20 parseInt("100", 2); // 4

錯誤8 if和while語句不使用{}

Javascript最明顯的特點是文法要求不那麼嚴格。但正是這樣的特點,有時會帶來麻煩。If和while語句的{}就會引起一些麻煩。{}是根據if條件成立時執行代碼語句的條數來用的。

if (true) console.log("inside the if statement");

這裡看起來沒什麼問題,因為這裡的執行語句只有一句

var arr = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"], i = arr.length - i;  while (i) console.log( arr[i--] );

但是這樣做不易於閱讀:首先,不用{}代碼結構看起來不是那麼清晰。

if (true) console.log("inside the if-statement."); console.log("outside the if-statement.");

看看上面的代碼,第二行console語句是不屬於if執行語句的,但是這裡它看起來像是if的執行語句。使用{}會使結構更清晰。同時,如果你想在if的執行代碼中添加一句,也需要使用{}。習慣使用{}並不是一件難事。

錯誤9-單個單個地插入dom元素

這並不是javascript自身的問題。99%100的javascript編程都會涉及DOM操作,在對DOM操作上會犯很多錯誤,但這是最明顯的一個。
DOM操作會使瀏覽器重繪頁面,所以如果有一連串的元素一個接一個的插入頁面中,這會急劇增加瀏覽器渲染頁面的負擔。

var list = document.getElementById("list"), items = ["one", "two", "three", "four"], el;  for (var i = 0; items[i]; i++) { el = document.createElement("li"); el.appendChild( document.createTextNode(items[i]) ); list.appendChild(el); // slow, bad idea }

Document fragments 是一個DOM元素容器,可以使用它同時添加這些元素到頁面中。Document fragment自身不是一個DOM節點,它不會在頁面DOM樹中顯示,並且在把它插入DOM之前它是不可見的。下面是它的用法:

var list = document.getElementById("list"), frag = document.createDocumentFragment(), items = ["one", "two", "three", "four"], el;  for (var i = 0; items[i]; i++) { el = document.createElement("li"); el.appendChild( document.createTextNode(items[i]) ); frag.appendChild(el); // better! }  list.appendChild(frag);

非常快速、簡潔!

錯誤10-不懂javascript

許多人不花時間來認真地學習javascript。
Javascript並不等於jquery。這是否嚇到你了?如果你會犯以上列出的錯誤,那麼你需要認真地學習javascript。Javascript是一門語言,一門基本上不用學習就可以使用的語言,這就導致許多人不花時間來認真學習。千萬不要這麼做,已經有太多太多的教程指出這樣做的弊端,你沒有借口不認真學習javascript。如果你只是瞭解jquery(或mootools,或別的),那麼你學習瞭解javascript的出發點就已經錯了。

錯誤11-嚴格遵循以上的規則

“Rules are made to be broken.”(規則是用來被打破的。)

雖然本文列舉了以上規則,但像任何事一樣,規則是用來被打破的。如果是剛開始學習javascript,你會嚴於律己,嚴格遵循以上規則。但是到了真正理解了為什麼要遵循以上規則的原因後,你才會知道靈活運用以上規則。例如,eval被反覆的說到不能用,但是它卻是唯一能解析伺服器端返回json字串的方法。當然這裡在運用它時會做很多安全的檢測(你可能會用到一個javascript庫)。這裡想要指明的是,在需要的地方,不應該害怕犯錯,大膽的運用它。當然,永遠不要犯錯誤10所指出的問題。

結論:

如果你是javascript新手,希望以上的內容對你javascript編程有所協助。如果你是一個資深javascript工程師,如過這裡有遺漏的,請在留言板中留言告知大家。

本文翻譯自《The 11 JavaScript Mistakes you’re Making》

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.