JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。
既然要实现继承,那么我们先定义一个父类:
// 定义一个动物类 function Animal (name) { // 属性 this.name = name || 'Animal'; // 实例方法 this.sleep = function(){ alert(this.name + '正在睡觉!'); } } // 原型方法 Animal.prototype.eat = function(food) { alert(this.name + '正在吃:' + food); };
1、原型链继承
核心: 将父类的实例作为子类的原型
function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = 'cat'; // Test Code var cat = new Cat(); alert(cat.name); alert(cat.eat('fish')); alert(cat.sleep()); alert(cat instanceof Animal); //true alert(cat instanceof Cat); //true
特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
缺点:
- 要想为子类新增属性和方法,必须要在
new Animal()
这样的语句之后执行,不能放到构造器中 - 无法实现多继承
- 来自原型对象的引用属性是所有实例共享的
- 创建子类实例时,无法向父类构造函数传参
2、借用构造函数继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } // Test Code var cat = new Cat(); alert(cat.name); alert(cat.sleep()); alert(cat instanceof Animal); // false alert(cat instanceof Cat); // true
特点:
- 解决了1中,子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象)(不完美,没有父类方法)
缺点:
- 实例并不是父类的实例,只是子类的实例
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
3、组合继承
核心:通过调用父类构造函数,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,继承父类属性和方法,实现函数复用
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } Cat.prototype = new Animal(); // Test Code var cat = new Cat(); alert(cat.name); alert(cat.sleep()); alert(cat instanceof Animal); // true alert(cat instanceof Cat); // true
特点:
- 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
- 可以实现多继承(同上)
缺点:
- 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)(仅仅多消耗了一点内存)
4、原型式继承
核心:继承已有的对象而不是函数
//对传入的对象执行一次浅复制 function object(o){ function F(){} F.prototype = o; return new F(); } //例子 var Animal = { name:"cat", friend:["aaron","cassic"] } var cat=object(Animal); cat.name="sss"; cat.friend.push("aseaff"); alert(cat.name); alert(cat.friend);
特点:
- 没有必要创建构造函数,只是想让一个对象与另一个对象保持类似
缺点:
- 与原型模式一样,包含引用类型的属性值会共享
5、寄生式继承
核心 : 与原型式继承紧密相关,只是多了方法
//对传入的对象执行一次浅复制 function object(o){ function F(){} F.prototype = o; return new F(); } //增强对象 function createobject(o){ var clone=object(o); clone.sayname=function(){ alert("hi"); }; return clone; } //例子 var Animal = { name:"cat", friend:["aaron","cassic"] } var cat=createobject(Animal); cat.sayname();
特点:
- 没有必要创建构造函数,只是想让一个对象与另一个对象保持类似
- 与原型式继承相比,有了自己的方法
缺点:
- 与原型式继承一样,包含引用类型的属性值会共享
6、寄生组合继承
核心:通过寄生方式,创建空函数,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; }function aaron(o,y){ var Super = function(){}; Super.prototype=y.prototype; o.prototype=new Super(); o.prototype.constructor=o; } aaron(Cat,Animal); // Test Code var cat = new Cat(); alert(cat.name); cat.sleep(); alert(cat instanceof Animal); // true alert(cat instanceof Cat); //true
特点:
- 堪称完美
缺点:
- 实现较为复杂
- 不能实现多继承(不完美,同上)
下面介绍多继承
7 拷贝继承
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(); alert(cat.name); cat.sleep(); alert(cat instanceof Animal); // false alert(cat instanceof Cat); // true
特点:
支持多继承
缺点:
效率较低,内存占用高(因为要拷贝父类的属性)(使用 for in 能遍历原型方法)
8 组合使用构造函数和拷贝实现多继承
function Parent1(name,age){ this.name = name; this.age = age; this.height=180; } Parent1.prototype.say = function(){ alert('hi'); } function Parent2(name,age,weight){ this.name = name; this.age = age; this.weight = weight; this.height = 170; this.skin='yellow'; } Parent2.prototype.walk = function(){ alert('walk'); } function Child(name,age,weight){ Parent1.call(this,name,age); Parent2.call(this,name,age,weight); } for(var i in Parent1.prototype){Child.prototype[i] = Parent1.prototype[i]} for(var i in Parent2.prototype){Child.prototype[i] = Parent2.prototype[i]} var c1 = new Child('xiaoming',10,8); console.log(c1); //Child { name="xiaoming", age=10, height=170, 更多...} console.log(c1.constructor);//Child(name,age,weight)
特点:javascript的多继承其实就是前面介绍的js循环拷贝继承的多次使用
总结:javascript是可以利用call方法和prototype属性来实现多继承的。继承方法与单继承相似,只是将需要继承的多个父类依次实现,另外对于属性或共有方法重命的时候,以最后继承的属性和方法为主。因为会覆盖前面的继承。
最好的两个继承是寄生组合继承和循环拷贝继承,但是寄生组合继承不能完美实现多继承,会漏掉父类原型方法,只有循环拷贝继承可以完美实现多继承。
来源:http://www.cnblogs.com/aaronchu/p/6168378.html