JS 类型判断

255 阅读3分钟

在 JavaScript 中,类型判断是一个常见的需求。以下是几种常用的类型判断方法及其适用场景:

 typeof 操作符

typeof 操作符可以用来判断大多数原始类型和函数类型,但对于 null 和数组等复杂类型的判断有些不足。

console.log(typeof 42);           // "number"
console.log(typeof 'hello');      // "string"
console.log(typeof true);         // "boolean"
console.log(typeof undefined);    // "undefined"
console.log(typeof Symbol());     // "symbol"
console.log(typeof BigInt(10));   // "bigint"
console.log(typeof function(){}); // "function"
console.log(typeof {});           // "object"
console.log(typeof []);           // "object"
console.log(typeof new Date());   // "object"
console.log(typeof null);         // "object" (这是一个历史遗留问题)

 instanceof 操作符

instanceof 操作符用于判断一个对象是否是某个构造函数的实例。它通过检查对象的原型链来进行判断。

// 原型链检查
console.log([] instanceof Array);          // true
console.log({} instanceof Object);         // true
console.log(function(){} instanceof Function); // true

// 在自定义类和继承关系中,`instanceof` 可以正确反映继承关系。
function Animal() {}
function Dog() {}
Dog.prototype = new Animal();

var dog = new Dog();
console.log(dog instanceof Dog);    // true
console.log(dog instanceof Animal); // true

// 假设在 iframe 中执行 
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const iframeArray = new iframe.contentWindow.Array();
console.log(iframeArray instanceof Array); // false
console.log(iframeArray instanceof iframe.contentWindow.Array); // true

// 可以通过重写 `Symbol.hasInstance` 方法来自定义 `instanceof` 的行为。
class MyClass {
    static [Symbol.hasInstance](instance) {
        return instance.customProperty === true;
    }
}
const obj = { customProperty: true };
console.log(obj instanceof MyClass); // true

总结

  • instanceof 通过检查对象的原型链来确定关系。
  • 手动修改原型链会影响 instanceof 的结果。
  • 在不同的 iframe 或窗口之间使用 instanceof 可能会失效。
  • instanceof 适用于检测对象的实例关系,而 typeof 适用于检测基本类型。
  • 可以通过重写 Symbol.hasInstance 方法来自定义 instanceof 的行为。

 Object.prototype.toString.call()

这是一个强大的方法,可以准确判断各种类型。它返回一个表示对象类型的字符串。

console.log(Object.prototype.toString.call(123)); // "[object Number]"
console.log(Object.prototype.toString.call('hello')); // "[object String]"
console.log(Object.prototype.toString.call(true)); // "[object Boolean]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call({})); // "[object Object]"
console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
console.log(Object.prototype.toString.call(function(){})); // "[object Function]"

此方法为什么能检测数据类型呢?起初我也很奇怪,只是记住了此方法,并未深究,今天让我们来一探究竟。

在介绍Object.prototype.toString方法之前,我们先把toString()方法和Object.prototype.toString.call()方法进行对比。

var arr=[1,2];
 
//直接对一个数组调用toString()
arr.toString();// "1,2"
 
//通过call指定arr数组为Object.prototype对象中的toString方法的上下文
Object.prototype.toString.call(arr); //"[object Array]"

数组对象上面的toString()调用到的实际是重写后的方法,并不是Object.prototype中的toString(),但是Object.prototype中的toString()也是被继承下来了。

当在Object.prototype(原型链上)调用 toString 方法时,执行以下步骤:

  1. 如果this 值是undefined,则返回[object Undefined]
  2. 如果this 值是null,则返回[object Null]
  3. 让 O 是通过使用 this 值作为参数调用 ToObject 所得到的结果。
  4. class 是 O 的内部属性 [[Class]] 的值。
  5. 返回连接三个字符串"[object", class"]"后的字符串值。

ToObject用于将非对象类型的值转换为对象。以下是如何将其描述为一个步骤:

  1. 如果 this 值是 undefined 或 null,抛出 TypeError 异常。
  2. 否则,将 this 值转换为对象并返回。

.call()的作用是重写this指针的指向,通过call()指定arr数组为Object.prototype对象中的toString方法的上下文,this指向的是arr,如果不加.call()的话,this指向为ObjectObject的数据类型自然是Object

 Array.isArray()

Array.isArray 方法专门用于判断一个值是否为数组。

console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray('hello')); // false

. 自定义类型判断函数

有时你可能需要自定义类型判断函数,结合以上方法来实现更复杂的类型判断逻辑。

function getType(value) {
  if (value === null) return 'null';
  if (typeof value === 'undefined') return 'undefined';
  if (typeof value === 'object') {
    if (Array.isArray(value)) return 'array';
    if (value instanceof Date) return 'date';
    if (value instanceof RegExp) return 'regexp';
    return 'object';
  }
  return typeof value;
}

console.log(getType(123)); // "number"
console.log(getType('hello')); // "string"
console.log(getType(true)); // "boolean"
console.log(getType(undefined)); // "undefined"
console.log(getType(null)); // "null"
console.log(getType([])); // "array"
console.log(getType({})); // "object"
console.log(getType(new Date())); // "date"
console.log(getType(/abc/)); // "regexp"
console.log(getType(function(){})); // "function"

总结

  • typeof 适用于大多数原始类型和函数类型的判断。
  • instanceof 适用于判断对象是否是某个构造函数的实例。
  • Object.prototype.toString.call() 是一种通用且强大的类型判断方法。
  • Array.isArray() 专门用于判断数组类型。
  • 自定义类型判断函数可以结合多种方法实现更复杂的类型判断逻辑。