2012年12月5日

Javascript 優良部分(筆記)__糟糕與不良的部分

A+
這一篇還滿莞爾的,書名叫做「Javascript 優良部分」,不過可能是因為已經看過「Javascript 設計模式」的關係,略微掃過後覺得大部分內容沒有很特別,唯一引起我興趣的是他的附錄,歸納了「Javascript 糟糕與不良的部分」,剛好與書名完全相反,也是我決定作筆記的內容。記錄這些 javascript 比較有問題的部分,遇上了才知道怎麼避免、學習與他們共處,就像學習與 IE 共處一樣...


一、糟糕的部分

1. 保留字

大家都知道 js 的保留字不能當變數名稱,不過保留字倒是可以拿來命名變數的屬性名稱,只是使用方式比較特殊。例如 class 為保留字──

var car; // ok
var class; // 不 ok


書上說「用保留字直接當屬性名稱不行,但保留字加上雙引號即可成為屬性」,但經過我的實測後──

car = {class: "1.8L"}; // CHROME、FireFox 均 ok,但 IE8 不 OK
car = {"class": "1.8L"}; // IE8 這樣才 ok

而一般呼叫屬性是使用句點 ".",書上說「屬性使用保留字時不允許這個方式,必須採另一種括號 [] 的方式」,經我的實測後──

car.class = "1.8L"; // CHROME、FireFox 均 ok,IE8 不 OK
car["class"] = "1.8L"; // IE8 這樣才 ok

雖然測試結果 Chrome、FireFox 是可以直接把保留字當作物件的屬性名稱,為了相容於各大瀏覽器,還是按照書上的方式比較安全。


2. typeof

typeof 本身大致沒啥問題,但也許是 js 對某些元素的設定還不夠嚴謹,所以會出現一些異象。

  A. null:null 代表 "無" 的資料型態,但──

alert(typeof null); // 顯示 object
原來 null 被歸類到 "物件",那這樣子要怎麼區分 null 與其他物件呢?由於 object 屬於 true 家族、null 屬於 false 家族,所以──

var car = {};
if (car && typeof car === "object") {...}

使用以上判斷句就能確定 car 不會是 null,而是真的 object 了。


  B. NaN:NaN 的定義為 "非數值",他不等於任何數值,也"不等於自己",結果──

alert(typeof NaN); // 顯示 number
alert(NaN === NaN); // 顯示 false
alert(NaN !== NaN); // 顯示 true

您說這像話嗎?


  C. 陣列:js 的陣列是假陣列,使用 typeof 可看出他其實是物件,其執行效能比真正的陣列差很多。那麼要如何區分資料型態是陣列還是物件呢?必須用到 constructor 這個屬性,例如──

var car = ["BMW", "FORD"];
if (typeof car === "object" && car.constructor === Array) {...}

這樣就能確定 car 是陣列了。(請注意 Array 並沒有用雙引號括起來)


  D. 正規表示式:假設一個正規表示式為 var re = /a/,如果用 typeof 檢查 re,書上說「有可能會顯示 object,也可能會顯示 function」,我測試各大瀏覽器都是顯示 object。總之書上表示「在目前制訂的 js 語言標準之下,還無法確實判斷這是一個正規表示式」。


3. ParseInt:ParseInt 會產生的 bug 已有專題文章因此不做紀錄,請參考「傳說中 JavaScript 的 parseInt('08')」。


4. 加號(+):js 的加號運算子可以把數字或字串相加。但如果相加的運算元其中一個是"字串",那麼運算結果將會超出預期。因此,如果想要做數字相加的話,務必確保所有的運算元不能出現一個字串(甚至也不能有空字串)。


以下舉三個例子看看 js 是怎麼做運算這件事,假設 Joshua 這個變數在執行過程中,因不明原因被轉成了字串並被刪除內容──

var John = 6, Joshua = "", Jane = 5;
alert(John + Joshua + Jane); // 顯示 65

var John = 6, Jimmy = 10, Joshua = "", Jane = 5;
alert(John + Jimmy + Joshua + Jane); // 顯示 165

var John = 6, Jimmy = 10, Joshua = "", Jane = 5, Jenny = 4;
alert(John + Jimmy + Joshua + Jane + Jenny); // 顯示 1654

相信看過以上例子後,在 js 要做相加的動作一定要很小心變數的型態。


5. 浮點數:這大概是本篇我最 shock 的一點了,比 NaN 還扯,你知道 0.1 + 0.2 等於多少嗎?答案不是 0.3──

var a = 0.1, b = 0.2, c = 0.3;
alert(a + b); // 顯示 0.30000000000000004
alert((a + b) === c); // 顯示 false

