JavaScript 型別檢查 (typeof / instanceof)

JavaScript 是動態型別語言,變數的型別可以在執行時改變。本篇介紹如何檢查值的型別。

typeof 運算子

typeof 運算子回傳一個字串,表示運算元的型別:

typeof 123; // 'number'
typeof 'hello'; // 'string'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof Symbol(); // 'symbol'
typeof 123n; // 'bigint'
typeof function () {}; // 'function'
typeof {}; // 'object'
typeof []; // 'object'
typeof null; // 'object' (歷史遺留的 bug)

typeof 的回傳值

型別typeof 結果
Number'number'
String'string'
Boolean'boolean'
Undefined'undefined'
Symbol'symbol'
BigInt'bigint'
Function'function'
其他物件(含 Array、null)'object'

typeof 的用途

檢查變數是否已定義

// 安全地檢查變數是否存在
if (typeof myVar !== 'undefined') {
  // myVar 已定義
}

// 直接使用可能會報錯
if (myVar !== undefined) {
  // ReferenceError(如果 myVar 未宣告)
}

檢查基本型別

function processValue(value) {
  if (typeof value === 'string') {
    return value.toUpperCase();
  } else if (typeof value === 'number') {
    return value * 2;
  } else if (typeof value === 'boolean') {
    return !value;
  }
}

typeof 的陷阱

null 的問題

typeof null; // 'object'(這是 JavaScript 的歷史 bug)

// 正確檢查 null
const value = null;
if (value === null) {
  console.log('是 null');
}

無法區分物件類型

typeof {}; // 'object'
typeof []; // 'object'
typeof new Date(); // 'object'
typeof /regex/; // 'object'

// typeof 無法區分這些不同的物件類型

instanceof 運算子

instanceof 用來檢查物件是否是某個建構函式的實例(檢查原型鏈):

const arr = [1, 2, 3];
const obj = { a: 1 };
const date = new Date();

arr instanceof Array; // true
arr instanceof Object; // true(Array 也繼承自 Object)
obj instanceof Object; // true
date instanceof Date; // true
date instanceof Object; // true

自訂建構函式

function Person(name) {
  this.name = name;
}

const mike = new Person('Mike');

mike instanceof Person; // true
mike instanceof Object; // true

instanceof 的限制

跨 frame/window 失效

在瀏覽器中,不同 frame 或 window 的 Array 是不同的建構函式:

// 從 iframe 取得的陣列
const iframeArray = iframe.contentWindow.Array;
const arr = new iframeArray(1, 2, 3);

arr instanceof Array; // false(因為是不同的 Array)

對基本型別無效

'hello' instanceof String; // false
123 instanceof Number; // false

// 只有包裝物件才會是 true
new String('hello') instanceof String; // true
new Number(123) instanceof Number; // true

Array.isArray()

最可靠的陣列檢查方法:

Array.isArray([1, 2, 3]); // true
Array.isArray('hello'); // false
Array.isArray({ length: 3 }); // false

// 即使跨 frame 也能正確判斷
Array.isArray(iframeArray); // true

Object.prototype.toString.call()

最通用的型別檢查方法,可以準確區分各種內建物件:

function getType(value) {
  return Object.prototype.toString.call(value);
}

getType(123); // '[object Number]'
getType('hello'); // '[object String]'
getType(true); // '[object Boolean]'
getType(undefined); // '[object Undefined]'
getType(null); // '[object Null]'
getType([]); // '[object Array]'
getType({}); // '[object Object]'
getType(function () {}); // '[object Function]'
getType(new Date()); // '[object Date]'
getType(/regex/); // '[object RegExp]'
getType(new Map()); // '[object Map]'
getType(new Set()); // '[object Set]'

可以包裝成更實用的函式:

function typeOf(value) {
  return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}

typeOf(123); // 'number'
typeOf([]); // 'array'
typeOf(null); // 'null'
typeOf(new Date()); // 'date'

constructor 屬性

每個物件都有 constructor 屬性,指向建立它的建構函式:

const arr = [];
const obj = {};
const str = 'hello';

arr.constructor === Array; // true
obj.constructor === Object; // true
str.constructor === String; // true
constructor 屬性可以被修改,所以不是最可靠的檢查方式。

實用的型別檢查函式

const is = {
  // 基本型別
  string: function (value) {
    return typeof value === 'string';
  },

  number: function (value) {
    return typeof value === 'number' && !isNaN(value);
  },

  boolean: function (value) {
    return typeof value === 'boolean';
  },

  undefined: function (value) {
    return typeof value === 'undefined';
  },

  null: function (value) {
    return value === null;
  },

  // 物件型別
  array: function (value) {
    return Array.isArray(value);
  },

  object: function (value) {
    return value !== null && typeof value === 'object' && !Array.isArray(value);
  },

  function: function (value) {
    return typeof value === 'function';
  },

  date: function (value) {
    return value instanceof Date && !isNaN(value);
  },

  regexp: function (value) {
    return value instanceof RegExp;
  },

  // 特殊檢查
  empty: function (value) {
    if (value === null || value === undefined) return true;
    if (Array.isArray(value) || typeof value === 'string') return value.length === 0;
    if (typeof value === 'object') return Object.keys(value).length === 0;
    return false;
  },

  numeric: function (value) {
    return !isNaN(parseFloat(value)) && isFinite(value);
  },
};

// 使用範例
is.string('hello'); // true
is.array([1, 2, 3]); // true
is.empty({}); // true
is.numeric('123'); // true

比較總結

方法優點缺點
typeof簡單、快速無法區分物件類型,null 回傳 'object'
instanceof可檢查原型鏈跨 frame 失效,基本型別無效
Array.isArray()準確檢查陣列只能檢查陣列
Object.prototype.toString.call()最準確、通用語法較冗長
constructor可以檢查自訂類型可被修改,不可靠

選擇建議

  • 檢查基本型別(string、number、boolean):使用 typeof
  • 檢查陣列:使用 Array.isArray()
  • 檢查自訂類別的實例:使用 instanceof
  • 需要精確區分內建物件類型:使用 Object.prototype.toString.call()