你可能不知道void
也是一個ECMAScript的保留關鍵字 (reserved keyword),也可能不知道它也是一個 運算子。
誒黑~!
因爲這張卡的效果就是:
什麼都不給
你可以像是一元運算子那樣使用它:
var a = 1;
a = -a; // -1
a = !a; // false
a = typeof a; // boolean
a = void a; // undefined
也可以將表達式用括號包起來,像是函式呼叫:
var a = 100
a = void(a) // undefined
實際上這個行爲更像是先針對表達式
(a)
求值,在使用運算子void
。過程中不存在一般意義上的函式呼叫。
但不管怎樣,它就是調皮的什麼也不給你,只會告訴你:「undefined
」。
console.log(void 1) // output: undefined
var v = void(100 + 50)
console.log(v) // output: undefined
就算是給了 null
、 NaN
或者就是給 undefined
也一樣。不存在特例,一視同仁。
console.log(void null) // output: undefined
console.log(void NaN) // output: undefined
console.log(void Infinity) // output: undefined
console.log(void undefined) // output: undefined
如果從 輸入->處理->輸出 ,其中通常輸出必須有一個或多個才有意義來看,void
好像沒什麼用處XD,但是void「存在即是事實」。
以下是高德納在他的著作《電腦程式設計藝術》裡對演算法的特徵歸納:
- 輸入:一個演算法必須有零個或以上輸入量。
- 輸出:一個演算法應有一個或以上輸出量,輸出量是演算法計算的結果。
- 明確性:演算法的描述必須無歧義,以保證演算法的實際執行結果是精確地符合要求或期望,通常要求實際執行結果是確定的。
- 有限性:依據圖靈的定義,一個演算法是能夠被任何圖靈完備系統類比的一串運算,而圖靈機只有有限個狀態、有限個輸入符號和有限個轉移函式(指令)。而一些定義更規定演算法必須在有限個步驟內完成任務。
- 有效性:又稱可行性。能夠實現,演算法中描述的操作都是可以通過已經實現的基本運算執行有限次來實現。
-- 截取自維基百科^1
實際上 undefined
確實是一個有效的輸出,而且不少JavaScript原生函式總有同樣的輸出結果。一個最顯擺的例子就是經常使用的console.log()
console.log(`console return: ${console.log("Hello World")}`);
Output:
Hello World
console return: undefined
不過它並不是什麼都不做,只是做了不直接告訴你而已:
void console.log("Hello, World"); // 會打印出"Hello World"
var a = 10;
void a++; // 實際上會執行 `a++` 而改變 `a` 值
console.log(a); // a: 11
Output:
Hello World
11
這與邏輯短路不同:
false && console.log("Hello, World");
var a = 10;
false && a++
console.log(a); // a: 10
如果你和我一樣有在寫TypeScript,那你有可能在TypeScript也看過它:
function hello(name: string): void {
console.log(`Hello, ${name}`);
}
和 number
、string
一樣,作爲一種「型別存在」。通常作爲輸出,但你也可以放在輸入:
function hello(name: void): number {
console.log(`Hello, ${name}`);
return 0;
}
不過放在輸入這實在沒什麼用,因爲相當於這樣寫:
function hello(): number {
console.log(`Hello, ${undefined}`);
return 0;
}
總結 void
的特性:
void
後的表達式會執行void
什麼也不回傳,或者說永遠回傳undefined
銜接上頭,有時候可能你會想這樣寫:
let hello_list = [];
function hello(name: string): void {
return hello_list.push(name)
}
因爲這樣比起將return
換行在寫,有時候更爲簡潔。
但你會發現lint檢查器因爲實際回傳值類型與申明不同而報錯。這時候就可以用上 void
let hello_list = [];
function hello(name: string): void {
return void hello_list.push(name)
}
首先,可以避免應該回傳undefined
的函式,回傳其他有效值。
避免呼叫函式後接值賦值,照成值泄漏問題。
let name = hello('bob');
console.log(name); // undefined
特別在箭頭表達式:單一表達式情況允許 不使用括號直接返回值 的情況。
特別是箭頭表達式右側doDomething()
如果會回傳一個參考值,而該箭頭函式設計上不該有任何返回值的情況。void
可以避免參考是不安全的被使用到。
button.onclick = () => void doSomething();
例子取自MDN^2
過去介紹IIFE的時候,大多是使用括號將函式表達式括起來。
像是:
// 匿名函式
(function(){})();
// 命名函式
(function f2(){})();
// 或使用箭頭表達式
(()=>{})();
// 或是進一步賦予變數函式
(f = function(){})();
(f3 = ()=>{})();
現在知道可以這樣子寫:
void function(){}();
void function f2(){}();
// 或是進一步賦予變數函式
void (f4 = function(){})();
void (f5 = ()=>{})();
針對a:href
元素,瀏覽器是支援JavaScript URI的,像是
<script type="text/javascript">
function newPage() {
return `
<h1>This is a new page</h1>
`;
}
</script>
<a href="javascript: newPage()">
Go to new page
</a>
這是有意識的將畫面顯式新的頁面內容。
更多時候只是希望執行一段JS,而不希望畫面跳轉,那newPage()
應該沒有返回值:
function newPage() {
// do something
return;
}
或是使用void
:
<a href="javascript: void newPage()">
Go to new page
</a>
我第一次看到
void
就是看到void(0)
出現在a:href
:
<a href="javascript:void(0);">action</a>
就如同class
作爲保留字存在好一段時間,直到ES5才正式存在意義。 還有相當多保留字目前沒有作用或是非常冷門^3:
enum
implements
interface
package
private
protected
public
static
abstract
native
synchronized
transient
volatile
(有需多同樣出現在TypeScript)
本文同時發表於我的隨筆