instanceof 运算符的实现原理及实现

263 阅读2分钟

instanceof 运算符的实现原理及实现

instanceof 运算符在 JavaScript 中用于判断一个对象是否属于某个构造函数的实例。它的实现原理主要依赖于原型链(prototype)。

原理

  1. 原型链

    • 每个 JavaScript 对象都有一个 __proto__ 属性,指向创建该对象的构造函数的原型对象。
    • 原型对象也有自己的 __proto__,形成一个链,直到达到 null,这个链被称为原型链。
    • 当试图访问一个对象的属性时,JavaScript 会沿着原型链向上查找,直到找到该属性或到达链的末端(null)。
  2. instanceof 工作方式

    • instanceof 检查目标对象的原型链中是否存在指定构造函数的原型。
    • 如果存在,返回 true;否则,返回 false

以下是一个简单的 instanceof 实现,模拟了 JavaScript 内部的工作逻辑: 以下是一个简单的 instanceof 实现,模拟了 JavaScript 内部的工作逻辑:

function instanceofOperator(target, constructor) {
  let proto = target.__proto__;
  
  while (proto !== null) {
    if (proto === constructor.prototype) {
      return true;
    }
    proto = proto.__proto__;
  }

  return false;
}

// 示例
function Person() {}
let person = new Person();

console.log(person instanceof Person); // 使用内置的 instanceof: true
console.log(instanceofOperator(person, Person)); // 使用自定义的 instanceof: true

注意instanceof 运算符不能用来判断基本数据类型(原始数据类型)的原因在于它的设计和工作原理。基本数据类型(如 StringNumberBooleanNullUndefinedSymbol)在 JavaScript 中是直接存储在栈内存中的,它们没有原型(prototype),因此也就不存在原型链。而 instanceof 是用来检查一个对象的原型链上是否存在指定构造函数的原型。由于基本数据类型没有实例化的概念,它们不是通过构造函数创建的,所以不能使用 instanceof 来检查。

例如,当你尝试使用 instanceof 检查一个基本数据类型的值时,如:

let num = 42;
console.log(num instanceof Number); // 输出 false

但是,如果你尝试使用基本数据类型原型与原型对象进行比较,如:

let n = 1;
console.log(n.__proto__ === Number.prototype);

这段代码实际上会在运行时创建一个临时的 Number 包装对象,然后访问其 __proto__。因为 Number 包装对象的原型是 Number.prototype,所以输出结果是 true。但是,需要注意的是,这个 __proto__ 并不属于原始的 n,而是临时转换得到的包装对象的特性。

在严格模式下("use strict"),尝试访问基本类型值的 __proto__ 可能会抛出错误,因为在严格模式下,JavaScript 引擎对这种隐式类型转换更加严格。在非严格模式下,这种行为是允许的,但并不推荐,因为它可能导致混淆和意外的结果。