Re: [問題] undefined null的差別?

作者: s25g5d4 (function(){})()   2013-12-02 12:13:10
※ 引述《lunamiou (○苗○烏)》之銘言:
: 看書的前面寫到
: var firstName = null;
這行後面 = null 是無意義的,
因為變數宣告在 javascript 有一個動作叫做 hoisting,
hoisting 就是 interpreter(解譯器) 會先掃過目前的 scope(作用域),
將所有 var 關鍵字抓出來,然後將每個變數宣告建立其專屬空間。
考慮以下程式碼:
function foo() {
bar = 2;
var bar;
console.log(bar);
}
foo(); // print 2
console.log(bar); // will throw an error
在直觀上我們會認為當執行 function foo 時,
因為尚未宣告 bar 就直接賦值,因此 bar 會成為全域變數
而造成可能的全域變數汙染。
但其實不然,因為 hoisting 的關係所以在執行 function foo 時,
變數 bar 會先被宣告並賦值 undefined 再從第一行執行。
另外這個 hoisting 對 function declartion(函數宣告)也是具有作用的
考慮以下程式碼:
console.log(foo()); // print 'bar'
function foo() {
return 'bar';
}
對於寫過 C/C++ 的程式設計師來說,這是違反傳統觀念的,
因為 foo 在被宣告前就被使用,理論上在這裡會噴錯誤,
但因為 hoisting 的關係,所以是可以正常執行的。
回到原題,
var firstName = null;
為什麼我說沒意義,是因為當關鍵字 var 出現時,其後跟著的變數
會在該 scope 開始執行前先被宣告並賦值 undefined,
(注意 hoisting 只對變數做宣告而忽略等號右邊)
此時當解譯器執行到 firstName = null 時
會把 firstName 賦值 null, 可以說這是多此一舉,
除非令 firstName = null 是你有意為之並且有特殊用途的。
考慮以下程式碼:
function foo() {
console.log(bar); // print undefined
bar = 1;
console.log(bar); // print 1
var bar = 2;
console.log(bar); // print 2
}
foo();
console.log(bar); // will throw an error
所以既然變數宣告會被 hoist, 那不如一開始寫的時候就先把變數宣告好,
也就是所有變數在使用 var 宣告時,一律放置在該 function 第一行,
至於要不要在宣告時賦值隨便你。
如果是想釋放變數儲存空間的話,是可以在該變數使用完後
令其等於 null, 這樣 JavaScript 引擎會自動作 garbage collection.
: 上面的程式一般用於初始化變數,表示尚不需要為該變數賦與一個實際值;
: 例如下面的程式,Object的一個實例info_obj的屬性message尚未初始化,
: 那麼,其值就是null:
: var info_obj = new Object();
: info_obj.message;
: alert(info_obj.message == null); //true
當然是 true 阿,這叫自動型態轉換,謝謝
這本書可以丟了,真的
同樣的例子,考慮 alert(info_obj.message === null);
結果就會不一樣了 (茶)
至於 undefined == null 是怎麼成為 true 的,
是有聽說這是歷史共業啦 (感謝 IE),
不過我傾向這是未定義行為,應該要避開。
如上要判斷 object property 是否有值的話,我會建議用
!!info_obj.message
一個 ! 代表 not, 把 true 變 false, 反之亦然
所以上面那行的詮釋為:
info_obj.message 未被賦值,其值為 undefined -> 自動型態轉換為 false ->
not false 轉換為 true -> not true 轉換為 false
當然如果 info_obj.message 本身就有值而且為 0, [], '' ... 等等
會被自動轉換為 false 的值,此時就要有額外的策略去判斷,
但在大部分應用中其實是不影響結果的。
如果這不是你要的結果,那可以用 obj.message === undefined 來判斷,
但要注意 undefined 值有可能會被覆蓋,這點可以透過用 closure 解決:
(function (undefined) {
....
}());
不傳入任何參數的話, undefined 值當然就是 undefined 了 (好饒舌 XD)
類似的用法可以參考 jQuery 原始碼,在此以 2.0.2為代表:
http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.js
可以看到程式開頭即為 closure 形式:
(function( window, undefined ) {
而結尾為:
})( window );
會傳入 window 主要是效能問題,因為加了一層 closure 等於多了一層作用域,
讓 window 成為 local 變數,就可以省下解譯器往上層作用域尋找的時間。
其原因為當解譯器在當前作用域找不到變數時,會往上一作用域搜尋。
: undefined 常數
: undefined常數用於尚未初始化的變數或未初始化的
: 動態物件屬性的特殊值。
: 例如下面的兩個變數都是undefined
: var firstName;
: var lastName;
: 這個跟null有差別嗎?
undefined 與 null 做不嚴謹相等比較 (==) 時為 true,
但做 === 時就不會返回 true,
這絕對是地雷,恭喜你踩到了 \:D/
== 與 === 的差別在於前者會做自動型態轉換,後者不會,
因此若等號兩邊型態不同,後者會直接返回 false.
考慮以下程式碼:
console.log(undefined == null); // print true
console.log(undefined === null); // print false
console.log(0 == false); // print true
console.log(0 === false); // print false
另外要注意一點,undefined 是可以被覆蓋的,也就是說:
function foo() {
var undefined = 10;
console.log(undefined);
}
foo(); // print 10
console.log(undefined); // print undefined
好消息是在新版瀏覽器中 global object (全域物件,在瀏覽器環境中即為 window)
下的 undefined 是 read only 唯讀變數,
但在舊版瀏覽器中要小心全域變數 undefined 是有可能被竄改的。
undefined 的正身便是在全域物件下的變數 undefined,
如果以下敘述執行當下的作用域(包含其上層作用域)裡並沒有對
undefined 賦予其他值,那麼所有 foo === undefined 等價於
foo === window.undefined.
另外為什麼說 undefined 是全域物件的變數而不是全域物件的屬性,
其實當然是可以這樣叫的,所有全域變數都是全域物件的一個屬性。
: 再看下面的程式,user是Object類別的一個實例,該實例的sex屬性如果
: 未初始化,那麼其屬性值為undefined,而非null,因為Object並非動態
: 類別。例如下面的程式:
: var user = new Object();
: alert(user.sex); //輸出undefined
: ----------------------------
: 以上看完還是疑惑,什麼樣的結果是null,什麼會是undefined呢?
: 這本書寫的「類別」是什麼意思,英文的原文會是?
Class
但一般稱此為 Constructor (建構式),比較不常稱 Class,
這是要跟使用 Class 的物件導向語言作區別。
所以說以下程式碼:
function Person(name, gender, age) {
this.name = name;
this.gender = gender;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log('My name is ' + this.name + ". I'm " + this.gender +
". I'm " + this.age + (this.age === 1) ?
' year' : ' years' + ' old.');
}
var jack = new Person('jack', 'male', 20);
Person 稱為 Constructor (建構式) 或稱 Class (類別),
jack 稱為 Instance of constructor Person (Person 建構式的實例),
jack.name, jack.gender, jack.age 稱為 Property (屬性),
jack.sayHello 稱為 Method (方法).
: 另外「動態物件屬性」、「動態類別」的意思分別是?
: 小的才學疏淺,這邊看了幾次還是不太懂,
對不起我也不懂... 麻煩給一下上下文,謝謝 QQ
: 還請大家指教一下,感激感激~~~ <(__ __)>
以上,不知道 console.log 是甚麼也可以用 alert() 代替
延伸閱讀:
https://developer.mozilla.org/en-US/docs/Web/
JavaScript/Reference/Statements/var
( 縮網址:http://ppt.cc/CNFY )
http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
http://stackoverflow.com/questions/6429225/javascript-null-or-undefined
作者: s25g5d4 (function(){})()   2013-02-02 12:13:00
哭哭 打到一半PTT斷線 還好有暫存檔 可是P幣變少了
作者: danny8376 (釣到一隻猴子@_@)   2013-02-02 14:49:00
用心推 不過... !!info_obj.message <- 這樣也是未定義真想避免所謂的未定義行為請用typeof去判斷...不過... 對obj(?這樣用到也沒啥問題... JS其實有些地方真的是頗麻煩 OTZ而undefined這東西... 主要是因為他實際上完整是window.undefined 實質上也就是個全域變數但這全域一般來說應該都是readonly才是
作者: mrbigmouth (大嘴先生)   2013-02-02 15:10:00
不對喔 你可以寫var undefined=123; XD
作者: danny8376 (釣到一隻猴子@_@)   2013-02-02 17:02:00
樓上 再怎改window.undefined都不會變啊...readonly的是window.undefined 又不是local...
作者: mrbigmouth (大嘴先生)   2013-02-02 17:17:00
readonly是舊版js沒有的功能 至少我剛模擬IE7,8都是能改的何況在local改變undefined的值是能成功的因此還是不要太相信undefined真的是undefined比較好
作者: akiratw   2013-02-02 17:24:00
有看過用 void 0 來取代 undefined 的void 是運算子,後面不管接什麼都會回傳真正的 undefined所以可以用 if (foo === void 0) 來檢查是否 undefined而不會有 undefined 被複寫的疑慮
作者: s25g5d4 (function(){})()   2013-02-02 18:09:00
這好酷... XDD
作者: danny8376 (釣到一隻猴子@_@)   2013-02-02 21:45:00
mrbigmouth IE是啥 早就隨他了XDlocal的問題... 大多時候都能自己掌控吧XD不過像很多lib都會自己在定義一次undefined就是wwws25g5d4 看到最後一段就感覺好多 直接end了XDD不過void這部分 a tag的inline看很多了script裡倒是沒看過www原PO修文有提到closure了www
作者: tomin (Schrödinger's cat)   2013-02-02 21:53:00
推 寫得很詳細

Links booklink

Contact Us: admin [ a t ] ucptt.com