【JS】instanceof 和 typeof 的使用

158 阅读3分钟

instanceoftypeof

instanceof

instanceof 用于检查一个对象是否是某个构造函数的实例。换句话说,它会检查对象的原型链上是否存在该构造函数的 prototype 属性。 示例代码

let a = new Number(1)
console.log(a instanceof Number);  // true
console.log(a.__proto__.constructor === Number) // true
console.log(a.__proto__ === Number.prototype) // true
console.log('------')
let b = 1
console.log(b instanceof Number); // false
console.log(b.__proto__.constructor === Number) // true
console.log(b.__proto__ === Number.prototype) // true (临时包装对象)

按照上面的说法
x instanceof Y 检查 x 的原型链上是否有 Y.prototype
可以等效为 x.__proto__ === Y.prototype (但是又不完全等效,因为instanceof会在整个原型链上递归查找)
如果我们仅看这个简单的等效,对比上面的4、9行代码。
a是对象,b是原始类型。严格来说,原始类型是没有__proto__的,但是JS引擎会在访问他们的属性的时候,临时包装成对象,使其看起来有__proto__,所以在第9行,还是会输出 true
所以这里为什么第7行,输出是false呢,不是按照上面的规则来,就检查x.__proto__ === Y.prototype 吗,既然第9行为true,但是第7行为false呢?
这里就涉及到另外一条规则了,如果x是原始类型,那么会直接返回false,因为原始类型没有原型链,上面的第9行是包装之后才有了原型链。

工作原理

x instanceof Y 的完整行为:

  1. 如果 x 是原始类型(如 1"a"true),直接返回 false(因为原始类型没有原型链)。
  2. 如果 x 是对象,则沿着 x 的原型链向上查找,检查是否有 Y.prototype
    • 先检查 x.__proto__ === Y.prototype,如果是,返回 true
    • 如果不是,继续检查 x.__proto__.__proto__ === Y.prototype,依此类推,直到原型链尽头(null)。
class Animal {}
class Dog extends Animal {}

const dog = new Dog();
console.log(dog instanceof Dog);     // true
console.log(dog instanceof Animal);  // true(因为 Dog 继承 Animal)
console.log(dog instanceof Object);  // true(所有对象最终继承 Object)

instanceof 的实现

function myInstanceof(left, right) {
    if (left === null || typeof left !== 'object') return false // 不是对象或null
    if (typeof right !== 'function' || !right.prototype) {
        // 对于JS中的函数,typeof 返回 'function'
        // 但是对于其他对象,typeof 返回 'object'
        // 这里我们需要判断 right 是否是一个函数
        throw new TypeError('Right-hand side of instanceof is not callable');
    }
    let proto = Object.getPrototypeOf(left) // 获取对象的原型
    let prototype = right.prototype // 获取构造函数的原型
    while (proto) {
        if (proto === prototype) return true // 找到原型链上的prototype
        proto = Object.getPrototypeOf(proto) // 继续向上查找原型链
    }
    return false
}

typeof

用来返回变量的基本类型,以字符串的形式返回,且不会检查原型链

console.log(typeof 42);           // "number"
console.log(typeof "hello");      // "string"
console.log(typeof true);         // "boolean"
console.log(typeof undefined);    // "undefined"
console.log(typeof null);         // "object"(历史遗留 bug)
console.log(typeof {});           // "object"
console.log(typeof []);           // "object"(数组也是对象)
console.log(typeof function() {}); // "function"
console.log(typeof Symbol());     // "symbol"
console.log(typeof 123n);         // "bigint"

其中数组、对象、null都会被判断为object。函数也是对象,但是typeof对其进行了特殊处理,返回了function。

  • typeof null === "object"
    这是 JavaScript 早期的一个 Bug,但由于历史原因无法修复。
  • typeof [] === "object"
    数组本质是对象,无法直接区分数组和普通对象(可以用 Array.isArray() 判断)。
  • typeof function() {} === "function"
    函数虽然是对象,但 typeof 对其特殊处理,返回 "function"

总结

操作符适用场景不适用场景
typeof检查原始类型、undefinedfunction无法区分对象的具体类型(如数组 vs 普通对象)
instanceof检查对象是否是某个类的实例(包括继承)不适用于原始类型

推荐组合使用:

  • 先 typeof 判断是否是原始类型。
  • 如果是对象,再用 instanceof 或 Array.isArray() 进一步判断。