基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

instanceof 操作符的运行原理是什么?

知识点图片

instanceof 操作符是 JavaScript 中用于检测数据类型的常用方法,特别是用于判断一个对象是否属于某个构造函数(类)的实例

它的核心运行原理可以用一句话概括:检查右侧构造函数的 prototype 属性,是否出现在左侧对象的原型链(Prototype Chain)上。

下面为您详细拆解它的运行机制、底层逻辑以及如何手写实现。


1. 基本语法

javascript
object instanceof constructor
// L instanceof R (L 代表左侧对象,R 代表右侧构造函数)

2. 核心运行步骤(底层逻辑)

当 JavaScript 引擎执行 L instanceof R 时,它会执行以下步骤:

  1. 获取右侧的 prototype 拿到构造函数 R 的显式原型对象,即 R.prototype
  2. 获取左侧的原型: 拿到对象 L 的隐式原型,即 L.__proto__(标准写法为 Object.getPrototypeOf(L))。
  3. 比对与遍历原型链:
    • 判断 L.__proto__ 是否等于 R.prototype
    • 如果相等,则返回 true
    • 如果不相等,则沿着 L 的原型链继续往上找,即将 L.__proto__.__proto__ 作为新的对比目标,再次与 R.prototype 对比。
    • 一直循环这个过程,直到原型链的尽头(null)。如果到了 null 还没找到相等的原型,则返回 false

3. 手写实现 instanceof

为了更直观地理解,我们可以用代码模拟 instanceof 的底层查找过程:

javascript
function myInstanceof(left, right) {
    // 1. 基础数据类型直接返回 false (instanceof 只能判断对象)
    if (typeof left !== 'object' || left === null) {
        return false;
    }

    // 2. 获取右侧构造函数的 prototype 属性
    let rightPrototype = right.prototype;
    
    // 3. 获取左侧对象的 __proto__ (隐式原型)
    let leftProto = Object.getPrototypeOf(left);

    // 4. 开启循环,沿着原型链向上查找
    while (true) {
        // 如果找到原型链尽头 (Object.prototype.__proto__ 是 null),说明没找到
        if (leftProto === null) {
            return false;
        }
        // 如果左右原型相等,说明找到了,返回 true
        if (leftProto === rightPrototype) {
            return true;
        }
        // 如果没找到,继续沿着原型链向上获取下一层原型
        leftProto = Object.getPrototypeOf(leftProto);
    }
}

// 测试:
let arr = [];
console.log(myInstanceof(arr, Array));  // true
console.log(myInstanceof(arr, Object)); // true

4. 原型链查找图解示例

[] instanceof Object 为例:

  1. left[]rightObject
  2. 第一次对比:[].__proto__ 指向 Array.prototype。它与 Object.prototype 不相等。
  3. 沿着原型链往上走:查找 Array.prototype.__proto__
  4. 第二次对比:Array.prototype.__proto__ 指向 Object.prototype。它与 Object.prototype 相等
  5. 查找结束,返回 true

5. instanceof 的局限性与特殊情况

  • 无法准确判断基本数据类型:
    instanceof 只能用于对象。对于字面量创建的基本类型,它会返回 false
    javascript
    "hello" instanceof String; // false
    new String("hello") instanceof String; // true
  • 原型链被修改会导致判断不准:
    如果手动修改了对象的 __proto__ 或者构造函数的 prototypeinstanceof 的结果会随之改变。
    javascript
    function Foo() {}
    let f = new Foo();
    Foo.prototype = {}; // 改变了构造函数的 prototype
    console.log(f instanceof Foo); // false,因为 f.__proto__ 还是指向原来的那个原型对象
  • 多全局环境(iframe)问题:
    在浏览器中,如果你把一个 iframe 里的数组传给父窗口,在父窗口中使用 arr instanceof Array 会返回 false。因为不同的 iframe 拥有各自独立的全局对象和构造函数(父窗口的 Array 和 iframe 里的 Array 内存地址不同)。

6. ES6 的扩展:Symbol.hasInstance

在 ES6 中,JavaScript 允许通过自定义 Symbol.hasInstance 方法来更改 instanceof 的默认行为:

javascript
class MyClass {
    // 静态方法,重写 instanceof 的行为
    static [Symbol.hasInstance](obj) {
        return obj.isMyClass === true;
    }
}

let testObj = { isMyClass: true };
console.log(testObj instanceof MyClass); // true,尽管 testObj 的原型链上并没有 MyClass.prototype

实际上,L instanceof R 运算在现代 JS 引擎中的第一步,是检查 R 是否有 [Symbol.hasInstance] 方法,如果有,就调用它并返回它的强制布尔结果;如果没有,才会降级去走上面提到的原型链比对逻辑。

00:00
00:00