JavaScript之继承的六种方式

一、原型链继承

**将父类的实例作为子类的原型,**他的特点是实例是子类的实例也是父类的实例,父类新增的原型方法/属性,子类都能够访问,并且原型链继承简单易于实现,缺点是来自原型对象的所有属性都被继承的实例共享,无法实现多继承,无法向父类构造函数传递。

function Animal(name) {
    this.name = name
    // 动态类型模式 利用原型共享方法
    if (this.eat !== 'function') {
        Animal.prototype.eat = function () {
            console.log(`吃${this.name}`)
        }
    }
}

function Dog(age) {
    this.age = age
}

// 用父类的实例来充当子类的原型对象
Dog.prototype = new Animal('动物')

var d = new Dog(3)
console.log(d.age) // 3
console.log(d.name) // '动物'
d.eat() // '吃'

 

二、构造继承

使用父类的构造函数来增强子类实例,既赋值父类的实例属性给子类。
构造继承可以向父类传递参数,可以实现多继承,通过call或apply多个父类对象。但是构造继承只能继承父类的实例属性和方法,不能继承原型属性和方法,无法实现函数的复用,每个子类都有父类实例函数的副本,影响性能。

function Animal(name) {
    this.name = name
    // 动态类型模式 利用原型共享方法
    if (this.eat !== 'function') {
        Animal.prototype.eat = function () {
            console.log('吃')
        }
    }
}

function Dog(name, age) {
    // 借用父类的构造函数
    Animal.call(this, name)

    /* 借用构造函数Animal 相当于执行了以下操作:
    this.name = name 
    Animal.prototype.eat = function() {
        console.log('吃')
    }
    */

    this.age = age
}

var d = new Dog('哈士奇', 2)

console.log(d.name) // '哈士奇'
console.log(d.age) // 2
d.eat() // Uncaught TypeError: d.eat is not a function

这种方式与上种方式恰好相反。子类不会继承父类原型中的任何属性和方法(即父类的原型不会被共享),但是它可以在构建实例时向父类传参,将父类本身的属性带到子类实例本身上。

三、组合继承

通过调用父类构造,继承父类的属性并保存传参的优点,然后通过实例作为子类原型实现函数复用。可以继承父类原型上的属性,可以传参,可复用。每个新势力引入的构造函数属性是私有的。

function Animal(name) {
    this.name = name
    // 动态类型模式 利用原型共享方法
    if (this.eat !== 'function') {
        Animal.prototype.eat = function () {
            console.log(`吃${this.name}`)
        }
    }
}
function Dog(name, age) {
    // 1.借用父类的构造函数
    Animal.call(this, name) // 第一次调用 Animal
    this.age = age
}

// 2.用父类实例充当子类原型
Dog.prototype = new Animal('动物') // 第二次调用 Animal
Dog.prototype.constructor = Dog  //手动指明构造函数

var d = new Dog('哈士奇', 2)

console.log(d.name) // '哈士奇'
console.log(d.age) // 2
d.eat() // '吃哈士奇'

四、原型继承

父类的引用类型属性会被所有子类实例共享,任何一个子类实例修改了父类的引用类型属性,其他子类实例都会受到影响
创建子类实例的时候,不能向父类传参

function object(o) {
    function F() {};
    F.prototype = o;
    return new F();
}

var person = {
    name: 'binguo',
    values: ['one', 'two', 'three']
}
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.values.push("Rob");
console.log(person.values); //[ 'one', 'two', 'three', 'Rob' ]

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.values.push("Barbie");
console.log(person.values); //"Shelby,Court,Van,Rob,Barbie" 

五、寄生继承

其实就是在原型式继承得到对象的基础上,在内部再以某种方式来增强对象

function createAnother(original) {
	var clone = object(original); //通过调用函数创建一个新对象
	clone.sayHi = function(){		// 以某种方式来增强这个对象
		alert("hi");
	};
	return clone;			// 返回这个对象
}

寄生式继承与原型式继承紧密相关,与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象。

六、寄生组合继承:

通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的时候,就不会初始化两次实例方法/属性,便面的组合继承的缺点。合继承是 JavaScript 最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。

function inheritPrototype( subObj, superObj ){
             var proObj = object( superObj.prototype ); //复制父类superObj的原型对象
             proObj.constructor = subObj; //constructor指向子类构造函数
             subObj.prototype = proObj; //再把这个对象给子类的原型对象
         }

在函数内部,第一步是创建超类型原型的一个副本。

第二步是为创建的副本添加 constructor 属性,从而弥补因重写原型而失去的默认的 constructor 属性。

最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我们就可以用调用 inheritPrototype()函数的语句,去替换前面例子中为子类型原型赋值的语句了

// 红宝书上面的方式
function object(o) {
    var F = function() {};
    F.prototype = o;
    return new F();
}
function inhert(subType,superType) {
    var prototype = object(superType.prototype);
// 构造器重定向,如果没有通过实例找回构造函数的需求的话,可以不做构造器的重定向。但加上更严谨
    prototype.constructor = subType;
    subType.prototype = prototype;
}

function Super(name) {
    this.name = name;
}
Super.prototype.sayName = function() {
    console.log(this.name);
}

function Sub(name, age) {
    Super.call(this, name);
    this.age = age;
}

inhert(Sub, Super);

var sub = new Sub('Tom', 22);
sub.sayName();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值