● 在 JavaScript 内, 继承是面向对象变成中一个重要的开发思维, 外围就是让 A 对象应用 B 对象的属性和办法. 说白了, 就是为了资源整合, 让咱们的代码复用性更高, 前期的可维护性和可扩展性也更高.

● 在 JS 中, 每一个对象都有一个本人的原型对象, JS 内的继承就是依赖这个原型对象, 而且原型对象也有本人的原型对象, 就是这一个一个的原型对象组成了原型链, 这就是继承的外围.

● 举个例子 :

○ 有一位老父亲, 致力了半辈子, 盖了一个大房子

○ 这个时候, 不论这个老父亲有多少个儿子, 他的儿子都是能够住在这个房子外面的

○ 能够用这个房子所有内容


○ 然而如果要是没有继承的话, 那么他的每一个儿子都要本人去从新盖房子

● 这就是咱们继承的意义, 接下来咱们就具体的说一说 JavaScript 内常见的继承计划

回顾之前的知识点
原型
● 构造函数(类) 有一个本人的属性 prototype, 是一个对象

● 每一个对象都有一个本人的属性 __proto__, 指向所属构造函数的 prototype

● 这样, 就将构造函数和对象连接了起来

原型链
● 原型链就是 proto 串联起来的对象链状构造

● 每一个对象都会有本人的原型对象( proto )

原型链拜访规定
● 对象成员的拜访规定就是依照原型链的机制进行拜访的

● 当你拜访一个对象的成员的时候

○ 会首先在对象本人内查找

○ 如果本人有, 间接应用本人的

○ 如果本人没有, 就会主动去到本人的 proto 查找

○ 如果还没有, 在持续去 proto 查找

○ 以此类推

○ 直到 Object.prototype 都没有, 那么返回 null

常见继承计划

  1. 原型链继承

● 实质就是在原型链上做一些手脚, 让某一个对象的 proto 能指向另一个对象就好了

● 外围 : 子类的原型指向父类的实例

// 父类function Person(name) {  this.name = name}Person.prototype.sayHi = function () {  console.log('hello world')}// 子类function Student(age) {  this.age = age}// 实现继承Student.prototype = new Person('千锋大前端')const s = new Student(10)console.log(s)

● 毛病 :

○ 子类没有本人的原型, 因为子类的原型就是父类的实例

○ 在继承属性这方面, 所有子类实例对象继承下来的属性都是截然不同的

● 长处 :

○ 属性和办法都能继承下来

  1. 借用构造函数继承

● 实质就是把父类构造函数当做一个一般函数来应用, 在利用一些扭转 this 指向的办法

● 外围 : 利用 call 或者 apply 办法调用父类构造函数, 让其 this 指向子类的实例

// 父类function Person(name) {  this.name = name}Person.prototype.sayHi = function () {  console.log('hello world')}// 子类function Student(age, name) {  this.age = age  // 实现继承  Person.call(this, name)}const s = new Student(10, '千锋大前端')console.log(s)

● 毛病 :

○ 只能继承父类的属性, 原型上的办法不能继承

● 长处 :

○ 子类有本人的原型, 能够向本人的原型上增加专属于子类的内容

○ 对于继承的属性而言, 每一个子类的实例都会继承一套本人的属性

  1. 根底组合继承

● 实质就是把下面的 原型继承 和 借用构造函数继承 组合在一起应用而已

● 将以上两种继承形式的长处全副合并在一起, 号称最早的完满继承

// 父类function Person(name) {  this.name = name}Person.prototype.sayHi = function () {  console.log('hello world')}// 子类function Student(age) {  this.age = age  // 实现继承( 目标 : 继承属性 )  Person.call(this, name)}// 实现继承( 目标 : 继承办法, 属性不重要 )Student.prototype = new Person()const s = new Student(10, '千锋大前端')console.log(s)
  1. 寄生继承

● 实质就是是利用一个空对象来包装父类的实例对象,而后再将该空对象的原型设置为父类的原型对象,最初将该空对象返回作为子类的实例对象,从而实现继承。

function Parent(name) {  this.name = name;}Parent.prototype.sayHi = function() {  console.log('hello world')}function Student(parent, name) {  let student = Object.create(parent)  student.name = name  student.sayHello = function() {    console.log(`Hello, my name is ${this.name}`)  }  return student}let parent = new Parent('千锋大前端')let student = createChild(parent, '千锋大前端')student.sayHello()student.sayHi()
  1. 寄生组合继承

● 这是一个十分经典的继承计划, 他是联合了 寄生继承 和 根底组合继承 两者发明进去的. 并且解决了组合继承中反复调用构造函数的难堪, 同时也很好的防止了寄生继承中的原型链净化, 是我集体感觉十分优良得以中继承计划, 不晓得是哪一个大神想进去的计划.

● 在寄生组合继承中,咱们首先应用组合继承的形式创建对象和原型之间的关系,而后通过寄生继承的形式修改原型上不须要的属性

// 寄生继承实现函数function inheritPrototype(student, person) {  // 创立父类原型的一个正本  let prototype = Object.create(person.prototype)  // 修改正本的 constructor 属性  prototype.constructor = student  // 将修改后的原型赋值给子类  student.prototype = prototype}// 父类function Person(name) {  this.name = name}Person.prototype.sayHi = function() {  console.log('hello world')}// 子类function Student(age, name) {  this.age = age  // 借用继承( 目标 : 继承属性 )  Person.call(this, name)}// 寄生继承inheritPrototype(Student, Person)// 向子类本人的原型上增加办法Student.prototype.sayHello = function() {  console.log(`Hello, My name is ${ this.name }`);}let instance = new Student(10, '千锋大前端')instance.sayHello()instance.sayHi()
  1. ES6 的类继承语法

● 在 ES6 标准中, 呈现了类继承的语法

● 也就是说, 咱们能够不须要以上这些乌七八糟的玩意了, 间接上语法

● 咱们能够间接应用 extends 关键字进行父类的继承

○ 留神 : 还须要在类的结构器内写一个 super 来继承父类属性

// 父类class Person {  constructor(name) {    this.name = name;  }  sayHi () {    console.log('Hello world')  }}// 子类, 间接应用 extends 关键字继承父类class Student extends Person {  constructor(name, age) {    // 继承父类属性    super(name)    this.age = age  }  sayAge() {    console.log(`Hello, My name is ${ this.name }`)  }}let instance = new Student('千锋大前端', 10)instance.sayHello()instance.sayHi()