js继承

继承

原型链继承

  • 通过原型链prototype实现的继承:将Man的prototype属性指向Person的实例对象
function Person(name) {
  this.name = 'Person'
  this.children = []
}
function Man() {
  this.sex = 'male'
}
Man.prototype = new Person()
Man.prototype.constructor = Man
const Tom = new Man()
const Jack = new Man()
Tom.children.push(2)
console.log(Tom.children) // [2]
console.log(Jack.children) // [2]
子类继承到的父类的引用数据类型指向同一个内存地址,是所有子类的实例化对象共用的,一个子类的实例化对象修改会造成别的实例化对象同一个属性的值改变

构造函数继承

  • 通过构造函数constructor实现的继承:在Man函数中调用Person构造函数并将Person函数的this指向Man中的this,即将构造函数Person中添加到this上的属性和方法添加到Man的this上
function Person(name) {
  this.name = name
}
Person.prototype.getName = function () {
  return this.name
}
function Man() {
  Person.call(this) // 将Person函数的this指向当前环境的this并执行Person函数
  this.sex = 'male'
}
let man = new Man('Tom')
console.log(man)
console.log(man.getName()) // 报错
子类只实现了对父类实例的属性和方法的继承,没有实现对父类原型属性和方法的继承

组合继承

  • 通过prototype和构造函数constructor实现的继承:在Man中需要调用Person.call(this),然后将Man的prototype指向Person的实例化对象,然后将Man.prototype.constructor指向Man,因为构造函数的原型对象上的constructor应该指回到其本身
function Person(name) {
  this.name = name
  this.children = [1]
}
Person.prototype.getName = function () {
  return this.name
}
function Man(name) {
  Person.call(this, name) // 首次执行
  this.sex = 'male'
}
Man.prototype = new Person() // 二次执行
Man.prototype.constructor = Man
let Tom = new Man('Tom')
let Jack = new Man('Jack')
Jack.children.push(2)
console.log(Jack.children) // [1,2]
console.log(Tom.children) // [1]
console.log(Jack.getName()) // Jack
console.log(Tom.getName()) // Tom
执行两次Person,多了性能开销

原型式继承

  • 通过Object.create实现的继承 Object.create()函数的参数为父类,Object.create()的返回结果为子类,实现了父类的属性在子类的[[prototype]]属性,即__proto__属性上,其中__proto__上没有constructor
let Man = {
  name: 'Man',
  children: [1],
  getName: function () {
    return this.name
  }
}
let Jack = Object.create(Man)
Jack.name = 'Jack'
Jack.children.push(2)
let Tom = Object.create(Man)
Tom.children.push(2, 3)
console.log(Jack.name) // Jack
console.log(Tom.name) // Man
console.log(Jack.name === Jack.getName()) // true
console.log(Jack.children) // [1,2,3,4]
console.log(Tom.children) // [1,2,3,4]
多个实例存在继承自Man对象的值为引用类型的数据指向同一个引用地址的问题

寄生式继承

  • 通过一个函数,扩展Object.create的结果实现的继承
let animal = {
  name: 'Animal',
  kinds: ['dog', 'cat'],
  getName: function () {
    return this.name
  }
}
function clone(other, name) {
  let clone = Object.create(other)
  clone.getKinds = function () {
    return this.kinds
  }
  clone.name = name
  return clone
}
let dog = clone(animal, 'dog')
console.log(dog.getName()) // dog
dog.kinds.push('pig')
console.log(dog.getKinds()) // ['dog', 'cat', 'pig']
在animal的基础上进行了clone的扩展,但是没有解决引用类型数据指向同一个地址的问题

寄生组合式集成

  • 寄生组合式继承,通过操作prototype和Object.create和扩展函数共同实现的继承
// 定义一个实现继承关系的方法
function clone(animal, kind) {
  kind.prototype = Object.create(animal.prototype)
  kind.prototype.constructor = kind
}
// 定义一个父类的构造函数,具有name和kinds属性
function Animal(name) {
  this.name = name
  this.kinds = ['dog', 'cat']
}
// 定义父类原型上的方法
Animal.prototype.getName = function () {
  return this.name
}
// 定义一个子类Dog的构造函数,继承自父类,同时传入对应的子类name值Dog,子类具有自己的kind属性dog
function Dog() {
  Animal.call(this, 'Dog')
  this.kind = 'dog'
}
// 用clone实现父类和子类之间的继承
clone(Animal, Dog)
// 定义子类原型上的的方法
Dog.prototype.getDogKind = function () {
  return this.kind
}
// 构造子类的实例
let dog = new Dog()
console.log(dog) // {name: 'Dog', kinds: ['dog', 'cat'], kind: 'dog', [[prototype]]: {constructor: function Dog(){}, getDogKind: function(){}, [[prototype]: {getName: function(){}, constructor: function(){},[[prototype]]: Object}]}}
console.log(dog.getName()) // Dog
console.log(dog.getDogKind()) // dog
解决了原型链继承和原型式继承,寄生原型式继承引用数据指向同一个位置的问题,同时克服了构造函数式继承中没有继承到父类原型对象上的方法和属性的问题。

extends关键字实现class继承

  • js引擎会在预编译阶段
class Animal {
  constructor(name) {
    this.name = name
    this.kinds = ['dog', 'cat']
  }
  getName() {
    return this.name
  }
}
class Dog extends Animal {
  constructor(name) {
    super(name)
    this.kind = 'dogs'
  }
  getDogKind() {
    return this.kind
  }
}
class Cat extends Animal {
  constructor(name) {
    super(name)
    this.kind = 'cats'
  }
  getCatKind() {
    return this.kind
  }
}
const dog = new Dog('dog')
const cat = new Cat('cat')
console.log(dog) // {name: 'dog',kinds: ['dog','cat'],kind: 'dogs',[[prototype]]: {constructor: class Dog{}, getDogKind: function(){}, [[prototype]: {getName: function(){}, constructor: class Anmial{},[[prototype]]: Object}]}}
console.log(cat) // {name: 'cat',kinds: ['dog','cat'],kind: 'cats',[[prototype]]: {constructor: class Cat{}, getCatKind: function(){}, [[prototype]: {getName: function(){}, constructor: class Anmial{},[[prototype]]: Object}]}}

extends关键字实现的继承,其最终结果是和寄生组合式继承一致的,通过babel编译之后也可以看出extends使用的就是寄生组合式继承。js继承模式中,寄生组合式继承是最优的一种继承模式了。

posted on 2024-12-04 22:58  shenhf  阅读(13)  评论(0)    收藏  举报

导航