原型和原型链

102 阅读3分钟

首先抛出三个问题

  • 什么是原型对象
  • 构造函数, 原型对象, 实例的三角关系图
  • 原型链如何形成

image-20210306104516852

原型对象

在 JavaScript 中,除去一部分内建函数,绝大多数的函数都会包含有一个叫做  prototype  的属性,指向原型对象,

基于构造函数创建出来的实例, 都可以共享访问原型对象的属性。

例如我们的  hasOwnPropertytoString  ⽅法等其实是 Obejct 原型对象的方法,它可以被任何对象当做⾃⼰的⽅法来使⽤。

hasOwnProperty  用于判断, 某个属性, 是不是自己的 (还是原型链上的)

来看一段代码:

let person = {
  name: 'Tom',
  age: 18,
  job: 'student',
}

console.log(person.hasOwnProperty('name')) // true
console.log(person.hasOwnProperty('hasOwnProperty')) // false
console.log(Object.prototype.hasOwnProperty('hasOwnProperty')) // true

可以看到,hasOwnProperty  并不是  person  对象的属性,但是  person  却能调用它。

那么  person  对象是如何找到 Object 原型中的  hasOwnProperty  的呢?这就要靠原型链的能力了。

需求: 简单绘制原型三角关系图!

原型链

在 JavaScript 中,每个对象中都有一个  __proto__  属性,这个属性指向了当前对象的构造函数的原型。

对象可以通过自身的  __proto__属性与它的构造函数的原型对象连接起来,

而因为它的原型对象也有  __proto__,因此这样就串联形成一个链式结构,也就是我们称为的原型链。

原型链的作用

原型链如此的重要的原因就在于它决定了 JavaScript 中继承的实现方式。当我们访问一个属性时,查找机制如下:

  • 访问对象实例属性,有则返回,没有就通过 __proto__ 去它的原型对象查找。
  • 原型对象找到即返回,找不到,继续通过原型对象的 proto 查找。
  • 一层一层一直找到 Object.prototype ,如果找到目标属性即返回,找不到就返回 undefined,不会再往下找,因为在往下找 __proto__ 就是 null 了。

通过原型链实现继承

寄生组合继承:是现在业内公认的比较可靠的JS  继承模式,ES6  的  class  继承在  babel  转义后,底层也是使用的寄生组合继承的方式实现的。

function inherit(Child, Parent) {
  Child.prototype = Object.create(Parent.prototype, {
    constructor: {
      value: Child,
      enumerable: false, // 不可枚举该属性
      writable: true, // 可改写该属性
      configurable: true, // 可用 delete 删除该属性
    },
  });
}

instanceof 的原理

instanceof用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

function _instanceof(l, R) {
  // 判断是不是对象或者函数(不包括null)
  const isObjectOrFunction = (target) => {
    return (typeof target === "object" && target !== null) || typeof target === "function";
  };
  if (!isObjectOrFunction(l)) {
    //原始值,直接返回false
    return false;
  } else {
    // 创建一个指针,指向l的隐式原型
    let p = l;
    // 构造函数的原型
    const prototype = R.prototype;
    while (p) {
      let proto = p.__proto__; //或者使用 Object.getPrototypeOf
      // 如果隐式原型===显示原型,则返回true
      if (proto === prototype) {
        return true;
      }
      // 沿着原型链执行,最终到null结束
      p = proto;
    }
    // 执行了到原型链终点,没有找到则返回false
    return false;
  }
}
  • 需要注意的是左边如果为原始值直接返回 false