js继承的几种方式

499 阅读5分钟

定义一个父类:

// 定义一个动物类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 Codevar cat = new Cat();alert(cat.name);alert(cat.eat('fish'));alert(cat.sleep());alert(cat instanceof Animal); //truealert(cat instanceof Cat); //true

特点:

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

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

缺点:

(1)要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中;

(2)无法实现多继承;

(3)来自原型对象的引用属性是所有实例共享的;

(4)创建子类实例时,无法向父类构造函数传参。

2、借用构造函数继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

function Cat(name){  Animal.call(this);  this.name = name || 'Tom';} // Test Codevar cat = new Cat();alert(cat.name);alert(cat.sleep());alert(cat instanceof Animal); // falsealert(cat instanceof Cat); // true

特点:

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

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

(3)可以实现多继承(call多个父类对象)(不完美,没有父类方法)

缺点:

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

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

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

3、组合继承

核心:通过调用父类构造函数,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,继承父类属性和方法,实现函数复用;

function Cat(name){  Animal.call(this);  this.name = name || 'Tom';}Cat.prototype = new Animal(); // Test Codevar cat = new Cat();alert(cat.name);alert(cat.sleep());alert(cat instanceof Animal); // truealert(cat instanceof Cat); // true

特点:

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

(2)既是子类的实例,也是父类的实例;

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

(4)可传参;

(5)函数可复用;

(6)可以实现多继承(同上)

缺点:

(1)调用了两侧父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)(仅仅多消耗了一点内存)

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);

特点:

(1)没有必要创建构造函数,只是想让一个对象与另一个对象保持类似;

(2)与原型模式一样,包含引用类型的属性值会共享。

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();

特点:

(1)没有必要创建构造函数,只是想让一个对象与另一个对象保持类似;

(2)与原型式继承相比,有了自己的方法;

缺点:

(1)与原型式继承一样,包含引用类型的属性值会共享;

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 Codevar cat = new Cat();alert(cat.name);cat.sleep();alert(cat instanceof Animal); // truealert(cat instanceof Cat); //true

特点:

  1. 堪称完美

缺点:

  1. 实现较为复杂
  2. 不能实现多继承(不完美,同上)

## 多继承

7、拷贝继承

function Cat(name){  var animal = new Animal();  for(var p in animal){    Cat.prototype[p] = animal[p];  }  Cat.prototype.name = name || 'Tom';} // Test Codevar cat = new Cat();alert(cat.name);cat.sleep();alert(cat instanceof Animal); // falsealert(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属性来实现多继承的。继承方法与单继承相似,只是将需要继承的多个父类依次实现,另外对于属性或共有方法重命的时候,以最后继承的属性和方法为主。因为会覆盖前面的继承。

最好的两个继承是寄生组合继承和循环拷贝继承,但是寄生组合继承不能完美实现多继承,会漏掉父类原型方法,只有循环拷贝继承可以完美实现多继承。

原文链接:www.cnblogs.com/aaronchu/p/…