本文主要會談到
「型別」是固有的、內建的特徵,能唯一識別特定值的行為。例如:數字 123 和字串 '123' 就是不一樣的,數字 123 可做數學運算處理,而字串 '123' 可能就是做些顯示到畫面上的操作。
JavaScript 定義了以下七種內建型別
'Hello World'
。{ name: 'Jack' }
、[1, 2, 3]
、function foo() { ... }
其中,這些內建型別又可分為兩大類-基本型別(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'
這裡會看到幾個有趣的(奇怪的)地方...
typeof null
卻得到 object,而非 null!這可說是一個 bug,可是若修正了這個 bug 則可能會導致很多網站壞掉,因此就不修了!typeof function() {}
是得到 function 而非 object,和陣列依舊得到 object 是不一樣的。另外,順道一提,函式是一種「可呼叫的物件」(callable object),它擁有 [[Call]]
的內部特性,讓它成為能夠被調用的物件。typeof NaN
結果就是 number,不要被字面上的意思「不是數字」(not a number)給弄糊塗了。另外,NaN 與任何數字運算都會得到 NaN,並且 NaN 不大於、不小於也不等於任何數字,包含 NaN 它自己。看到這裡是不是覺得 JavaScript 很難懂呢? (/‵Д′)/~ ╧╧
好的,在翻桌之前,先來解決剛剛提到的幾個問題。
先回顧一下之前提到的「Truthy & Falsy」的概念,在做比較時會被轉型為 false 的值有
""
空字串而除了以上之外,都會被轉為 true,舉例如下
'Hello World'
非空字串[], [1, 2, 3]
陣列,不管是不是空的{}, { name: 'Jack' }
物件,不管是不是空的function foo() {}
函式咦?是不是看到一線生機?
我們可利用 null 會被 typeof 檢測為 object 並且會轉為 false 的結果來驗證值是否為 null。
const happy = null;
if (!happy && typeof happy === 'object') {
console.log('我是 null!');
}
得到「我是 null!」,輕鬆解決,得分!
函式的 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
再次強調,變數沒有型別,變數可在不同時間點持有不同型別的值,因此,只有「值」才有型別。雖然我們可用 typeof 檢測某個變數所儲存的值的型別,但記得並不是檢測變數本身,而是變數所存的值。
const name = 'Jack';
typeof name // 'string'
稍後在 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'
。'undefined'
。總結就是,無論變數是未定義或未宣告,typeof 這兩種狀況皆會得到 'undefined'
。
那麼,對未宣告的變數做 typeof 而得到 'undefined'
,有什麼用處呢?(*´・д・)?
對未宣告的變數做 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)取代了。
看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到...
""
空字串、0、-0、NaN、null、undefined 和 false,其餘都會被轉為 true。Object.prototype.toString
可用來檢測值是屬於物件的哪個子分類。'undefined'
。對未宣告的變數做 typeof 而得到 'undefined'
的結果可作為具有保護性的檢測措施,在撰寫測試特定條件時常會用到。同步發表於部落格。
感謝分享~~
好強平常 null 到底用在哪??
因為這是人為的程式不會丟null出來啊
我會用到的地方有...
!a
可阻擋 a 為 null、undefined、空字串、false 等串接 API 時遇到某欄位沒資料(就給我 null)或沒丟這個欄位(就是 undefined)或被亂丟資料。{
"level": 1,
"specs": {
"temp_77965072": {
"spec_id": "temp_77965072",
"spec_name": "紅色",
"spec_num": "10",
"spec_status": "Y",
"spec_ext": {
"goods_no": "1234567",
"goods_note": "需要補貨時請洽小明 02-27123456"
}
},
"temp_76736337": {
"spec_id": "temp_76736337",
"spec_name": "藍色",
"spec_num": "20",
"spec_status": "Y",
"spec_ext": null
}
}
}