下面這段代碼,你知道有哪些錯誤嗎:
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 documentname = 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 = 2var 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;
簡單質樸,往往是最好的。