手写继承

606 阅读2分钟

首先什么是继承

简单来说就是 儿子可以拥有爸爸的属性和方法

有哪些常见继承

原型链继承 构造函数实现继承 组合继承 寄生式组合继承 class继承

原型链继承

代码

function farther() {
  this.wealth = ['汤臣一品','拉法','股票']
}

farther.prototype.getWealth = function () {
  return this.wealth
}

function son() {}
son.prototype = new farther()

var son1 = new son()
son1.wealth.push('汤臣一栋')
var son2 = new son()
console.log(son2.wealth) //  ['汤臣一品', '拉法', '股票', '汤臣一栋']

问题

  • 1:原型中的引用类型属性会被所有实例共享
  • 2:子类在实例化的时候不能给父类构造函数传参

构造函数继承

代码

function farther(wealth) {
  this.wealth = wealth
  this.getWealth = function() {
      return this.wealth
  }
}
function son(wealth) {
  farther.call(this, wealth)
}
son.prototype =  new farther()

var son1 = new son(['汤臣一品','拉法','股票'])
son1.wealth.push('汤臣一栋')
console.log(son1.wealth,'son1.wealth')
var son2 = new son(['小房子'])
console.log(son2.wealth,'son2.wealth') 

2.png

问题

  • 1:子类无法对父类的方法和属性进行扩展

组合继承

使用原型链继承原型上的属性和方法,将原型上的构造函数指向自身。
就可以实现子类对属性和方法扩展的功能。

代码


function father(wealth) {
  this.wealth = wealth
}
father.prototype.getWealth = function() {
  return this.wealth
}
function son(wealth, name) {
  father.call(this, wealth)
  this.name = name
}
son.prototype = new father()
son.prototype.constructor = son

let son1 = new son(['天空'],'张三')
son1.wealth.push('大地')
console.log(son1)
let son2 = new son(['悬空城'],'李四')
console.log(son2) 

3.png

问题

  • 1:调用了 2 次父类构造函数,第一次是在 new father(),第二次是在 father.call() 这里。会有一定的性能消耗。

寄生式组合继承

不直接调用父类构造函数给子类原型赋值,而是通过创建空函数fn获取父类原型的副本

两种写法

第一种写法


function father(wealth) {
  this.wealth = wealth
}
father.prototype.getWealth = function() {
  return this.wealth
}
function son(wealth, name) {
  father.call(this, wealth)
  this.name = name
}
function createObj(obj) {
  function fn() {}
  fn.prototype = obj
  return new fn()
}

function extend(child, parent) {
  let prototype = createObj(parent.prototype)
  prototype.constructor = child
  child.prototype = prototype
}
extend(son, father)

第二种写法

function farther(wealth) {
  this.wealth = wealth
}
farther.prototype.getWealth = function() {
  return this.wealth
}
function son(wealth, name) {
  farther.call(this, wealth)
  this.name = name
}

son.prototype =  Object.create(farther.prototype)
son.prototype.constructor = son

class继承

代码

class farther {
  constructor(wealth) {
      this.wealth = wealth
  } 
  getWealth() {
      return this.wealth
  }
}
class son extends farther {
  constructor(wealth, name) {
      super(wealth)
      this.name = name
  }
}

实战面试题

代码

function fn() {
  fn.a = function() {console.log(1)}
  this.a = function() {console.log(2)}
}

fn.prototype.a = function() {console.log(3)}

fn.a = function() {console.log(4)}


fn.a()

var obj = new fn() 
obj.a()

fn.a()

执行结果

4.png

分析

为什么是4-2-1
首先我们看代码执行顺序

fn.a()
显然执行 fn.a = function() {console.log(4)} 打印4

var obj = new fn() 实例化的时候执行
fn.a = function() {console.log(1)}
this.a = function() {console.log(2)}

obj.a()
this.a = function() {console.log(2)} 这个this就是obj
显然执行 2

fn.a()
在之前 var obj = new fn() 实例化的时候执行
外部fn.a {console.log(1)}
先找自身上面的属性方法才会去找原型链上面的属性方法
所以执行结果 4
如果没有 fn.a = function() {console.log(1)}
那执行结果就是 fn.prototype.a = function() {console.log(3)}