js 最佳继承方案

继承作为面向对象语言的三大特性(封装、继承、多态)之一,可以在不影响父对象的情况下,可以让子对象具有父对象的特性;同时还能不影响父类对象行为的情况下扩展子类对象的行为,为编码带来了极大的便利(复用和扩展) javascript不是正统的面向对象的语言 ES6前,JavaScript中只有封装,继承也只是模拟继承,谈不上面向对象;但后面有class,超集Typescript 可以说是完成可以当成面向对象语言使用。
所以这里用ES6前的内容间接模拟实现一个最佳继承方案
继承肯定要有父对象

// 父构造函数
function Animal(name) {
    // 实例属性
    this.type ='Animal';
    this.name = name;
    // 实例方法
    this.sleep = function(){
        console.log(this.name + '睡觉!')
   }
}
// 原型方法
Animal.prototype.eat = function(food) {
    console.log(this.name + '在吃' + food)
}
// 原型属性
Animal.prototype.age = 2; 

原型链继承

重写子类的prototype属性,将其指向父类的实例

function Dog(name) {
    this.name = name
}
// 将父类实例对象挂在子类的原型上,从而使得子类实例可以访问父类实例和原型的数据
// 这里因直接挂载父类实例,所以无法给父类传参数
Dog.prototype = new Animal()
// 子类Gog的构造函数指向自身
Dog.prototype.constructor = Gog
// 子类的添加新的原型对象属性要放在继承父类原型对象的下面,要不然会被父类的覆盖
Dog.prototype.eat = function(){
    
}
console.log(new Dog('中华田园犬')) 

image.png

缺点:子类的所有实例将共享父类的属性;在创建子类实例时,无法向父类的构造函数传递参数;无法实现多继承;为子类增加原型对象属性和函数时,必须放在new Animal()函数之后

构造继承

子类的构造函数中通过call函数改变this指向,调用父类的构造函数,使得父类的实例属性和方法绑定定到子类的this上

// 子类
function Dog(name) {
    // 了解call的应该很好理解,这里相当于Animal函数中的this就是Dog的实例,
    // 里面用this赋值得都是在向Dog的实例中添加属性或方法,
    // 所以这里是可以传参数的,不传相当于都是undefined
    Animal.call(this, name)
    this.age = 4
} 

image.png

缺点:实例只是子类的实例,并不是父类的实例,根据原型链找不到Animal;只能继承父类实例的属性和方法,不能继承原型对象;无法复用父类的实例方法,每个通过call得到父类方法都不相同,造成不必要发内存消耗

复制继承

通过for… in父类实例,将其设置到子类的实例对象和原型对象上

// 子类
function Dog(name) {
    var animal = new Animal(name);
    for (var key in animal) {
    // 父类实例属性和方法添加到子类实例中
    if(animal.hasOwnProperty(key)) {
        this[key] = animal[key]
    } else {
    // 父类原型对象添加到子类对象中
    Dog.prototype[key] = animal[key]
        }
    }
}
console.log(new Dog('小狥')) 

image.png

缺点:父类的所有属性和方法都需要复制,消耗内存;实例只是子类的实例,并不是父类的实例

组合继承

结合构造继承原型继承

// 子类
function Dog(name) {
    // 构造继承
    Animal.call(this, name) // 第一次
    this.age = 8
}
// 原型继承
Dog.prototype = new Animal() // 第二次
Dog.prototype.constructor = Dog
console.log(new Dog('小狥')) 

image.png

缺点:父类的实例属性会绑定两次,call调用一次,改写子类的prototype对象是调用一次父类的构造函数

寄生组合继承

在组合继承的基础上,通过设置子类的prototype属性,去掉父类实例属性和方法

// 子类
function Dog(name) {
    Animal.call(this, name)
    this.age = 8
}
// Super 构造函数的原型指向父类的Animal的原型,去掉了父类的实例属性,下面有两种方式,都是为了去掉父类实例属性
// 第一种处理方法
// function Super(){}
// Super.prototype = Animal.prototype
// Dog.prototype = new Super()
// 第二种处理方法
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog
console.log(new Dog('小狥')) 

image.png

缺点:实现起来有点复杂,代码不多涉及的js核心概念不少,没点功力不好理解,该方式解决了以上面方式的不足,可以继承实例属性和方法,也可以继承原型属性和方法,实例是子类的实例,也是父类的实例,不存在引用类型属性相互影响的问题,函数可复用,也可以传参,也不用实例属性绑两次

每一步都理解,说明对js还是有一定的了解了,说出你的看法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值