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

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

錯誤1-使用全局變量

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

  1. var total = 0// total price
  2. tax = 0.05// 5%

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

  1. var total = 15// number of tweets pulled from twitter

或則是如下代碼:

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

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

  1. (function () {
  2.  var total = 0tax = 0.05;
  3.  
  4.  // other code
  5.  }());

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

  1. var cartTotaler = (function () {
  2.  var total = 0tax = 0.05;
  3.  
  4.  // other code
  5.  
  6.  return {
  7.  addItem : function (item) { },
  8.  removeItem : function (item) { },
  9.  calculateTitle : function () { }
  10.  };
  11.  }());

關於全局變量有一點值得注意,如果不用關鍵詞var來聲明創建變量,那麼javascript引擎會默認將該變量定義爲全局變量。

  1. (function () {
  2.  tax = 0.05;
  3.  }());
  4.  
  5.  var totalPrice = 100 + (100 * tax)// 105

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

錯誤2-不加分號

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

  1. function returnPerson (name) {
  2.  return
  3.  {
  4.  name : name
  5.  };
  6.  }

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

  1. return {
  2.  name : name
  3.  };

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

錯誤3-使用==

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

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

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

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

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

  1. '' == '0' // false
  2.  '0' == '' // true
  3.  false == '0' // true
  4.  ' \t\r\n ' == 0 // true

錯誤4-使用數據類型的包裝對象

Javascript提供了各個數據類型的包裝對象。

  1. new Number(10);
  2.  new String("hello");
  3.  new Boolean(true);
  4.  new Object();
  5.  new Array("one""two""three");

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

  1. 10;
  2.  "hello";
  3.  true;
  4.  {};
  5.  ["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和空對象。

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

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

  1. var propobj = { name"Joe"job"Coder"age25 };
  2.  
  3.  for (var prop in obj) {
  4.  console.log(prop + "" + obj[prop]);
  5.  }

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

  1. nameJoe
  2.  jobCoder
  3.  age25

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

  1. Function Dog (name) {
  2.  this.name = name;
  3.  }
  4.  Dog.prototype.legs = 4;
  5.  Dog.prototype.speak = function () {
  6.  return "woof!";
  7.  };
  8.  
  9.  var d = new Dog("Bowser");
  10.  
  11.  for (var prop in d) {
  12.  console.log( prop + "" + d[prop] );
  13.  }
  14.  
  15.  console.log("=====");
  16.  
  17.  for (var prop in d) {
  18.  if (d.hasOwnProperty(prop)) {
  19.  console.log( prop + "" + d[prop] );
  20.  }
  21.  }
  22.  
  23.  // Output
  24.  
  25.  // name: Bowser
  26.  // legs: 4
  27.  // speak: function () {
  28.  return "woof!";
  29.  // }
  30.  // =====
  31.  // name: Bowser

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

  1. for (var prop in d) {
  2.  if (typeof d[prop] !== 'function') {
  3.  console.log( prop + "" + d[prop] );
  4.  }
  5.  }

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

錯誤6-使用with或eval

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

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

  1. var person = { name"Joe"age : 10 };
  2.  
  3.  with (person) {
  4.  console.log(name)// Joe
  5.  console.log(age)// 10
  6.  }

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

  1. var person = { name"Joe"age : 10 },
  2.  name = "Billy";
  3.  
  4.  with (person) {
  5.  console.log(name)// Billy
  6.  job = "Designer";
  7.  }
  8.  
  9.  console.log(person.job)// undefined;
  10.  console.log(job)// Designer

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

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

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

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

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

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

  1. parseInt("200")// 200
  2.  parseInt("043")// 35

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

  1. parseInt("020"10)// 20
  2.  parseInt("100"2)// 4

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

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

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

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

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

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

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

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

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

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

  1. var list = document.getElementById("list"),
  2.  items = ["one""two""three""four"],
  3.  el;
  4.  
  5.  for (var i = 0items[i]i++) {
  6.  el = document.createElement("li");
  7.  el.appendChild( document.createTextNode(items[i]) );
  8.  list.appendChild(el)// slow, bad idea
  9.  }

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

  1. var list = document.getElementById("list"),
  2.  frag = document.createDocumentFragment(),
  3.  items = ["one""two""three""four"],
  4.  el;
  5.  
  6.  for (var i = 0items[i]i++) {
  7.  el = document.createElement("li");
  8.  el.appendChild( document.createTextNode(items[i]) );
  9.  frag.appendChild(el)// better!
  10.  }
  11.  
  12.  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工程師,如過這裏有遺漏的,請在留言板中留言告知大家。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章