Skip to content

继承

目的是让子类的实例也能实用父类的属性和方法

原型继承

通过修改子类的原型让子类的实例能通过原型链访问到父类公有的属性和方法

  • 不像其他语言的继承其他语言的继承一般都是拷贝继承, 也就是子类继承父类, 会把父类中的 属性和方法拷贝一份到子类中, 供子类的实例调取使用, 它是把父类的原型放在子类的原型链上, 实例想调取这些方法, 是基于原型链查找机制完成的(查找继承)

  • 子类可以重写父类中的方法, 会导致父类其他的子类也受影响(其他语言中由于是拷贝继承, 子类重写父类的方法并不会影响其他子类)

  • 父类中私有或公有的属性和方法, 最后都会变为子类中公有的属性和方法

js
function Foo () {}
Foo.prototype.foo = () => {
  console.log('foo')
}

function Bar () {}
Bar.prototype = new Foo() // => Bar.prototype.__proto__ === Foo.prototype
Bar.prototype.constructor = Bar // 记得修改 constructor 指向

const bar = new Bar()
bar.foo() // 调用父类的方法
js
function Foo () {}
Foo.prototype.foo = () => {
  console.log('foo')
}

function Bar () {}
Bar.prototype.__proto__ = Foo.prototype
js
function Foo () {}
Foo.prototype.foo = () => {
  console.log('foo')
}

function Bar () {}
Object.setPrototypeOf(Bar.prototype, Foo.prototype)
js
function Foo () {}
Foo.prototype.foo = () => {
  console.log('foo')
}

function Bar () {}
Bar.prototype = Object.create(Foo.prototype)
Bar.prototype.constructor === Bar

call 继承

子类中把父类当作普通函数执行,同时让父类中的 this 指向子类的实例,相当于给子类的实例 设置了很多私有的属性和方法

  • 只能继承父类私有的属性和方法(因为把父类当普通函数执行, 和其原型上的属性和方法没有关系)

  • 父类私有的属性或方法变为子类私有的属性或方法

js
function Foo (x) {
  this.x = x
}

Foo.prototype.sayFoo = function () {
  console.log('foo')
}

function Bar(y) {
  // this: Bar 的实例
  Foo.call(this, 100) // => bar.x = 100
}

const bar = new Bar()
bar.x // output: 100 
bar.sayFoo()
// output: TypeError: bar.sayFoo is not a function
// 因为只执行了 Foo(),没有 new。

寄生组合继承(call 继承 + 原型继承)

  • 父类私有和公有的属性和方法分别是子类私有和公有的属性和方法(推荐)
js
function Foo (x) {
  this.x = x
}

Foo.prototype.sayFoo = function () {
  console.log('foo')
}

function Bar (y) {
  Foo.call(this, 100) 
  this.y = y
}

Bar.prototype = Object.create(Foo.prototype)
Bar.prototype.constructor = Bar

Bar.prototype.sayBar = function () {
  console.log('bar')
}

const bar = new Bar()
bar.x // output: 100
bar.sayFoo() // output: foo
bar.sayBar() // output: bar

ES6 中的继承

  • 父类私有和公有的属性和方法分别是子类私有和公有的属性和方法,原理类似寄生组合继承。
js
class Foo {
  constructor (x) {
    this.x = x
  }

  sayFoo () {
    console.log('foo')
  }
}

class Bar extends Foo { 
  constructor (y) {
    super(100)
    this.y = y
  }

  sayBar () {
    console.log('bar')
  }
}

Bar.prototype.__proto__ = Foo.prototype // output: true

for...in

js
function Foo () {}
Foo.prototype.foo = () => {
  console.log('foo')
}

function Bar () {}

const bar = new Bar()
const foo = new Foo()

for (const key in foo) {
  bar[key] = foo[key]
}