JavaScript 原型链解密和手写 instanceof

122 阅读3分钟

先上总结

JavaScript 通过原型和原型链机制,实现了面对对象中”继承“的思想

原型链主要属性

主要属性就是下面两种,我们一个个的讲:

  • prototype
  • __proto__

prototype 是什么

我们通过 Person 构造函数创建一个 person 实例:

function Person(name) {  // 构造函数
  this.name = name;
}

// Person 是个构造函数,所以有 prototype 属性,指向了一个原型对象
Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
};

// person.__proto__ 也指向了相同的原型对象
const person = new Person('John');

我们可以通过打印 Person.prototype 查看 Person 的原型,可以看到,原型就是一个对象

Pasted image 20230626112317.png

new Person 相当于把 Person 当做构造函数调用,实例化了 person 对象。 那么 Person 函数是怎么知道通过自己创建的实例都继承了哪些方法呢?就是通过 prototype 属性,这个属性指向了 Person 的原型对象,里面保存了 person 实例可以使用的方法和属性。

下图可以看到,Person.prototype 指向了一个原型对象,p1.__proto__ 也指向了这个原型对象:

image.png

只有函数有 prototype 属性,其他类型比如普通对象没有这个属性,因为其他类型不能当做构造函数使用,只有函数可以当做构造函数使用。

__proto__ 是什么

__proto__  是所有对象都有的属性。和 prototype 一样,__proto__ 指向的也是一个原型,同时某个构造函数的 prototype 属性也指向了这个原型。

我们可以通过打印这个对象,查看它的 __proto__ 属性:

Pasted image 20230626112036.png

原型链是由  __proto__  属性构成的单链表,最终指向  Object.prototype,也就是所有对象的最顶层原型:

image.png

__proto__ 隐式原型,为什么”隐式“ ?

为啥 __proto__ 叫隐式原型呢,因为__proto__ 这个名字不是官方标准定义的,而是所有浏览器都把它叫做 __proto__。所以不推荐直接使用 __proto__ ,而是使用Object.getPrototypeOf()

__proto__ VS prototype

prototype 是构造函数的属性,指向了这个构造函数的原型,比如 Person.prototype 指向了 Person 构造函数的原型,用这个构造函数实例化的对象,它们的 __proto__ 属性也指向了这个原型。

__proto__ 是对象的属性,用来形成原型链。比如,孙子对象的 __proto__ 指向了儿子对象的 __proto__ ,儿子对象的 __proto__ 指向了父对象的 __proto__ ,从而形成了原型链。

__proto__prototype 的关系: image.png

手写 instanceof,进一步理解原型链

instanceof 的原理是通过原型链查找符合要求的原型对象,搞明白原型链,就搞明白了instanceof 的原理。

function instance_of(obj, cls) {
  let clsProto = cls.prototype;  // 原型对象
  let objProto = obj.__proto__;  // 原型对象

  while (true) {
    // 一直顺着原型链找到 null 了都没有找到
    if (!objProto) {
      return false;
    }
    // 对象的原型链上存在一个原型对象,和构造函数的原型对象一样,说明对象是由这个构造函数创建的
    if (objProto === clsProto) {
      return true;
    }
    // 往原型链上继续走
    objProto = objProto.__proto__;
  }
}

function Person() {}
const p = new Person();
console.log(instance_of(p, Person)); // true
console.log(instance_of(p, Object)); // true
console.log(instance_of(p, String)); // false

Symbol.hasInstance 可以自定义 instanceof,这也是 instanceof 为什么不靠谱的原因。

Reference

developer.mozilla.org/en-US/docs/…

developer.mozilla.org/en-US/docs/…

javascript.info/prototype-i…