继承的六种方法的代码实现及优缺点

86 篇文章 5 订阅
6 篇文章 0 订阅

原型链继承

重点:让新实例的原型等于父类的实例
优点:
实例可继承的属性有:
1、实例的构造函数的属性,
2、父类构造函数的属性,
3、父类原型的属性(新实例不会继承父类实例的属性)

缺点:
1、新实例无法向父类构造函数传参
2、继承单一;
3、所有新实例都会共享父类的属性(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改)

function Person() {
    this.name = 'fur'
}
Person.prototype.getName = function () {
    return this.name
}

function Child() {
}
Child.prototype = new Person()
var child = new Child()
console.log(child.getName()) //fur

构造函数继承

用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
优点:
1、解决了原型链继承缺点新实例无法向父类构造函数传参;继承单一;新实例共享父类的属性
2、可以继承多个构造函数属性(call多个)。
3、在子实例中可向父实例传参
缺点:
1、只继承了父类构造函数的属性,没有继承父类原型的属性。
2、无法实现构造函数的复用。(每次用都要重新调用)
3、每个新实例都有父类构造函数的副本,臃肿。

// 构造函数
function Person(){
    this.name = 'fur'
    this.colors=['red','yellow','green']
}
Person.prototype.getName = function (){
    return this.name
}
function Child(age){
    Person.call(this)
    this.age = age
}
let child1 = new Child(1)
let child2 = new Child(2)

child1.colors.push('blue')
// console.log(child1.getName())  //失败
console.log(child1.name)
console.log(child1.age)
console.log(child1.colors)
console.log(child2.colors)
// fur
// 1
// 2
// [ 'red', 'yellow', 'green', 'blue' ]
// [ 'red', 'yellow', 'green' ]

组合继承(原型链+构造函数)

组合原型链继承和借用构造函数继承,常用
重点:结合了两种模式的优点,传参和复用
优点:
1、可以继承父类原型上的属性,可以传参,可复用。
2、每个新实例引入的构造函数属性是私有的。
缺点:
调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

function Parent(name){
    this.name = name
    this.colors=['red','yellow','green']
}
Parent.prototype.getName = function(){
    return this.name
}
function Child(name,age){
    Parent.call(this,name) //第二次调用Parent
    this.age = age
}
Child.prototype = new Parent() //第一次调用Parent
let child1 = new Child('fur',1)
let child2 = new Child('hui',2)
child1.colors.push('blue')
console.log(child1.getName())
console.log(child1.name)
console.log(child2.name)
console.log(child1.age)
console.log(child2.age)
console.log(child1.colors)
console.log(child2.colors)
// fur
// fur
// hui
// 1
// 2
// [ 'red', 'yellow', 'green', 'blue' ]
// [ 'red', 'yellow', 'green' ]

原型式继承

重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。Object.create()就是这个原理。
优点:类似于复制一个对象,用函数来包装。
缺点:
1、所有实例都会继承原型上的属性。
2、无法实现复用。(新实例属性都是后面添加的)

function CreateObj(o){  //CreateObj函数效果等同于Object.create(o)
    function F(){} //媒介
    F.prototype = o//person
    return new F() //返回新建的person实例
}
let person = {
    name:'fur',
    colors:['red','yellow','green']
}
let person1 = CreateObj(person)
//等同于 let person1 = Object.create(person)
let person2 = CreateObj(person)
person1.colors.push('blue')
person1.name = 'hui'
console.log(person1.colors)
console.log(person2.colors)
console.log(person1.name)
console.log(person2.name)
// [ 'red', 'yellow', 'green', 'blue' ]
// [ 'red', 'yellow', 'green', 'blue' ]
// hui
// fur

注意:
这里修改了person1.name的值,person2.name的值并未改变,并不是因为person1和person2有独立的name值,而是person1.name='person1’是给person1添加了name值,并非修改了原型上的name值,因为我们找对象上的属性时,总是先我实例上对象,没有我到的话再去原型对象上的属性。实例对象和原型对象上如果有同名属性,总是先取实例对象上的值。

在这里插入图片描述
原型及原型链知识普及
在这里插入图片描述

寄生式继承

重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象,这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。

// 寄生式继承
let ob = {
    name: 'fur',
    colors: ['red', 'yellow', 'green']
}

function CreateChild(o){
    let newOb = Object.create(o)
    newOb.getName = function (){
        return this.name
    }
    newOb.pushColor = function (color){
        this.colors.push(color)
    }
    return newOb
}
let p1 = CreateChild(ob)
p1.pushColor('blue')
console.log(p1.getName())
console.log(ob.colors)
// fur
// [ 'red', 'yellow', 'green', 'blue' ]

寄生组合式继承

重点:修复了组合继承的问题,即调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
用一个原型来辅助继承,并隔离作用域
在这里插入图片描述

function Parent(name) {
    this.name = name
    this.colors = ['red', 'yellow', 'green']
}
Parent.prototype.getName = function () {
    return this.name
}

function Child(name, age) {
    Parent.call(this, name)
    this.age = age
}
// Child.prototype = new Parent() 删除,改成下方代码
function makePrototype(child, parent) {
    //媒介,新建一个继承自父类原型的对象,__proto__指向父类原型,防止覆盖父类实例
    let prototype = Object.create(parent.prototype)
    prototype.constructor = child
    child.prototype = prototype
}
makePrototype(Child, Parent)
let child1 = new Child('fur', 1)
let child2 = new Child('hui', 2)
child1.colors.push('blue')
console.log(child1)
console.log(child1.getName())
console.log(child2)
// Child {
//     name: 'fur',
//     colors: ['red', 'yellow', 'green', 'blue'],
//     age: 1
// }
// fur
// Child {
//     name: 'hui',
//     colors: ['red', 'yellow', 'green'],
//     age: 2
// }

小结

原型链继承:引用值共享问题,不能传参
构造函数继承:父类上的原型不能被继承,无法实现构造函数的复用
组合继承:函数多执行了一次call
原型式继承:所有实例都会继承原型上的属性,无法实现复用
寄生式继承:没用到原型,无法复用
寄生组合式继承:利用原型寄生解决组合继承的问题

参考:
https://www.bilibili.com/video/BV1Ki4y1L7dY?t=126
https://blog.csdn.net/qq_44162474/article/details/98489229

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值