1.1 原型的六种继承序言
之前学习了与原型链相关的prototype的概念,现在来继续深入学习以下六种继承的原理以及它们的优缺点。
2.1 原型链继承
2.1.1 原型链继承的基本思路
通过原型链普通的继承原理,一个原型对象继承多个对象的属性以及方法。
2.1.2 原型链继承的代码实现
function user(name){
this.name = 'a'
this.age = 10
this.thing = []
}
function vip(){
}
vip.prototype = new user()
// 为什么需要以 子类构造函数的显式原型 = 实例化的父类构造函数 书写呢?
// 原型链的层次关系 vip.prototype → new user()实例化对象 → [ new user()实例化对象.__proto__ 既是 user.prototype ]
vip.prototype.constructor = vip
user.prototype.addThing = function(item){
this.thing.push(item)
}
let warn = new vip('xxx')
console.log(warn)
let a = new vip()
a.addThing('hasVipCard')
console.log('a',a)
let b = new vip()
console.log('b',b)
运行截图
2.1.3 原型链继承的优缺点
- 优点1:代码实现比较简单。
- 缺点1:不能够通过子类构造函数的同时传参给父类构造函数。
- 缺点2:修改其中一个子类实例对象,会影响其它子类实例对象引用值。
2.2 盗用构造函数
2.2.1 盗用构造函数的基本思路
在子类构造函数中重新调用父类构造函数进行传参。
2.2.2 盗用构造函数的代码实现
function user(name,thing){
this.name = name
this.thing = thing
}
function vip(name,thing){
user.call(this,name,thing)
//call意思是使用this对象指向到user对象,vip可以直接调用user所有属性以及方法
}
vip.prototype.addThing = function (item){
this.thing = [item]
}
let a = new vip('小明',['书包'])
console.log('a',a)
let c = new vip()
c.addThing('笔')
console.log('c',c)
let d = new vip()
console.log('d',d)
运行截图
2.2.3 盗用构造函数的优缺点
- 优点:能够通过子类构造函数的同时传参给父类构造函数。
- 缺点:子类不能够获取到父类的原型对象定义的方法,因为没有继承(即没有原型链)。
2.3 组合继承(盗用构造函数+原型链继承)
2.3.1 组合继承的基本思路
通过盗用构造函数来,从子函数中定义的this对象指向父类并给给它传值。又可以通过原型链继承,子类生成的实例可以调用父类定义的方法。
2.3.2 组合继承的相关代码
function user(name,thing){
this.name = name
if(thing){
this.thing = [thing]
}else{
this.thing = thing
}
}
function vip(name,thing){
user.call(this,name,thing)
}
user.prototype.addThing = function(item){
if(this.thing){
this.thing.push(item)
}else{
this.thing = [item]
}
}
vip.prototype = new user()
let a = new vip('小红','笔')
console.log('a',a)
let b = new vip()
b.addThing('书包')
console.log('b',b)
let c = new vip()
console.log('c',c)
运行截图
2.3.3 组合继承的优缺点
- 优点:每个子类生成的实例互相独立不影响,且可以传值给父类或使用父类的方法。
- 缺点:父类构造函数中的局部变量保持相同(未定义的也会占用父类构造函数的空间)
2.4 原型式继承
2.4.1 原型式继承的基本思路
所生成的原型指向第一个参数(原型对象),第二个参数是定义生成新的原型对象中的各个变量属性规则。
2.4.2 原型式继承的相关代码
// Object.create()底层实现原理 (ES6的新特性)
Object.create = function (o) {
var F = function () {
};
F.prototype = o;
return new F();
};
let person = {
name:'萨哈',
things:[]
}
let a = Object.create(person)
a.name = '小红'
a.things.push('笔')
console.log('a',a)
let b = Object.create(person,{
//定义该原型对象里每个变量的属性
name:{
//该变量的值
value:'小黄',
//该变量是否可写
writable: false,
//该变量是否能遍历
enumerable:true,
//是否允许外部操作修改这个变量的其它属性值(value属性除外)
configurable:true
}
})
b.name = '我要修改'
console.log('b',b)
运行截图
2.4.3 原型式继承的优缺点
- 优点:不需要定义子类构造函数,已经原型链.prototype的指向操作。
- 缺点:父类构造函数的变量值还是会共享,保持原型链继承的特征。
2.5 寄生式继承
2.5.1 寄生式继承的基本思路
是类似于原型寄生构造函数与工厂模式,在封装继承过程中增强需要使用到的构造函数方法。
2.5.2 寄生式继承的相关代码
// 内部定义object()传入的参数,它作为父类原型对象,并与其return返回的新实例F构建继承关系
function object(o){
function F(){
}
F.prototype = o
return new F()
}
function smallObject(item){
//给原来item的原型对象与新建的clone原型对象添加继承关系
let clone = object(item)
//给新的clone原型对象添加新的内部构造方法(增强)
clone.outPut = function(){
console.log('我是由item原型对象生成的打印')
}
return clone
}
let person = {
name:'a'
}
//生成一个新的原型对象a,并拥有了person的属性以及方法。(其中包括方法outPut())
let a = smallObject(person)
a.outPut()
console.log('a',a)
let b = smallObject()
console.log('b',b)
运行截图
2.5.3 寄生式继承的优缺点
- 优点:需要新建的继承新建原型对象不是构造函数或者自定义对象,可以适用寄生式继承。
- 缺点:原型需要运用到的构造函数是在于生成新原型对象内部定义的,复用的话会有局限性,无法针对多场景继承原型对象。
2.6 寄生式组合继承
2.6.1 寄生式组合继承的基本思路
组合继承因为会调用两次父类构造函数(再子类构造函数指向父类而调用+添加继承关系而调用),会存在效率上的问题,以及父类的属性生成时也会有默认属性。而提出寄生式组合继承,原理是封装子类与父类的关系以及继承关系,只调用一次父类构造函数,以节省空间,防止产生多余。
2.6.2 寄生式组合继承的相关代码
//定义继承关系(子类与父类的关系以及继承关系的封装)
function object(o){
function F(){
}
F.prototype = o
return new F()
}
function bindprototype(parent,son){
let p = object(parent.prototype)
p.constructor = son
son.prototype = p
}
//父类构造函数
function personN(name){
this.name = name
this.thing = ['书包']
}
//子类构造函数
function personV(name,age) {
//父类对象指向this的对象,(若使用下行的personV.prototype = new personN()), personN.call()第二次调用就是第二次调用personN()
personN.call(this,name)
this.age = age
}
// 下第一次调用personN()
// personV.prototype = new personN()
bindprototype(personN,personV)
var a = new personV('小红','18')
a.thing.push('笔')
console.log('a',a)
var b = new personV('小黄')
console.log('b',b)
运行截图
2.6.3 寄生式组合继承的优缺点
- 优点:设置继承关系时只调用一次,节省空间。
- 缺点:封装继承关系时,代码比较繁琐难以理解。
3.1 结论
从这六种继承方法种,寄生式组合继承是现在开发人员普遍使用的继承方法,既满足性能以及代码空间的利用率,又可以对原型与原型之间的继承方法的封装,实现原型链的继承效果。经过本人的几天学习,6种继承方法比较繁琐,有些部分也比较难懂需要更深入地加重复习一下。