【深入理解JavaScript系列】继承

109 阅读2分钟

最近在复习JS,此系列文章仅作为笔记,如有错误欢迎评论交流

1.原型链继承

function Parent() {
  this.name ='kevin'
}
Parent.prototype.getName = function () {
  console.log(this.name);
}
function Child() {
}
Child.prototype = new Parent()
const child = new Child()
child.name = 'felix'
child.getName() // felix

缺点

  • 函数不能传参
  • 引用类型的属性会被所有实例【共享】

2.构造函数继承(经典继承)

function Parent(name) {
  this.name =name
}
function Child(name) {
  Parent.call(this,name)
}
const child1 = new Child('felix')
const child2 = new Child('tom')
console.log(child1.name); // felix
console.log(child2.name); // tom

优点

  • 可以传参
  • 避免了引用类型的属性被共享 缺点
  • 如果构造函数有方法,那么每次创建实例都会被创建一次

3.组合继承

原型链继承和构造函数 1 + 1

function Parent(name) {
  this.name =name
}
function Child(name) {
  Parent.call(this,name)
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
const child1 = new Child('felix')
const child2 = new Child('tom')
console.log(child1.name); // felix
console.log(child2.name); // tom

融合原型链继承和构造函数继承的优点,是javascript中最常用的继承方式

4.原型式继承

function createObj(o) {
  function F(){}
  F.prototype = o;
  return new F();
}
var person = {
  name: 'felix',
  friends: ['tom',]
}

var person1 = createObj(person);
var person2 = createObj(person);
person1.name = 'person1';
console.log(person2.name); // felix
person1.friends.push('taylor');
console.log(person2.friends); // ["tom", "taylor"]

修改 person1.name 的值,person2.name 的值并未发生改变,并不是因为 person1person2 有独立的 name 值,而是因为 person1.name = 'person1' ,给 person1 添加了 name 值,并非修改了原型上的 name 值。

5.寄生式继承

function createObj(fn) {
  const clone = Object.create(fn)
  clone.sayName = function () {
    console.log('hi');
  }
  return clone
}

缺点

  • 每次创建对象都会创建一遍方法

6.寄生组合式继承

先来看一下我们之前写的组合继承

function Parent(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
  console.log(this.name);
}
function Child(name) {
  Parent.call(this,name)
}
Child.prototype = new Parent()
const child1 = new Child('felix')
child1.getName()

组合继承最大的缺点是会调用两次父构造函数。

一次是在设置子类型实例原型的时候 Child.prototype = new Parent()

二是在创建子类实例的时候 Parent.call(this,name)

在这里我们打印 child1Child 会发现,Child.prototypechild1 中都有 colors 属性,值为['red', 'blue', 'green'] 那么如何避免重复调用呢?

其实只要有一个中间函数作为跳板就可以,下面看代码。

function Parent(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
  console.log(this.name);
}
function Child(name) {
  Parent.call(this,name)
}
const F = function () {}
F.prototype = Parent.prototype
Child.prototype = new F()
const child1 = new Child('felix')
child1.getName()

最后我们可以封装一下这个方法

function object(prototype) {
  function F() {  }
  F.prototype = prototype
  return new F()
}
function prototype(child,parent) {
  const prototype = object(parent.prototype)
  prototype.constructor = child
  child.prototype = prototype
}

使用

prototype(Child,Parent)

参考

JavaScript深入之继承的多种方式和优缺点