js继承

337 阅读2分钟

原型链继承

原型链的基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。

function Father() {
  this.fatherName = 'Li Lei'
}
Father.prototype.getName = function() { console.log(this.name) }

function Children() {
  this.age = 18
}
// 原型链继承
Children.prototype = new Father()
Children.prototype.getAge = function() { console.log(this.age) }

let child1 = new Children()

// 输出实例和原型对象的属性:age、fatherName、getAge、getName
for (let key in child1) {
  console.log(key)
}
// 输出实例上的属性: ["age"]
console.log(Object.keys(child1))

以上代码Children继承了Father,而继承是通过创建Father的实例,并将该实例赋给Children.prototype实现的,实现的本质是重写原型对象。关系如下图所示:

image.png

  • 在使用原型链实现继承时,不能通过对象字面量创建原型方法,因为使用字面量创建原型方法会重写原型链

确定原型和实例的关系

  • instanceof instanceof用来测试实例与原型链中出现过的构造函数
console.log(child1 instanceof Object) // true
console.log(child1 instanceof Children) // true
console.log(child1 instanceof Father) // true
  • isPrototypeOf 只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
console.log(Object.prototype.isPrototypeOf(child1)) // true
console.log(Children.prototype.isPrototypeOf(child1)) // true
console.log(Father.prototype.isPrototypeOf(child1)) // true

原型链继承存在在问题

  • 父类包含引用类型的原型属性会被所有实例共享
  • 没有办法在不影响所有对象实例的情况下,给父类的构造函数传递参数

借用构造函数继承

在子类构造函数的内部调用超类构造函数。

function Father(name) {
  this.name = name
  this.colors = ['red', 'pink']
}

Father.prototype.getName = function() { console.log(this.name) }

function Children() {
  // 借用构造函数继承
  Father.call(this, 'wu')
}

let child1 = new Children()
child1.colors.push('blue')

// ["red", "pink", "blue"]
console.log(child1.colors)
// wu
console.log(child1.name)
// Error: child1.getName is not a function
child1.getName()

借用构造函数继承存在的问题

借用构造函数解决了引用类型属性共享问题和传递参数的问题,但是借用构造函数不能解决函数复用的问题,因为函数都在实例上,而且父类的原型中定义的方法,对子类而言也是不可见的。

组合继承

组合继承指的是将原型链和借用构造函数的技术组合到一块。

function Father(name) {
  this.name = name
  this.colors = ['red', 'pink']
}

Father.prototype.sayName = function() { console.log(this.name) }

function Children(name, age) {
  // 继承属性
  Father.call(this, name)
  
  this.age = age
}

// 继承方法
Children.prototype = new Father()

Children.prototype.sayAge = function() { console.log(this.age) }

let child1 = new Children('chen', 18)
let child2 = new Children('wu', 19)

child1.colors.push('blue')
child1.sayAge()
child1.sayName()

console.log(child1)
console.log(child2)

console.log(Object.keys(child1))

原型式继承

Object.create接收一个对象,然后该函数返回一个新对象,这个新对象将person作为原型,所以新对象的原型就包含了一个基本类型name属性和一个引用类型friends属性。

let person = {
  name: 'chen',
  friends: ['Li', 'Xu']
}

let person1 = Object.create(person)
let person2 = Object.create(person)

person1.age = 18

person1.friends.push('Yun')

// ["Li", "Xu", "Yun"]
console.log(person1.friends)
//  ["Li", "Xu", "Yun"]
console.log(person2.friends)

// ["age"]
console.log(Object.keys(person1))