網頁制作poluoluo文章簡介:可能被你忽略的 JavaScript 代碼陷阱.
下面這段代碼,你知道有哪些錯誤嗎:
var g_bar = "bar"; function foo(container, config) { var container = container || document, name = config.name || "無名氏", isLive = config.isLive || true; var g_bar = g_bar || ""; if(g_foo) { /* your code */ } } foo(document, {isLive: false});
請仔細思考後再往下閱讀。
—- 幫助你思考的刷屏線 開始 —-
—- 幫助你思考的刷屏線 結束 —-
1. isLive = config.isLive || true, 當傳入的值有可能就是0, undefined, null, false, "", NaN這六個 falsy 值時,用 || 來設定默認值不妥當。更保險的做法是:
isLive = "isLive" in config ? config.isLive : true;
如果是獨立變量,可以采用:
someVar = typeof someVar !== "undefined" ? someVar : defaultValue;
注意:大部分情況下,用 || 已經夠用,比如:
container = container || document name = config.name || "無名氏"
一切皆權衡。
2. var g_bar = g_bar || "", 原意是取全局變量 g_bar 的值給內部變量 g_bar, 默認為空字符串。然而,實際情況等價為:
var g_bar; g_bar = g_bar || "";
很明顯,|| 號左邊的 g_bar 也是內部變量,並且為 undefined, 因此var g_bar = g_bar || ""實際上是var g_bar = "", 沒有滿足代碼的原始意圖。
思考:代碼中的var container = container || document有無問題?為什麼?
3. if(g_foo) { /* code */ }, 這段代碼在執行時會報錯。我們都知道在 JS 裡,變量不定義就可以用。但一定要清楚,未定義的變量,僅僅是可寫,但不可讀。比如:
g_foo = 2; // 等價 window.g_foo = 2 var t = g_foo2; // 不等價為 var t = window.g_foo2, 會報錯
具體原因可以參見 JavaScript 運行機制淺探:
未定義變量意味著在 scriptObject 的變量表中找不到,JS 引擎會沿著 scriptObject 的 upvalue 往上尋找,如果都沒找到,對於寫操作 i = 1; 最後就會等價為 window.i = 1; 給 window 對象新增了一個屬性。對於讀操作,如果一直追溯到全局執行環境的 scriptObject 上都找不到,就會產生運行期錯誤。
因此嚴謹的寫法是:
if(window.g_foo) { /* your code */ }
不要小看這些細微之處,有時會讓人抓狂的。但這些細微之處又很容易被忽略或濫用。比如 YUI 2.8r4 裡,有一個遺傳了很久的 bug:
var NOTHING = []; // .... later: function(when, o, fn, data, periodic) { when = when || 0; o = o || {}; var m = fn, d = data, f, r; // ... if (d && !L.isArray(d)) { d = [data]; } f = function() { m.apply(o, d || NOTHING); }; // ... }
當你的調用代碼類似Lang.later(delay[0], o, "show", index)時,如果 index 不幸是 base-0 的,那麼取 0 時,m.apply(o, d || NOTHING)會讓你得到“驚喜”。更妥的做法是類似 YUI3 中的修正:
// ... if (!L.isArray(d)) { d = [data]; }? f = function() { m.apply(o, d); }; //...
對於 || 和 && 的用法,很多 JS 書籍(無論中外),都用來片面強調 JS 的靈活性,包括 Douglas 的《JavaScript The Good Parts》中也存在誤導。
最後,有感於 NCZ 今天寫的 Writing Maintainable Code, 再舉一例(和本文主題關系不明顯,但的確又有關系,交給你去思考啰):
var isBoy = true; isBoy = typeof isGirl !== "undefined" ? !isGirl : true;
或者來個耍酷的代碼:
var isBoy = true; (typeof isGirl !== "undefined") && (isBoy = !isGirl);
然而,以上兩種寫法,無論從代碼長度還是性能上講,都不如更直白的寫法:
var isBoy = true; if(typeof isGirl !== "undefined") isBoy = !isGirl;
簡單質樸,往往是最好的。