借用构造函数继承

115 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情

借用构造函数继承

借用继承 / call 继承

        通过改变 父类 构造函数的 this 指向来达到继承效果

        核心代码 在字类构造函数体内, 父类.call(字类的实例)

构造函数的执行

        1. 是一个普通函数, 可以当作函数直接调用

        2. 当作普通函数执行的时候, this 指向谁, 就向谁身上添加内容

        3. call 方法可以改变函数的 this 指向

借用继承的优缺点

        优点:

          1. 继承来的属性是在自己身上

          2. 我们一个实例化过程在一个位置传递参数

       **** 缺点:

          1. 只能继承父类构造函数体内的内容

          2. 父类原型上的内容不能继承

function Student(gender, name, age) {
this.gender = gender      
// Person('Jack', 18)     
// 使用 call 方法改变一下 Person 函数内部的 this 指向     
// 改变成指向谁, Person 就会向谁的身上添加一个 name 一个 age     
// Person.call('Jack', 18)     
// 这个位置的 this 指向 Student 的实例, 因为 new Student      
// Person 函数内部的 this 指向 Student 的实例 
this === s     
Person.call(this, name, age)      
// 这个函数执行完毕以后, 会像 Student 的实例身上添加一个 name 一个 age   
}
   
   Student.prototype.study = function () {  
   console.log('study')  
   }  
   const s = new Student('男', 'Jack', 18)  
   console.log(s)

  组合继承

把 原型继承 和 借用构造函数继承 合并在一起使用

组合继承的优缺点

     优点:

          1. 父类构造函数体内和原型上的内容都能继承

          2. 继承下来的属性放在自己身上

          3. 在一个位置传递所有参数

     缺点:

          1. 当你给字类添加方法的时候, 实际上是添加在了父类的实例身上

function Student(gender, name, age) {  
this.gender = gender
// 借用继承, 目的: 把属性继承在自己身上      
Person.call(this, name, age)    
}
// 原型继承, 目的: 继承父类原型上的方法   
Student.prototype = new Person()
// 书写属于 Student 自己的方法    
Student.prototype.study = function () {
console.log('study')
}
// 使用 Student 创建实例    
const s = new Student('男', 'Jack', 18)   
console.log(s)

拷贝继承

拷贝继承(for in 继承)

利用 for in 循环的特点, 来继承所有的内容

        先实例化一个父类的实例

        使用 for in 循环来遍历这个实例对象

          因为 for in 循环不光遍历对象自己, 还会遍历 proto

        直接把父类实例身上的所有内容直接复制到字类的 prototype

        核心代码

          const p = new Person('Jack', 18)

          for (var key in p) {

            console.log(key)

          }

拷贝继承的优缺点

      优点:

          1. 父类的构造函数体内的和原型上的都可以继承

          2. constructor 能正常配套

          3. 添加自己的方法的时候, 确实是在自己的原型身上

        缺点:

          1. for in 循环: for in 循环需要一直遍历到 Object.prototype

          2. 不能继承 不可枚举 的属性

          3. 继承来的属性不再自己身上

function Student(gender, name, age) {  
this.gender = gender
// for in 继承     
const p = new Person(name, age)    
for (let key in p) {        
Student.prototype[key] = p[key]  
}    }  
Student.prototype.study = function () {
console.log('study')
}  
const s = new Student('男', 'Jason', 18)  
console.log(s)

寄生继承

        是一种伪继承

        1. 构造函数不要写 return

          return 基本数据类型, 写了白写

          return 复杂数据类型, 构造函数没有意义

       核心代码

          const instance = new Person(name, age)

           return instance

function Student(name, age) {   
this.gender=  '男'
// 寄生继承   
const instance = new Person(name, age) 
return instance   }
// s 确实是 new Student 来的
// s 就是 Student 的实例, 但是真实的内容是 Person 的实例    
const s = new Student('Jason', 18)   
Student.prototype.study = function () {}   
console.log(s)

寄生继承 2

        出现了第二种寄生继承

        不直接寄生实例, 寄生原型

寄生继承的优缺点

        优点: (号称完美继承)

          1. 原型和构造函数体内的都能继承下来

          2. 寄生原型的话, 自己的属性和方法依旧可以添加和使用

      缺点:

          1. 寄生实例的时候, 没有自己的任何内容

          2. 寄生原型的时候, 一旦修改原型上, 父类的实例也会有这些方法

// 寄生继承2 
function Student(gender) {  
this.gender = gender    }
// 寄生原型 
Student.prototype = Person.prototype 
// 该自己的  
Student.prototype.stduy = function () {}  
const s = new Student('男')   
console.log(s)

 寄生式组合继承(完美继承)

        合并了 寄生继承 + 原型继承 + 独立第三方构造函数 + 借用继承

        核心代码

       

  (function () {     
  function Abc(name, age) {} 
  // 让 第三方构造函数 来寄生 父类 的原型   
  Abc.prototype = Person.prototype    
  Student.prototype = new Abc()  })();
  function Person(name, age) {
  this.name = name     
  this.age = age   
  }   
  Person.prototype.sayHi = function () {
  console.log('hello world') 
  }  
  function Student(gender, name, age) { 
  this.gender = gender 
  // 借用继承: 继承来了父类的属性     
  Person.call(this, name, age)  
  }    (function () {      
  function Abc(name, age) {}
  // 让 第三方构造函数 来寄生 父类 的原型    
  Abc.prototype = Person.prototype   
  const a = new Abc()    
  Student.prototype = a    })()
  // 当你去修改 Student 的 prototype 的时候
  // 相当于在修改 Abc 的实例, 和 Person 父类没有任何关系  
  Student.prototype.study = function () {
  console.log('study') }   
  const s = new Student('男', 'Jason', 18)  
  console.log(s)
  //从现在开始就是如下展示    
  // a = {    
  //  name: 'Jack',   
  //   age: 18,     
  //  __proto__: Person.prototype {  
  //   sayHi: function () {},    
  //     __proto__: Object.prototype    
  //   }    
  // }

另外 s 的属性

 此时: Student.prototype = {    
 name: 'Jason',       
 age: 18,         
 __proto__: Person.prototype {  
 sayHi: function () {},        
 __proto__: Object.prototype      
 }        
 }  
// 将来实例化 s 的时候     
 s = {      
 gender: '男',     
 __proto__: { 
 // 第三方构造函数的实例    
 name: 'Jason',            
 age: 18,            
 __proto__: Person.prototype {    
 sayHi: function () {},       
 __proto__: Object.prototype    
 }          
 }      
 }

ES6 类的继承

        ES6 把继承这个使用变成了 关键字

          1. extends

            class 字类类名 extends 父类 {}

          2. super()

            constructor 里面书写一个 super()

            super(name, age) 等价于 Person.call(this, name, age)

        注意:

          1. super 需要写在 constructor 里面

          2. 如果你要写自己的属性, 必须写在 super 后面

          3. ES6 的继承可以继承 ES5 的构造函数也可以继承 ES6 的类

function Person(name, age) {    
this.name = name     
this.age = age    
}   
Person.prototype.sayHi = function () {
console.log('hello world') 
}
class Student extends Person
{     
constructor (gender, name, age){  
super(name, age)       
this.gender = gender   
}     
study () {     
console.log('study')     
}  
}   
const s = new Student('男', 'Jack', 18)  
console.log(s)  
class Abc extends Student {     
constructor () {      
super('女', 'Rose', 20)  
}  
}   
const a = new Abc()   
console.log(a)

继承到这里就写完了 ,后面我们开始 jQuery.晚安