js实现继承的几种方法

大多OO语言都支持两种继承方式: 接口继承和实现继承 ,而ECMAScript中无法实现接口继承,ECMAScript只支持实现继承,而且其实现继承主要是依靠 原型链 来实现。

1.原型链继承

基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法,即 将父类的实例作为子类的原型。每个父类(构造函数)都有一个原型对象prototype,原型对象包含一个指向构造函数的指针constructor属性,而子类(实例)都包含一个指向原型对象的内部指针_proto_。

function Parent(){
   this.name = 'mike';
}

function Child(){
    this.age = 12;
}
Child.prototype = new Parent();
//Child继承Parent,通过原型,形成链条

var test = new Child();
alert(test.age);
alert(test.name);//得到被继承的属性
//继续原型链继承
function Brother(){   //brother构造
    this.weight = 60;
}
Brother.prototype = new Child();//继续原型链继承
var brother = new Brother();
alert(brother.name);//继承了Parent和Child,弹出mike
alert(brother.age);//弹出12

特点:

1非常纯粹的继承关系,实例是子类的实例,也是父类的实例

2父类新增原型方法/原型属性,子类都能访问到

3简单,易于实现。

缺点:

1无法实现多继承

2来自原型对象的引用属性是所有实例共享的

3创建子类实例时,无法向父类构造函数传参

推荐指数:★★(3、4两大缺陷)

2.构造继承

基本思想:在子类型构造函数的内部调用父类构造函数,通过使用call()和apply()方法可以在新创建的对象上执行构造函数,等于是复制父类的实例属性给子类(没用到原型)

function Parent(age){
   this.name = ['mike','jack','smith'];
   this.age = age;
}

function Child(age){
   Parent.call(this,age);
}
var test = new Child(21);
alert(test.age);//21
alert(test.name);//mike,jack,smith
test.name.push('bill');
alert(test.name);//mike,jack,smith,bill

特点:

1解决了1中,子类实例共享父类引用属性的问题

2创建子类实例时,可以向父类传递参数

3可以实现多继承(call多个父类对象)

缺点:

1实例并不是父类的实例,只是子类的实例

2只能继承父类的实例属性和方法,不能继承原型属性/方法

3无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

推荐指数:★★(缺点3)

3. 组合继承

基本思想:将原型链和借用构造函数的技术组合在一块。调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。

function Parent(age){
    this.name = ['mike','jack','smith'];
    this.age = age;
}
Parent.prototype.run = function () {
    return this.name  + ' are both' + this.age;
};
function Child(age){
    Parent.call(this,age);//对象冒充,给超类型传参
}
Child.prototype = new Parent();//原型链继承
var test = new Child(21);//写new Parent(21)也行
alert(test.run());//mike,jack,smith are both21

特点:

1弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法

2既是子类的实例,也是父类的实例

3不存在引用属性共享问题

4可传参

5函数可复用

缺点:

调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

推荐指数:★★★★(仅仅多消耗了一点内存)

4.拷贝继承

// 定义一个动物类
function Animal (name) {
   // 属性
   this.name = name || 'Animal';
   // 实例方法
   this.sleep = function(){
       console.log(this.name + '正在睡觉!');
   }
}
// 原型方法
Animal.prototype.eat = function(food) {
    console.log(this.name + '正在吃:' + food);
};
function Cat(name){
    var animal = new Animal();
    for(var p in animal){
        Cat.prototype[p] = animal[p];
    }
    Cat.prototype.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特点:

支持多继承

缺点:

1效率较低,内存占用高(因为要拷贝父类的属性)

2无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

推荐指数:★(缺点1)

发表评论

电子邮件地址不会被公开。 必填项已用*标注