js类与继承

110 阅读5分钟

类的声明与实例化

类的声明有两种方式es5中的构造函数和es6的class。实例化的就相对比较简单,直接使用new就可以了。

如何实现继承

类的继承在es6中非常容易,使用extends与super方法就能实现。但在es5中实现起来就相对比较困难。

许多面向对象语言都支持两种继承的方式:接口继承和实现继承.接口继承只继承方法签名.而实现继承则继承实际
的方法.在JavaScript中由于函数没有签名也就无法实现接口继承.而只支持实现继承.而且实现继承主要通过原型链来实现的.

继承的几种方式

DNRUBMTX)E{H_L(MY0{@S.png

原型链继承

// 原型链继承
function FirstSon( ) {
this.name ='Tom'
}
// 将Son的原型指向到父类的实例
FirstSon.prototype = new Parent( )
var tom = new FirstSon( )
//调用父类的实例方法
tom.sum( )
//调用父类的原型方法
tom.say( )
// instanceof方法用于检查元素是否在某个元素的原型链上
//这里因为f irs tSon继承了Parent的属性,所以它的结果是t rue
console. log( tom instanceof Parent )
//修改子类的引用属性size
tom.size.push( 'aa' )
var jerry = new FirstSon('jerry')
jerry.sum( )
//因为共享了父类的引用属性,所以这里的size已经被修改
console. log( jerry.size )

原型链继承的重点:将子类的原型指向父类的实例
优点:简单明了.子类可以继承父类的属性.构造函数和原型.
缺点:
1.新实例无法向父类的构造函数传参
2.所有的新实例都会共享父类实例的属性(如果父类的实例属性是一个引用类型的话.所有的实例中继承到的属性将会指向同一个地址.当其中一个修改了这个属性.其它的实例也会变化)

构造函数继承

//构造函数继承
function SecondSon( name ) {
//使用call或apply来将父类构造函数注入到子类函数中
Parent.call( this, name )
}
//可以传参 
var tom = new SecondSon('Tom') 
var jerry = new SecondSon('Jerry')
tom.sum() // Tom
jerry.sum( ) // Jerry
//修改引用属性
tom.size.push('aa')
//避免了引用属性的共享问题
console.log(jerry.size) // ['176cm','76kg'] 
//没能继承父类的原型属性和方法, 所以这里报错了
tom.say() // Uncaught TypeError: tom.say is not a function

构造函数继承的重点:使用call或 apply 来将父类构造函数绑定到子类对象上
优点: 1.父类的引用属性不会被共享 2.子类构建实例时可以父类传参
缺点: 1.只能继承父类的方法和实例属性.不能继承原型属性和方法 2.无法实现复用.每个子类都有父类实例函数的副本.影响性能

组合继承

// 组合继承
function ThirdSon(name) {
//构造函数继承,用于继承实例属性和方法
Parent. call(this, name)
}
//原型链继承,用于继承原型属性和方法
ThirdSon.prototype = new Parent( )
//可以传参
var tom = new ThirdSon('Tom')
var jerry = new ThirdSon('Jerry')
tom.sum( )
jerry.sum()
//修改引用属性
tom.size.push('aa')
// 也没有引 |用属性的共享问题
console.log(jerry.size) // ["176cm", "76kg"] 
//原型方法与属性也能获取
tom.say() // 18

组合式继承的重点:在类的定义中使用call或 apply方式将父类构造函数绑定到子类后.再将子类的原型指向父类的实例
优点: 1.继承了父类的实例属性和方法.同时也继承了原型属性和方法 2.避免了引用属性共享引起的问题 3.可传参.且函数可复用
缺点:调用了两次父类的构造函数.生成了两份实例.造成了性能的浪费

原型式继承

//原型继承
function Fourth(obj) {
function F() { }
//与原型链几乎一致,没看出来有什么不同
F.prototype = obj 
return new F( )
}
var parent = new Parent( )
var tom =Fourth(parent)
tom.name='Tom'
var jerry = Fourth(parent)
jerry.name = 'Jerry'
tom.sum() // Tom
jerry.sum() // Jerry
tom.say() // 18
tom.size.push('aa')
//引用属性被共享
console.log(jerry.size) // ["176cm", "76kg", "aa"] 

寄生继承


function create0bj(obj) {
function F() { }
F.prototype = obj
return new F( )
}
function Fifth(origin) {
  var clone = create0bj(origin)
  clone. getName = function () {
  return this.name
  }
 return clone 
}
var parent = new Parent( )
var tom = Fifth(parent)
tom.name = 'Tom'
var jerry = Fifth(parent)
jerry.name = 'Jerry'
tom.sum() // Tom
jerry.sum() // Jerry
tom.say() // 18
tom.size.push('aa')
//引用属性被共享
console.log(jerry.size) // ["176cm", "76kg", "aa" ]

寄生组合式继承

//寄生组合式继承
//利用原型可以基于已有对象创建对象的特性,创建一个新的对象
function create0bj(obj) {
  function F() { } //创建一个构造函数
  F.prototype = obj //将传入的参数赋值给构造函数的原型
  return new F() //返回实例化的构造函数
}
function inheritPrototype(parent, son) {
  var prototype = create0bj(parent.prototype) //利用父类的   原型创建一个新的原型
  prototype.constructor = son //将新原型的构造函数指向子类
  son.prototype = prototype //将子类的原型指向新的原型
}
function Sixth(name) {
  Parent.call(this, name)
}
inheritPrototype(Parent, Sixth)
//可以传参
var tom = new ThirdSon('Tom')
var jerry = new ThirdSon('Jerry')
tom.sum( )
jerry.sum( )
//修改引用属性
tom.size.push('aa')
//也没有引|用属性的共享问题
console.log(jerry.size) // ["176cm", "76kg"]
//原型方法与属性也能获取
tom.say() // 18

寄生组合式继承的重点:通过构造函数继承的方式来继承父类的实例属性和方法.
通过建立一个中介对象的方式来获得--个父类的原型副本后将其指给子类的原型.
优点:该有的都有了
缺点:稍有点复杂
最终上面这些继承的方式在ES6中被进化成了Class

![_X8%L`NPHL%30DQY}ID4ZU.png