你懂 JavaScript 嗎?#4 型別(Types)

你所不知道的 JS

本文主要會談到

何謂「型別」?

「型別」是固有的、內建的特徵,能唯一識別特定值的行為。例如:數字 123 和字串 ‘123’ 就是不一樣的,數字 123 可做數學運算處理,而字串 ‘123’ 可能就是做些顯示到畫面上的操作。

內建型別(Built-in Types)

JavaScript 定義了以下七種內建型別

其中,這些內建型別又可分為兩大類-基本型別(primitives)和物件型別(object)。基本型別有 number、string、boolean、null、undefined、symbol,而物件型別就是物件與其子型別(subtype),例如:物件、陣列、函式、日期等。

我們可用 typeof 來檢測值的資料型別為何。

typeof 'Hello World!'; // 'string'
typeof true; // 'boolean'
typeof 1234567; // 'number'
typeof null; // 'object'
typeof undefined; // 'undefined'
typeof { name: 'Jack' }; // 'object'
typeof Symbol(); // 'symbol'
typeof function () {}; // 'function'
typeof [1, 2, 3]; // 'object'
typeof NaN; // 'number'

這裡會看到幾個有趣的(奇怪的)地方…


看到這裡是不是覺得 JavaScript 很難懂呢? (/‵Д′)/~ ╧╧

翻桌


好的,在翻桌之前,先來解決剛剛提到的幾個問題。

Q1:如何檢測 null?

先回顧一下之前提到的「Truthy & Falsy」的概念,在做比較時會被轉型為 false 的值有

而除了以上之外,都會被轉為 true,舉例如下

咦?是不是看到一線生機?

shock

我們可利用 null 會被 typeof 檢測為 object 並且會轉為 false 的結果來驗證值是否為 null。

const happy = null;

if (!happy && typeof happy === 'object') {
  console.log('我是 null!');
}

得到「我是 null!」,輕鬆解決,得分!

shock

Q2:既然函式與陣列都是物件,那其中的屬性 length 有什麼不同?

函式的 length 是指參數個數,而陣列的 length 是指內部成員個數。

function testMe(arg1, arg2, arg3) {
  console.log('This is testMe!');
}

const list = [1, 2, 3, 4, 5];

testMe.length; // 3
list.length; // 5

Q3:typeof 檢測的對象是誰?

再次強調,變數沒有型別,變數可在不同時間點持有不同型別的值,因此,只有「值」才有型別。雖然我們可用 typeof 檢測某個變數所儲存的值的型別,但記得並不是檢測變數本身,而是變數所存的值。

const name = 'Jack';
typeof name; // 'string'

Q4:辨識物件子型別的方法?

稍後在 Natives(原生功能)的部份會說明取得物件內部分類的方法,這裡就先大略提一下。

物件型別的值其內部有一個 [[Class]] 屬性來標記這個值是屬於物件的哪個子分類,雖然無法直接取用,但可透過 Object.prototype.toString 間接取得,範例如下。

Object.prototype.toString.call([1, 2, 3]); // "[object Array]"
Object.prototype.toString.call({ name: 'Jack' }); // "[object Object]"
Object.prototype.toString.call(function sayHi() {}); // "[object Function]"
Object.prototype.toString.call(/helloworld/i); // "[object RegExp]"
Object.prototype.toString.call(new Date()); // "[object Date]"

未定義(undefined)vs 未宣告(undeclared)

總結就是,無論變數是未定義或未宣告,typeof 這兩種狀況皆會得到 'undefined'

那麼,對未宣告的變數做 typeof 而得到 'undefined',有什麼用處呢?(*´・д・)?

對未宣告的變數做 typeof

對未宣告的變數做 typeof 而得到 'undefined' 可說是一種保護措施,可避免瀏覽器丟出 ReferenceError 的錯誤訊息,在撰寫測試特定條件時常會用到。

範例如下,我們可能在非正式環境下會引用了某支 js 檔案,其中會設定 DEBUG 為 true,而其他檔案會根據 DEBUG 變數是否被宣告或宣告並設定為 true 時做出相對應的事情。

if (typeof DEBUG !== 'undefined') {
  // start to debug...
}

或著,我們也可以不用 typeof 的作法,而改用檢測 window 屬性的方式,同樣也不會丟出 ReferenceError。

if (typeof window.DEBUG) {
  // start to debug...
}

再或者,使用依存性注入(dependency injection)的方式也是可以的,將要檢測的條件當參數傳入函式,若條件不存在則使用預設值。

function doSomethingCool(DEBUG) {
  var helper =
    DEBUG ||
    function () {
      /* 預設值 */
    };
  // ...
}

只是這解法已被 ES6 的預設傳入參數(Default Paramaters)取代了。

回顧

看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到…

References


同步發表於2019 鐵人賽


You-Dont-Know-JS javascript array javascript 2019鐵人賽 你所不知道的JS 你懂JavaScript嗎? 鐵人賽 You-Dont-Know-JS-Types-and-Grammar ReferenceError undefined NaN 你懂 JavaScript 嗎?2019 iT 邦幫忙鐵人賽 系列文