js的继承主要是通过原型链来实现,有以下几种方式实现,各有优缺点。
第一种,原型式继承
function Top(){
this.topName = 'top'
this.roles = ['a']
}
Top.prototype.getTopName = function(){
return this.name
}
function Down(){
this.downName = 'down'
}
Down.prototype = new Top();
Down.prototype.getDownName = function (){
return this.downName
}
let instance = new Down();
instance.roles.push('b')
let instanceTwo = new Down();
console.log(instance.getTopName()) // top
console.log(instace.roles) // ['a','b']
console.log(instaceTwo.roles) // ['a','b']
Dwon.prototype 不使用自己默认的原型,而是使用了new Top(),所以Down.prototype.[[Prototype]] === Top.prototype,Down可以使用Top的属性和方法,实现了继承。
优点:使用原型来进行继承,清楚明了;
缺点:当原型中包含引用值的时候,会在所有实例间共享;子类型在实例化时不能给父类型的构造函数传参。
第二种,盗用构造函数
function Top(name){
this.name = name
this.roles = ['a']
this.doing = function(){
return this.name
}
}
Top.prototype.say = function(){
console.log('111')
}
function Down(name){
//继承Top
Top.call(this,name)
}
let instance = new Down();
instance.roles.push('b');
let instanceTwo = new Down('down');
instance.doing() // undefined
instance.name //undefined
instance.say() // error
console.log(instanceTwo.name) // down
console.log(instanceTwo.roles) // ['a']
instanceTwo.doing() //down
instanceTwo.say() // error
优点:使用简单,解决了不能传参及引用值复用的问题。
缺点:必须在构造函数中定义方法,因此函数不能重复使用;子类型不能访问父类原型上的方法。
第三种,炮灰式继承
function paohui(o){
function Demo(){}
Demo.prototype = o
return new Demo()
}
let person = {
name:'a',
friends:['jack','zo']
}
let anotherPerson = paohui(person);
anotherPerson.name = 'b'
anotherPerson.friends.push('rola');
let yetAnotherPerson = paohui(person);
yetAnotherPerson.friends.push('Linda');
console.log(person.friends) // 'jack,zo,rola,Linda'
这个paohui函数会创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。本质上,是对传入的对象执行了一次浅复制。
优点:适合不需要单独创建构造函数,但任然需要在对象间共享信息的场合。
缺点:和原型式继承一样,属性中包含的引用值始终会在相关对象间共享。
第四种,寄生式继承
function paohui(data){
function Demo (){}
Demo.prototype = data
return new Demo()
}
function js(data){
let clone = paohui(data)
clone.say = function(){
console.log(111)
}
return clone
}
let person = {
name:'1',
friends:[1]
}
let a = new js(person)
a.friends.push(2)
let b = new js(person)
console.log(b.friends) // '1,2'
优点:创建一个实现继承的函数,以某种方式增强对象。
缺点:和原型式继承一样,属性中包含的引用值始终会在相关对象间共享。
第五种,原型类组合继承
function Top(name){
this.name = name;
this.roles = ['Linda']
}
Top.prototpye.sayName = function (){
console.log(this.name)
}
function Down(name,age){
Top.call(this,name); // 第二次调用
this.age = age;
}
Down.prototype = new Top(); // 第一次调用
Down.prototype.constructor = Down;
Down.prototype.sayAge = function(){
console.log(this.age)
}
let instance = new Down('jack',20);
instance.roles.push('jack');
let instanceTwo = new Down('zo',18);
console.log(instanceTwo.roles) // 'Linda'
优点:解决了属性中的引用值共享的问题。
缺点:效率存在问题,父类的构造函数会被调用两次。
第六种,寄生组合式继承
function paohui(data){
function Demo (){}
Demo.prototype = data
return new Demo()
}
function inheritPrototype(Top,Down){
let prototype = paohui(Top);
prototye.constructor = Down;
Down.prototype = ptototype;
}
function Top(name){
this.name = name;
this.roles = ['Linda']
}
Top.prototpye.sayName = function (){
console.log(this.name)
}
function Down(name,age){
Top.call(this,name);
this.age = age;
}
inheritPrototype(Top,Down)
let instance = new Down('jack',20);
instance.roles.push('jack');
let instanceTwo = new Down('zo',18);
console.log(instanceTwo.roles) // 'Linda'
优点:不仅解决了属性中引用值共享问题,而且Top构造函数只调用了一次,引用类型继承的最佳模式。
缺点:复杂程度相对高点
注:仅供个人学习记笔记使用,如果错误,欢迎指正👏🏻。