JS 原型与继承

80 阅读3分钟

JS 原型与继承

参考来源:继承与原型链 - JavaScript | MDN (mozilla.org)

本文为个人学习记录,仅供参考

原型与原型链

在 JS 中,每个对象都有一个私有属性指向另一个名为原型 prototype的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链 prototype chain中的最后一个环节

PS: 对象的原型可以通过 __proto__ 属性访问,这个属性并非 ECMAScript 标准,不过众多 JS 引擎都实现了该属性

按照 ECMAScript 规范,应该使用 Object.getPrototyoeof()Object.setPrototypeof() 来访问对象原型

当访问对象上的属性时,JS 首先会检查它们是否直接存在于该对象上,如果不存在,则会顺着原型链查找,直至找到或者返回 undefined

函数的 prototype 属性

在 JS 中,每个函数都有一个属性 prototype,但其并非函数本身的原型

该属性值是一个对象,该对象中的 constructor 默认为函数本身

对于普通函数来说,该属性基本无用,但对于构造函数来说,构造实例的时候,便会将其分配给实例对象的原型

function A {}
A.prototype === A.__proto__ // false
A.prototype // { constructor: A }

const a = new A()
a.__proto__ === A.prototype // true

函数的构造函数为 Function,所以其原型指向 Function.prototype,其值是由 JS 引擎的提供,JS 无法解析

A.__proto__ === Function.prototype // true
Function.prototype // f () { [native code] }

JS 中的内置构造函数的原型和prototype 属性会有些特殊。但原型链的最后都指向 Object.prototype,而 Object.prototype 的原型指向 null

Object.__proto__ // f () { [native code] }
Function.__proto__ // f () { [native code] }

String.prototype == '' // true
String.prototype === '' // false
Array.prototype.map((x) => x) // []
Number.prototype == 0 // true
// ...

Function.prototype.__protot__ === Object.prototype // true
Object.__proto__.__proto__ === Object.prototype // true

Object.prototype.__proto === null // true

类与继承

JS 中没有传统意义上的类,ES6 的 class 本质是构造函数的语法糖

class A {}
A instanceof Function // true

创建对象实例

JS 中通过 new 操作符和构造函数来创建对象实例,大致步骤如下:

  1. 创建一个空对象
  2. 将这个空对象的原型,指向构造函数的 prototype 属性
  3. 将函数内部的 this 指向这个对象
  4. 执行构造函数内部的代码
  5. 如果构造函数没有返回对象,则返回 this

基于原型链的继承

根据原型的特点,我们可以根据实际情况编写合适的代码来定制继承方式

下面是最常见的一种继承方式,被称之为寄生组合继承

function Parent(value) {
  this.val = value
}
Parent.prototype.getValue = function() {
  console.log(this.val)
}

function Child(value) {
  // 1.调用父类构造函数,继承父类的属性
  Parent.call(this, value)
}
// 2.以父类 prototype 作为子类的 prototype
Child.prototype = Object.create(Parent.prototype)
// 3.将子类 prototype 中的 constructor 指向子类本身
Child.prototype.constructor = Child

const child = new Child(1)

child instanceof Child // true
child instanceof Parent // true
child.getValue() // 1

ES6 class 继承

使用 class 就非常方便了

class Parent {
  constructor(value) {
    this.val = value
  }
  getValue() {
    console.log(this.val)
  }
}

// 1.使用 extends 关键字表示继承
class Child extends Parent {
  constructor(value) {
    // 2.在构造函数中调用 super 方法,类似 Parent.call(this, value)
    super(value)
    this.val = value
  }
}

const child = new Child(1)
child instanceof Child // true
child instanceof Parent // true
child.getValue() // 1