JavaScript:class继承 组合式继承 与 组合寄生式继承

245 阅读4分钟

constructor VS 构造函数 和 实例方法 VS prototype方法

class Person {
  constructor(name) {
    this.name = name
  }
  sayHello() {
    return 'hello, I am ' + this.name
  }
}
var brynn = new Person('Kevin')
// 相同点
console.log(brynn)
// 不同点
console.log(Object.keys(Person.prototype))
console.log(Object.getOwnPropertyNames(Person.prototype))

prototype上方法是否可枚举,class:否

function Person(name) {
  this.name = name
}

Person.prototype.sayHello = function () {
  return 'hello, I am ' + this.name
}
var brynn = new Person('Kevin')
// 相同点
console.log(brynn)
// 不同点
console.log(Object.keys(Person.prototype))
console.log(Object.getOwnPropertyNames(Person.prototype))

prototype上方法是否可枚举,function:是

是否可枚举是通过defineProperty方法实现的
setter/getter和value/writable:true不能共存

let brynn = {}
brynn.age = 21
Object.defineProperty(brynn, 'description', {
  enumerablefalse,
  get() => {
    if (brynn.age > 18return 'adult'
    else return 'teenager'
  },
  set(newSex) => {
    if (brynn.age > 20) brynn.description = newSex
  },
})
console.log(brynn)
console.log(Object.keys(brynn))
console.log(Object.getOwnPropertyNames(brynn))

实例属性 VS 构造函数

class Person {
    name = 'brynn' 
}
class Person {
  constructor(name) {
    this.name = name
  }
}
function Person(name) {
  this.name = name
}

静态方法和静态属性 && 构造函数的属性

class Person {
  constructor(name) {
    this.name = name
  }
  static sayHello() {
    return 'hello, I am ' + this.name
  }
  static age = 20
}
var brynn = new Person('Kevin')
console.log(brynn)
console.log(Person.sayHello()) // √
console.log(brynn.sayHello()) // ×
function Person(name) {
  this.name = name
}

Person.sayHello = function () {
  return 'hello, I am ' + this.name
}
Person.age = 20
var brynn = new Person('Kevin')
// 相同点
console.log(brynn)
console.log(Person.sayHello()) // √
console.log(brynn.sayHello()) // ×

setter/getter

class Person {
  constructor() {
    this.sex = 'female'
  }
  set name(newName) {
    console.log('查看this.name'this.name)
    console.log(newName)
  }
  get name() {
    return 'brynn'
  }
}
var brynn = new Person()
console.log(brynn)
brynn.name = 'brown'
function Person() {
  this.sex = 'female'
}
Person.prototype = {
  set name(newName) {
    console.log('查看this.name'this.name)
    console.log(newName)
  },
  get name() {
    return 'brynn'
  },
}
var brynn = new Person()
console.log(brynn)
brynn.name = 'brown'

注意,这里的getter和setter都是把属性放到构造函数的prototype上,对象上面并没有(虽然这里看到了),用brynn.hasOwnProperty('name')返回依然是false

entends VS 组合寄生式继承

class Parent {
  constructor(name) {
    this.name = name
  }
  getName() {
   console.log(this.name)
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name) // 调用父类的 constructor(name)
    this.age = age
  }
}

var child1 = new Child('brynn''20')

console.log(child1)
console.log(Child)
console.log(Child.prototype)

es6的extends对应的是组合寄生式继承,

function Parent(name) {
  this.name = name
}

Object.defineProperty(Parent.prototype'getName', {
  valuefunction () {
    console.log(this.name)
  },
  enumerablefalse,
})
// Parent.prototype.getName = function () {
//   console.log(this.name)
// }

function Child(name, age) {
  Parent.call(this, name)
  this.age = age
}

Child.prototype = Object.create(Parent.prototype, {
  constructor: {
    valueChild,
    enumerablefalse,
    writabletrue,
    configurabletrue,
  },
})

var child1 = new Child('brynn''20')

console.log(child1)
console.log(Child)
console.log(Child.prototype)

关键点是:

  • ES6的extends自动给子类的prototype添加一个指向子类本身的constructor属性,所以在ES5中模拟时也要添加上去(Object.create的第三个参数)
  • ES6的定义的实例方法都是不可枚举的(不能直接将函数方法添加到prototype上,要用defineProperty的value,并且不能用箭头函数,否则this的指向有问题)

对比组合式继承 组合式继承和组合寄生式继承都是将父类的prototype作为子类prototype的proto
区别是在这个过程中是否调用了父类的构造函数
组合继承调用了父类的构造函数,所以会在子类的原型链上添上一些不必要的属性

// 组合寄生继承
Child.prototype = Object.create(Parent.prototype, {
  constructor: {
    value: Child,
    enumerablefalse,
    writabletrue,
    configurabletrue,
  },
})
// 组合继承
Child.prototype = new Parent()