天哪!救人喔~哪天遇上了真不曉得有沒有辦法 debug 出來。要怎麼解決呢?書上提供的概念:先把每個數乘以 10 的 n 次方,相加後再除以 10 的 n 次方──

var a = 0.1, b = 0.2, c = 0.3,
plus = (10 * a + 10 * b) / 10;
alert(plus); // 顯示 0.3
alert(plus === c); // 顯示 true

問題是,誰會在寫程式碼的時候這麼做,就為了數字有可能出現 0.1、0.2?也許這是 js 最需要小心提防的 bug!


6. undefined 與 NaN:書上表示 undefined 與 NaN 都是全域變數,但並非保留字,因此可以改變他們的值。


但是經過我在各大主流瀏覽器實測的結果──

var blog = "電腦玩物"; // 初始值 原 js 程式碼
var newblog = "playpcesor"; // 原 js 程式碼

var undefined = "WFU BLOG"; // 非原程式碼 假設這一行是我在同網頁中其他 js 中亂加的程式碼 由於 undefined 是全域變數 所以會套用到同網頁的其他 js 程式
; // 過程省略

blog = undefined; // 這行原程式碼的用意為把 blog 數值暫時取消
; // 過程省略

if (!blog) {blog = newblog}
alert(blog); // CHROME 與 FIREFOX 顯示 "playpcesor" 但是 IE8 顯示 "WFU BLOG" 被我竄改成功

從以上的例子可以很清楚看到,在 CHROME 與 FireFox 之下,undefined 是唯讀的,不允許修改數值;但在 IE8 下 undefined 是可以被修改的,IE8 非安全的瀏覽器,「建議避免使用 IE8」。



二、不良的部分

1. ==

大部分 js 書都會強調使用 === 與 !==,避免使用 == 與 !=,不過沒看到實例的話很難瞭解這有多可怕,以下就來看看一些案例──

alert("" == "0"); // 顯示 false
alert(0 == ""); // 顯示 true
alert(0 == "0"); // 顯示 true

這個遇上了一定很難 dubug...如果三個全部使用 === 的話,結果全部都是 false。

alert(false == "false"); // 顯示 false
alert(false == "0"); // 顯示 true
alert(false == undefined); // 顯示 false
alert(false == null); // 顯示 false
alert(null == undefined); // 顯示 true

以上這幾個拿來比較是因為 false、null、undefined 都是 false 家族,從結果可知道最好還是用 === 來運算。

alert("\n\t" == 0); // 顯示 true
這個也滿特殊的,\n 是代表換行的意思,這種只有跳脫字元組成的字串也會被判定成類似空字串 ""。


2. with

基本上 with 我從沒用過,且很多 js 書都說不要用 with,把原因稍微筆記一下便是:

with (obj) {a = b;}
以上代表以下四種可能性:

a = b;
a = obj.b;
obj.a = b;
obj.a = obj.b;

由於根本無法預測執行出來會是哪種結果,所以結論是最好不要用 with。


3. eval

eval = evil,這很多人都知道了,除非不得以的情況再使用 eval。簡單紀錄繞過 eval 的使用語法,就像是「一、糟糕的部分」→「1. 保留字」的解決方法。


提供一個例子──

var car = {
class1: "1.8L",
class2: "2.0L",
class3: "2.4L"
}, i;

for (i = 1; i < 4; i++) {eval("alert(car.class" + i + ")");}

從以上的例子看來,要依序將 car 裡面 class1 ~ class3 的數值利用迴圈顯示出來,似乎只能使用 eval;但其實用中括號就能解決這個 case,不需使用 eval,將 for 迴圈改成以下即可──

for (i = 1; i < 4; i++) {alert(car["class" + i]);}
基本上使用刮號 [] 就能避免大部分需要使用 eval 的時機。

筆記到此結束,這篇對我自己的幫助還滿大的,至少釐清許多基礎觀念,基本功扎得實才能走得更遠。也希望對大家的 js 觀念有所幫助,節省許多 debug 的時間。


Javascript 相關筆記:

沒有留言:

↑TOP

張貼留言注意事項:

◎ 勾選「通知我」可收到後續回覆的mail!
提問請附網址、詳細描述狀況,如提供的資訊不足,則無法回覆。
◎ 請在相關文章留言,與文章無關的主題請至「Blogger 中文論壇」。
◎ 若詢問 CSS 、非官方範本問題、或貴站為商業網站 ,請參考「本站諮詢頁面」→「1. 諮詢服務」
◎ 若留言要輸入語法,"<"、">"這兩個符號請用其他符號代替,否則語法會消失!
◎ 若發現留言不見了,通常是因為 "複製貼上" 的內容常被系統判定為垃圾留言,請不用擔心,我會定期將留言恢復。
◎ 本站「已關閉自刪留言功能」。