iT邦幫忙

2022 iThome 鐵人賽

DAY 2
0
Modern Web

這些那些你可能不知道我不知道的Web技術細節系列 第 2

你可能不知道void也是一個運算子

void也是一個運算子

你可能不知道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

就算是給了 nullNaN 或者就是給 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}`);
}

numberstring 一樣,作爲一種「型別存在」。通常作爲輸出,但你也可以放在輸入:

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)

過去介紹IIFE的時候,大多是使用括號將函式表達式括起來。

像是:

// 匿名函式
(function(){})();

// 命名函式
(function f2(){})();

// 或使用箭頭表達式
(()=>{})();

// 或是進一步賦予變數函式
(f = function(){})();
(f3 = ()=>{})();

現在知道可以這樣子寫:

void function(){}();
void function f2(){}();


// 或是進一步賦予變數函式
void (f4 = function(){})();
void (f5 = ()=>{})();

避免意外的JavaScript URIs

針對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)

本文同時發表於我的隨筆


上一篇
這些那些你可能不知道我不知道的Web技術細節系列(前言)
下一篇
你可能不知道在JavaScript裡的萬國碼
系列文
這些那些你可能不知道我不知道的Web技術細節33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言