原來void是將其後的字面量當元表達式執行,並永遠返回undefined。同時undefined不是關鍵詞。。javascript
因爲JS表達式偏囉嗦,因而最近便開始採用Coffeescript來減輕負擔。舉個栗子,當我想取屋子裏的第一條dog時,首先要判斷house對象是否存在,而後再判斷house.dogs是否存在,最後取house.dogs[0]
。在JS須要這麼寫java
var dog = (typeof house !== 'undefined && house !== null) && house.dogs && house.dogs[0]
在Coffee中,我只須要這麼寫:web
dog = house?.dogs?[0];
寫到這裏,讀者會問,這跟標題《Javascript中的void》有一毛錢關係?Coffee的本質就是JS,之因此Coffee能工做的很好,是由於它生成出了高效並且健壯的JS代碼,咱們能夠看看它的生成結果。segmentfault
var dog, _ref; dog = typeof house !== "undefined" && house !== null ? (_ref = house.dogs) != null ? _ref[0] : void 0 : void 0;
短短一行Coffee代碼生成出了如此長的JS代碼,看上去彷佛比我最前面本身用JS寫的更靠譜更安全,末尾還出來了兩個void 0
,這到底是何方神聖?瀏覽器
結構化一下上面的例子:安全
dog = (typeof house !== "undefined" && house !== null) ? ((_ref = house.dogs) != null ? _ref[0] : void 0 ) : void 0;
- 若是
house
未定義或house
爲null
時,返回void 0
- 若是
house.dogs
爲null
時,返回void 0
可void 0
到底是什麼值,這個倒很容易測試:服務器
typeof void 0 //獲得"undefined" console.log(void 0) //輸出undefined
彷佛void 0
就是undefined
了,但這樣子路數太野,也不夠嚴謹,即沒法回答:void 100
, void hello()
, void i++
這無數可能組合的值是什麼?app
咱們來瞅瞅規範是怎麼說的吧。ide
規範是這麼說的
在ECMAScript 262規範,有以下描述:函數
The void Operator
The production UnaryExpression : void UnaryExpression is evaluated as follows:
- Let expr be the result of evaluating UnaryExpression.
- Call GetValue(expr).
- Return undefined.
NOTE: GetValue must be called even though its value is not used because it may have observable side-effects.
搬譯一下:
void操做符
產生式 UnaryExpression : void UnaryExpression 按以下流程解釋:
- 令 expr 爲解釋執行UnaryExpression的結果。
- 調用 GetValue(expr).
- 返回 undefined.
注意:GetValue必定要調用,即便它的值不會被用到,可是這個表達式可能會有反作用(side-effects)。
重點在於:不管void
後的表達式是什麼,void
操做符都會返回undefined
. 所以上面由Coffee編譯出來的代碼咱們能夠認爲是這樣的:
dog = (typeof house !== "undefined" && house !== null) ? ((_ref = house.dogs) != null ? _ref[0] : undefined ) : undefined ;
問題來了,既然(void 0) === undefined
,那直接寫undefined不就好了麼?
爲何要用void?
由於undefined
在javascript中不是保留字。換言之,你能夠寫出:
function joke() { var undefined = "hello world"; console.log(undefined); //會輸出"hello world" } console.log(undefined); //輸出undefined
對的,你能夠在一個函數上下文內以undefined作爲變量名,因而在這個上下文寫的代碼便只能經過從全局做用域來取到undefined,如:
window.undefined //瀏覽器環境 GLOBAL.undefined //Node環境
但要注意的是,即使window, GLOBAL仍然能夠在函數上下文被定義,故從window/GLOBAL上取undefined
並非100%可靠的作法。如:
function x() { var undefined = 'hello world', f = {}, window = { 'undefined': 'joke' }; console.log(undefined);// hello world console.log(window.undefined); //joke console.log(f.a === undefined); //false console.log(f.a === void 0); //true }
因而,採用void
方式獲取undefined便成了通用準則。如underscore.js裏的isUndefined
即是這麼寫的:
_.isUndefined = function(obj) { return obj === void 0; }
除了採用void
能保證取到undefined值之外,還有其它方法嗎?有的,還有一種方式是經過函數調用。如AngularJS的源碼裏就用這樣的方式:
(function(window, document, undefined) { //..... })(window, document);
經過不傳參數,確保了undefined參數的值是一個undefined。
其它做用
除了取undefined外,void
還有什麼其它用處嗎?
還有一個常見的功能,填充href
。下面是一個微博截圖,它的轉發
, 收藏
, 討論
都是超連接,可是用戶並不但願點擊它們會跳轉到另外一個頁面,而是引起出一些交互操做。
理論上而言,這三個超連接都是沒有URL的,但若是不寫的話,呵呵,點擊它會刷新整個頁面。因而便用上了href="javascript:void(0)
的方式,確保點擊它會執行一個純粹無聊的void(0)
。
另外一種狀況是,若是咱們要生成一個空的src
的image
,最好的方式彷佛也是src='javascript:void(0)'
,參見StackOverflow上的這個問題:What's the valid way to include an image with no src?
寫在最後
回到void
的定義,有一句話特別讓人迷惑:
注意:GetValue必定要調用,即便它的值不會被用到,可是這個表達式可能會有反作用(side-effects)。
這是什麼意思?這表示不管void右邊的表達式是什麼,都要對其求值。這麼說可能不太明白,在知乎上winter大神有過闡述關於js中void,既然返回永遠是undefined,那麼GetValue有啥用?,我且拾人牙慧,代入一個場景,看代碼:
var happiness = 10; var girl = { get whenMarry() { happiness--; return 1/0; //Infinity }, get happiness() { return happiness; } }; console.log(girl.whenMarry); //調用了whenMarry的get方法 console.log(girl.happiness); // 9 void girl.whenMarry; //調用了whenMarry的get方法 console.log(girl.happiness); // 8 delete girl.whenMarry; //沒有調用whenMarry的get方法 console.log(girl.happiness); //仍是8
上述代碼定義了一個大齡文藝女青年,每被問到何時結婚呀(whenMarry),happiness
都會減1。從執行狀況能夠看出,不管是普通訪問girl.whenMarry
,仍是void girl.whenMarry
都會使她的happiness--
。而若是把void
換成delete
操做符寫成delete girl.whenMarry
,她的happiness
就不會減了,由於delete
操做符不會對girl.whenMarry
求值。
總結
void
有以下做用:
- 經過採用
void 0
取undefined
比採用字面上的undefined
更靠譜更安全,應該優先採用void 0
這種方式。 -
填充
<a>
的href
確保點擊時不會產生頁面跳轉; 填充<image>
的src
,確保不會向服務器發出垃圾請求。