你懂 JavaScript 嗎?#4 型別(Types)
11 Oct 2018本文主要會談到
- 何謂「型別」?內建型別有哪些?常見疑難雜症與解法。
- 未定義(undefined)vs 未宣告(undeclared)。
何謂「型別」?
「型別」是固有的、內建的特徵,能唯一識別特定值的行為。例如:數字 123 和字串 ‘123’ 就是不一樣的,數字 123 可做數學運算處理,而字串 ‘123’ 可能就是做些顯示到畫面上的操作。
內建型別(Built-in Types)
JavaScript 定義了以下七種內建型別
- number 數字,例如:12345。
- string 字串,例如:
'Hello World'
。 - boolean 布林,例如:true、false。
- null
- undefined
- object 物件,例如:
{ name: 'Jack' }
、[1, 2, 3]
、function foo() { ... }
- symbol
其中,這些內建型別又可分為兩大類-基本型別(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'
這裡會看到幾個有趣的(奇怪的)地方…
- null 是基本型別之一,但
typeof null
卻得到 object,而非 null!這可說是一個 bug,可是若修正了這個 bug 則可能會導致很多網站壞掉,因此就不修了! - 雖然說 function 是物件的子型別,但
typeof function() {}
是得到 function 而非 object,和陣列依舊得到 object 是不一樣的。另外,順道一提,函式是一種「可呼叫的物件」(callable object),它擁有[[Call]]
的內部特性,讓它成為能夠被調用的物件。 - NaN 表示是無效的數字,但依舊還是數字,因此在資料型別的檢測
typeof NaN
結果就是 number,不要被字面上的意思「不是數字」(not a number)給弄糊塗了。另外,NaN 與任何數字運算都會得到 NaN,並且 NaN 不大於、不小於也不等於任何數字,包含 NaN 它自己。
看到這裡是不是覺得 JavaScript 很難懂呢? (/‵Д′)/~ ╧╧
好的,在翻桌之前,先來解決剛剛提到的幾個問題。
Q1:如何檢測 null?
先回顧一下之前提到的「Truthy & Falsy」的概念,在做比較時會被轉型為 false 的值有
""
空字串- 0, -0, NaN
- null
- undefined
- false
而除了以上之外,都會被轉為 true,舉例如下
'Hello World'
非空字串- 42 非零的有效數字
[], [1, 2, 3]
陣列,不管是不是空的{}, { name: 'Jack' }
物件,不管是不是空的function foo() {}
函式- true
咦?是不是看到一線生機?
我們可利用 null 會被 typeof 檢測為 object 並且會轉為 false 的結果來驗證值是否為 null。
const happy = null;
if (!happy && typeof happy === 'object') {
console.log('我是 null!');
}
得到「我是 null!」,輕鬆解決,得分!
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)
- 未定義(undefined):未賦值的變數所儲存的值是 undefined,對此變數做 typeof 也會得到
'undefined'
。 - 未宣告(undeclared):變數在未宣告並使用的狀況下會得到 ReferenceError,並指出該變數並未宣告;變數在未宣告並賦值的狀況下,在嚴格模式下會報錯 ReferenceError,而在非嚴格模式下,變數會成為全域變數的屬性。注意,對未宣告的變數做 typeof 也會得到
'undefined'
。
總結就是,無論變數是未定義或未宣告,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)取代了。
回顧
看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到…
- JavaScript 的內建七種型別有 number、string、boolean、null、undefined、object、symbol,其中…
- Truthy & Falsy 的轉換規則,會被轉為 false 的值有
""
空字串、0、-0、NaN、null、undefined 和 false,其餘都會被轉為 true。 - typeof 可用來識別值的資料型別;
Object.prototype.toString
可用來檢測值是屬於物件的哪個子分類。 - 函式的 length 是指參數個數,而陣列的 length 是指內部成員個數。
- Truthy & Falsy 的轉換規則,會被轉為 false 的值有
- 未定義(undefined)表示變數曾被宣告但值未被定義,此時值為 undefined;未宣告(undeclared)表示變數未曾被宣告過,而 typeof 這兩種狀況的變數皆會得到
'undefined'
。對未宣告的變數做 typeof 而得到'undefined'
的結果可作為具有保護性的檢測措施,在撰寫測試特定條件時常會用到。
References
同步發表於2019 鐵人賽。