JavaScript 复习笔记 - 继承

130 阅读3分钟

继承

  • 原型链继承
  • 借用构造函数继承
  • 组合继承
  • 原型继承
  • 寄生式继承
  • 寄生组合继承
  • ES6 的 extends

原型链继承

// 继承的本质就是重写原型链
function SuperType() {
  this.superName = 'superName'
  this.colors = ["red", "blue", "green"]
}

SuperType.prototype.getSuperName = function() {
  return this.superName;
}

function SubType() {}
// 关键:创建 SuperType 实例并赋值(重写)给 SubType 原型对象(prototype)
SubType.prototype = new SuperType()

const instance = new SubType()
instance.getSuperName() // 'superName'

// 缺陷:多个实例的引用类型属性指向相同的内存,存在篡改的可能
const instance2 = new SubType();
instance2.colors.push('yellow')
instance.colors // ["red", "blue", "green", "yellow"]
instance.colors === instance2.colors // true

构造函数继承

function SuperType() {
  this.superName = 'superName'
  this.colors = ["red", "blue", "green"]
}
SuperType.prototype.getSuperName = function() {
  return this.superName;
}
function SubType() {
  // 关键 理解call
  SuperType.call(this)
}
const instance = new SubType()
instance.getSuperName === undefined // true
// 缺陷
// 1、只能继承父类的属性/方法,不能继承原型的属性/方法
// 2、每个实例都有父类函数的方法,无法复用,会影响性能

组合继承

// 组合继承 = 原型链继承 + 借用构造函数继承
function SuperType() {
  this.superName = 'superName'
  this.colors = ["red", "blue", "green"]
}
SuperType.prototype.getSuperName = function() {
  return this.superName;
}

function SubType() {
  // 借用构造函数继承
  SuperType.call(this)
}
// 原型链继承
SubType.prototype = new SuperType()
// 重写 constructor 指向 SubType
SubType.prototype.constructor = SubType 

const instance = new SubType()
const instance2 = new SubType()

instance.colors.push('yellow')

console.log(instance.colors) // ["red", "blue", "green", "yellow"]
console.log(instance2.colors) // ["red", "blue", "green"]

// 缺陷
// 原型上会存在和实例相同属性/方法

原型继承

let parent = {
  name: "Nicholas",
  colors: ["red", "blue", "green"],
  getName: function() {
    return this.name;
  }
}

/*
Object.create(parent)

function _create(obj) {
  const newObj = {}
  newObj.prototype = obj
  return newObj
}

Object.create 等价于 _create
*/

const son = Object.create(parent)
son.name = 'Greg'
const son1 = Object.create(parent)
son1.name = 'Linda'

console.log(son.name) // 'Greg'
console.log(son1.name) // 'Greg'

// 缺陷
// 1、创建原型的方法是浅拷贝,引用类型属性指向相同的内存,存在篡改的可能
// 2、无法传参
son1.colors.push('yellow')
console.log(son.colors) // ["red", "blue", "green", "yellow"]

son.getName === son1.getName // true

寄生式继承

let parent = {
  name: "Nicholas",
  colors: ["red", "blue", "green"],
  getName: function() {
    return this.name;
  }
}

// 封装原型,为原型新增属性和方法,以增强函数
function clone(obj) {
  let clone = Object.create(obj)
  clone.colors = function() {
    return this.colors
  }
  return clone
}

const son = clone(parent)
son.name = 'Greg'

// 缺陷和原型继承一致

寄生组合

// 组合继承
function Parent() {
  this.name = 'Nicholas'
  this.colors = ['red', 'blue', 'green']
}

// 支持传参
function Child(name, age) {
  Parent.call(this, name)
  this.age = age
}

// Object.create(Parent.prototype) 解决了原型上存在和实例相同属性/方法的缺陷
Child.prototype = Object.create(Parent.prototype) 
Child.prototype.constructor = Child

let person = new Child('Greg', 18)
let person2 = new Child('Linda', 19)

// 解决引用类型属性篡改问题
person.colors.push('orange') 
person2.colors.push('yellow')

console.log(person)
console.log(person2)

ES6 extends

就是寄生组合模式

function _extends(subType, superType){
  subType.prototype = Object.create(superType.prototype)
  subType.prototype.constructor = subType;

  Object.setPrototypeOf 
  ? Object.setPrototypeOf(subType, superType) 
  : subType.__proto__ = superType
}
function Parent(name) {
  this.name = name
}
Parent.prototype.getName = function() {
  return this.name
}
function Child(name, age) {
  Parent.call(this, name)
  this.age = age
}
// 继承
_extends(Child, Parent);
const child = new Child('Greg', 18)
const child1 = new Child('Linda', 19)

console.log(child.name, child.age, child.getName())
console.log(child1.name, child1.age, child1.getName())

// 好了,你会写继承了!!哈哈

总结

  • 原型链
    • 解决问题:
    • 存在缺陷:多个实例的引用类型属性指向相同的内存,存在篡改的可能
  • 借用构造函数继承
    • 解决问题:
    • 存在缺陷:只能父类的属性/方法,不能继承原型的属性/方法
  • 组合继承
    • 解决问题:
    • 存在缺陷:原型和实例会存在相同的属性和方法
  • 原型式继承
    • 解决问题:
    • 存在缺陷:多个实例的引用类型属性指向相同的内存,存在篡改的可能
  • 寄生式继承
    • 解决问题:
    • 存在缺陷:
  • 寄生组合式继承
    • 解决问题:
    • 存在缺陷:

参考