原型链方式实现继承

  1. function SuperType(){
  2. this.property = true;
  3. this.colors = ['red','blue','green'];
  4. }
  5. SuperType.prototype.getSuperValue = function(){
  6. return this.property;
  7. };
  8. function SubType(){
  9. this.subprototype = false;
  10. }
  11. SubType.prototype = new SuperType(); //继承了SuperType
  12. SubType.prototype.constructor = SubType
  13. var instance1 = new SubType();
  14. instance1.colors.push('black');
  15. console.log("instance1.colors:",instance1.colors,'getSuperValue():',instance1.getSuperValue());
  16. var instance2 = new SubType();
  17. console.log("instance1.colors:",instance1.colors,'getSuperValue():',instance1.getSuperValue());
function SuperType(){
this.property = true;
this.colors = ['red','blue','green'];
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}; function SubType(){
this.subprototype = false;
}
SubType.prototype = new SuperType(); //继承了SuperType
SubType.prototype.constructor = SubType var instance1 = new SubType();
instance1.colors.push('black');
console.log("instance1.colors:",instance1.colors,'getSuperValue():',instance1.getSuperValue()); var instance2 = new SubType();
console.log("instance1.colors:",instance1.colors,'getSuperValue():',instance1.getSuperValue());
以上代码展示了原型链实现继承的方式。instance1实例继承了new  SuperType(),new SuperType()又继承了Object的实例(JavaScript中所有的对象都默认继承Object)。如此层层递进,构成了实例与实例原型的链。
 

原型链的问题

原型链很强大,可以用它来实现继承,但它也存在一些问题。其中最大的问题就是包含引用类型值得原型,而原型会被所有实例都共享。就像以上代码,SubType.prototype包含一个引用类型的值colors属性,而SubType的两个实例instance1和instance2都共享colors属性,对instance1.colors修改能够通过instance2.colors反映出来。
第二个问题是:在创建子类实例时,不能向超类的构造函数中传递参数。有鉴于此,在加上刚刚讨论过的原型中包含引用类型值得问题,实践中很少会单独使用原型链实现继承。

借用构造函数方式实现继承

在解决原型中包含引用类型值所带来的问题的过程中,开发人员使用一种叫做借用构造函数的技术。这种技术相当简单,即在子类型类构造函数内部调用超类型构造函数(使用apply()和call()方法实现)。事例代码如下:
  1. function SuperType(name){
  2. this.colors = ['red','blue','grezen'];
  3. this.name = name;
  4. this.print = function(obj){
  5. console.log("print:",obj);
  6. }
  7. }
  8. SuperType.prototype.getName = function(){
  9. return this.name;
  10. }
  11. function SubType(name,age){
  12. SuperType.call(this,name);
  13. this.age = age;
  14. }
  15. var instance1 = new SubType('zxy',24);
  16. instance1.colors.push('baclk');
  17. console.log('instance1.name:',instance1.name,'instance1.age:',instance1.age);
  18. var instance2 = new SubType('ABC',111);
  19. console.log('instance2.name:',instance2.name,'instance2.age:',instance2.age,'instance2.colors:',instance2.colors);
  20. instance2.print(instance2.colors);
function SuperType(name){
this.colors = ['red','blue','grezen'];
this.name = name;
this.print = function(obj){
        console.log("print:",obj);
    }
}
SuperType.prototype.getName = function(){
return this.name;
} function SubType(name,age){
SuperType.call(this,name);
this.age = age;
} var instance1 = new SubType('zxy',24);
instance1.colors.push('baclk');
console.log('instance1.name:',instance1.name,'instance1.age:',instance1.age); var instance2 = new SubType('ABC',111);
console.log('instance2.name:',instance2.name,'instance2.age:',instance2.age,'instance2.colors:',instance2.colors);
instance2.print(instance2.colors);

SuperType.call(this, name);会在新对象(指的是SubType的实例)上执行SuperType()函数中定义的所有代码初始化。结果SubType()的每个实例都有一个自己的colors属性的副本了。(解决了原型中包含引用类型值得问题)

我们还可以在SubType()构造函数内部调用SuperType()构造函数时,给SuperType()构造函数出入参数。这样就可在创建SubType()的实例时出入参数。(解决了创建子类实例时,不能像父类传递参数的问题)
 

借用构造函数存在的问题

借用构造函数也存在一些问题:1)方法都要在构造函数中定义,因此函数复用就无从谈起了。这样做降低了效率。上面代码中instance1.print == instance2.print的结果是false,也就是说创建了两个完成同样任务的函数实例,这样是没有必要的,会降低效率。
2)超类原型上的方法对子类是不可见的,子类继承不到。比如:SubType()的实例不能使用SuperType.prototype上的方法。
考虑到这两个问题,借用构造函数实现继承的方式在实践中也很少单独使用。

组合继承

组合继承是指将原型链和借用构造函数的技术组合到一起,从而发挥二者之长的一种继承技术。其背后的思路是使用原型链技术实现对原型上方法和属性的继承,而通过借用构造函数来实现对实例属性的继承。这样既能通过在原型上定义方法实现函数的复用,又能保证每个实例都有它自己的属性。ps:就这么原封不动的照着书敲下来了...因为总结不啰嗦,很精辟....
事例代码如下:
  1. function SuperType(name){
  2. this.name = name;
  3. this.colors = ['red','blue','green'];
  4. }
  5. SuperType.prototype.getName = function(){
  6. return this.name;
  7. }
  8. function SubType(name,age){
  9. SuperType.call(this,name);
  10. this.age = age;
  11. }
  12. SubType.prototype = new SuperType();
  13. SubType.prototype.constructor = SubType;
  14. SubType.prototype.getAge = function(){
  15. return this.age;
  16. }
  17. var instance1 = new SubType('zxy',24);
  18. instance1.colors.push('black');
  19. console.log('colors:',instance1.colors);
  20. console.log('instance1.getName():',instance1.getName(),'instance1.getAge():',instance1.getAge());
  21. var instance2 = new SubType('zxy',24);
  22. console.log('colors:',instance2.colors);
  23. console.log('instance2.getName():',instance2.getName(),'instance2.getAge():',instance2.getAge());
function SuperType(name){
this.name = name;
this.colors = ['red','blue','green'];
}
SuperType.prototype.getName = function(){
return this.name;
} function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.getAge = function(){
return this.age;
} var instance1 = new SubType('zxy',24);
instance1.colors.push('black');
console.log('colors:',instance1.colors);
console.log('instance1.getName():',instance1.getName(),'instance1.getAge():',instance1.getAge()); var instance2 = new SubType('zxy',24);
console.log('colors:',instance2.colors);
console.log('instance2.getName():',instance2.getName(),'instance2.getAge():',instance2.getAge());

组合继承避免了原型链和借用构造函数技术的缺陷,融合了它们的优点,成为JavaScript中最常用的继承模式。而且instanceof和isPrototypeOf()也能够用于基于组合模式创建的对象。

 
 

原型式继承

道格拉斯。克罗克福德在2006年写了一篇文章,题为 JavaScript中的原型式继承。在文章中他介绍了一种实现继承的方法,

print?

  1. function object(o){
  2. function F(){}
  3. F.prototype = o;
  4. return new new F();
  5. }
function object(o){
function F(){}
F.prototype = o;
return new new F();
}

在object()函数内部,先创建了一个临时性的构造函数F(),然后将传入的对象作为这个构造函数的原型,最后返回这个这个临时类型的新实例。从本质上来讲,object对传入的其中的对象执行了一次浅复制。我们看下面的例子:

 
  1. var person = {
  2. name:'zxy',
  3. friends: ['A','B','C']
  4. };
  5. var person1 = object(person);
  6. person1.name = 'zxy person1';
  7. person1.friends.push('D');
  8. console.log(person1);
  9. var person2 = object(person);
  10. person2.name = 'zxy person2';
  11. console.log(person2);
var person = {
name:'zxy',
friends: ['A','B','C']
}; var person1 = object(person);
person1.name = 'zxy person1';
person1.friends.push('D');
console.log(person1); var person2 = object(person);
person2.name = 'zxy person2';
console.log(person2);

克罗克福德主张的这种原型式继承,要求你必须有一个对象可以作为另一个对象的基础。如果有这么一个对象的话,就把它传递给object()函数,然后在根据需要在对得到的对象加已修改。在上面的例子中,可以最为基础对象的是person,于是我们把它出入object()中,然后object()函数返回一个新对象,这个新对象将person对象作为原型,所以它的原型中包含一个基本类型值和一个引用类型值。这就意味着person.fridends不仅属于pserson所有,而且也会被person1和person2共享。实际上,相当于创建了person的两个副本(浅复制)。原型式继承和原型链技术实现继承存在同样的问题。

ECMAScript5通过新增Object.create()规范原型式继承。想了解的可以百度一下。
 

原型式继承的问题

在没有必要兴师动众地创建构造函数,只想让一个对象与另一个对象保持类的情况下,原型式继承完全可以胜任。
不过别忘了,原型式继承和原型模式技术实现继承存在同样的问题。原型包含引用类型的值,通过一个实例对这个引用类型修改,会在其它实例反映出来。
 

寄生式继承

寄生式继承是与原型式继承紧密相关的一种思路,并且同样也是有克罗克福德推而广之的。即创建一个仅用于封装继承过程的的函数,该函数内部已某种方式增强对象(给对象添加方法,也就是添加新的行为),比较类似工厂模式。
已下代码示范了寄生式继承模式:
  1. function object(o){
  2. function F(){}
  3. F.prototype = o;
  4. return new F();
  5. }
  6. function createAnother(o){
  7. var clone = object(o);//通过调用函数创建一个新对象
  8. clone.sayHi = function(){//通过给新对象新增方法来增强这个对象
  9. console.log('HI 小明..');
  10. }
  11. return clone; //返回这个对象
  12. }
function object(o){
function F(){}
F.prototype = o;
return new F();
} function createAnother(o){
var clone = object(o);//通过调用函数创建一个新对象
clone.sayHi = function(){//通过给新对象新增方法来增强这个对象
console.log('HI 小明..');
}
return clone; //返回这个对象
}

在这个例子中,createAnother()函数接受一个参数,就是将要作为新对象的基础(原型)对象。然后把这个对象(o)传递给object()函数,将返回值赋给clone。在为clone对象添加新方法sayHi(),最后返回clone。可以像下面这样使用createAnother()函数:

  1. var person = {
  2. name:'zxy',
  3. friends: ['A','B','C']
  4. };
  5. var anotherPerson = createAnother(person);
  6. anotherPerson.sayHi();
var person = {
name:'zxy',
friends: ['A','B','C']
}; var anotherPerson = createAnother(person);
anotherPerson.sayHi();

这个例子中的代码,基于person对象返回了一个新对象anotherPerson.新对象不仅具有person的所有属性和方法。而且还有自定义的sayHi()方法。

按照我的理解的寄生式继承:一个函数中调用了其他函数(这个函数不是必须是object()函数,只要是能返回一个对象的函数就可以)产生一个新对象,在为新对象添加新方法,并将新对象返回。
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生继承也是一种有用的模式。
 
寄生模式存在的问题
1)达不到函数复用的目的。2)原型包含引用类型的值。

寄生组合模式

寄生组合模式JavaScript中真正的大牛级别实现继承的方式出现了。在介绍寄生组合模式之前我们先来说说组合模式存在的的最大问题。
我们之前说过组合模式是JavaSscript中最常用的继承模式。不过,它也有自己的不足。组合模式最大的问题就是无论什么情况下都会调用两次超类的构造函数:第一次是在将父类实例赋给子类的原型,第二次是在子类的构造函数内部调用父类构造函数。
第一次调用超类的实例后,子类实例就包含了所有父类实例的属性(从子类构造函数原型上继承来的),但是我们又在调用第二次父类的构造函数时不得不在子类构造函数中重新了这些属性。请看下面组合继承的例子:
 
  1. //超类
  2. function SuperType(name){
  3. this.name = name;
  4. this.colors = ['red','blue','green'];
  5. }
  6. SuperType.prototype.getName = function(){
  7. return this.name;
  8. }
  9. //子类
  10. function SubType(name,age){
  11. SuperType.call(this,name);//第二次调用超类构造函数
  12. this.age = age;
  13. }
  14. SubType.prototype = new SuperType(); //第一次调用超类构造函数
  15. SubType.prototype.constructor = SubType;
  16. SubType.prototype.getAge = function(){
  17. return this.age;
  18. }
  19. //创建SubType()构造函数的一个实例
  20. var instance = new SubType('zxy',24);
  21. console.log(instance); //输出类instance的原型链(见图IMG-1)
//超类
function SuperType(name){
this.name = name;
this.colors = ['red','blue','green'];
}
SuperType.prototype.getName = function(){
return this.name;
} //子类
function SubType(name,age){
SuperType.call(this,name);//第二次调用超类构造函数
this.age = age;
}
SubType.prototype = new SuperType(); //第一次调用超类构造函数
SubType.prototype.constructor = SubType;
SubType.prototype.getAge = function(){
return this.age;
} //创建SubType()构造函数的一个实例
var instance = new SubType('zxy',24); console.log(instance); //输出类instance的原型链(见图IMG-1)

在上面代码中第一次调用SuperType()构造函数时,会在SubType.prototype创建两个属性colors和name(图片中画红圈的后两个),它们都是SuperType()实例的属性,只不过是位于SubType的原型对象上。
第二次调用时,会在instance实例上创建colors和name属性(图片中画红圈的前两个),这两个实例上的colors和name会屏蔽原型中的同名属性。
如上所述,有两组colors和name属性,一组在SubType()的原型上,一组在SubType()的实例上。为了解决组合继承的问题,我们来学习一下寄生组合式继承。
 
寄生组合式继承,即通过借用构造函数继承来实现属性继承,通过原型链的混成来实现方法继承。其背后的基本思路是:不必为指定子类的原型而调用超类的构造函数(new超类的构造函数),我们所需要无非就是父类构造函数原型的一个副本而已。其实就是使用寄生式继承来继承父类的原型。寄生组合式继承的基本模式如下所示:
  1. function object(o){
  2. function F(){};
  3. F.prototype = o;
  4. return new F();
  5. }
  6. function inheritPrototype(subType,superType){
  7. var prototype = object(superType.prototype); //以superType.prototype为基础创建一个新对象(原型式继承).
  8. //注意这里是浅复制,尽量避免将colorsPrototype这样的属性定义在原型对象中,应该像colors属性定义在构造函数内部
  9. //subType.prototype = prototype;
  10. //subType.prototype.constructor = c;
  11. prototype.constructor = subType;             //给prototype对象添加constructor属性,增强对象(寄生式继承)
  12. subType.prototype = prototype;               //将prototype对象子类的原型对象
  13. }
function object(o){
function F(){};
F.prototype = o;
return new F();
} function inheritPrototype(subType,superType){
var prototype = object(superType.prototype); //以superType.prototype为基础创建一个新对象(原型式继承).
//注意这里是浅复制,尽量避免将colorsPrototype这样的属性定义在原型对象中,应该像colors属性定义在构造函数内部
//subType.prototype = prototype;
//subType.prototype.constructor = c;
prototype.constructor = subType; //给prototype对象添加constructor属性,增强对象(寄生式继承)
subType.prototype = prototype; //将prototype对象子类的原型对象 }

这个实例中的inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:第一个参数是子类构造函数和超类构造函数。在函数内部,第一步是创建超类原型的一个副本。第二部是添加一个constructor属性,从而弥补重写原型而失去的默认的constructor属性。最后一步将新创建的对象(即父类原型的副本)赋给子类的原型。这样,我们可以调用inheritPrototype()函数来替换前边例子中为子类原型赋值的语句了。实例如下:

  1. //也可以将object()和inheritPrototye()功能合并成一个函数
  2. /*function inheritPrototype1(subType, superType){
  3. function F(){};
  4. F.prototype = superType.prototype;
  5. subType.prototype = new F();
  6. subType.prototype.constructor = subType;
  7. }*/
  8. //父类
  9. function SuperType(name){
  10. this.name = name;
  11. this.colors = ['red','green','black'];
  12. }
  13. SuperType.prototype.sayName = function(){
  14. console.log("sayName():"+this.name);
  15. };
  16. SuperType.prototype.colorsPtototype = ['A','B'];
  17. //子类
  18. function SubType(name,age){
  19. SuperType.call(this,name);
  20. this.age = age;
  21. }
  22. inheritPrototype(SubType,SuperType);    //使SubType继承SuperType
  23. //SubType.prototype = new SuperType(); //SubType.prototype.constructor = SubType; //以前的写法
  24. SubType.prototype.sayAge = function(){
  25. console.log("sayName():"+this.age);
  26. }
  27. var subType1 = new SubType('zxy','20');
  28. subType1.sayName();
  29. subType1.sayAge();
  30. subType1.colorsPtototype.push('C');
  31. console.log("colorsPtototype:"+subType1.colorsPtototype);
  32. console.log("subType1:",subType1);
  33. var subType2 = new SubType();
  34. console.log("colorsPtototype:"+subType2.colorsPtototype);
  35. console.log("subType2",subType2);
//也可以将object()和inheritPrototye()功能合并成一个函数
/*function inheritPrototype1(subType, superType){
function F(){};
F.prototype = superType.prototype;
subType.prototype = new F();
subType.prototype.constructor = subType;
}*/ //父类
function SuperType(name){
this.name = name;
this.colors = ['red','green','black'];
}
SuperType.prototype.sayName = function(){
console.log("sayName():"+this.name);
};
SuperType.prototype.colorsPtototype = ['A','B']; //子类
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
inheritPrototype(SubType,SuperType); //使SubType继承SuperType
//SubType.prototype = new SuperType(); //SubType.prototype.constructor = SubType; //以前的写法
SubType.prototype.sayAge = function(){
console.log("sayName():"+this.age);
} var subType1 = new SubType('zxy','20');
subType1.sayName();
subType1.sayAge();
subType1.colorsPtototype.push('C');
console.log("colorsPtototype:"+subType1.colorsPtototype);
console.log("subType1:",subType1); var subType2 = new SubType();
console.log("colorsPtototype:"+subType2.colorsPtototype);
console.log("subType2",subType2);

这个例子的高效体现在他只调用一次父类(SubType)的构造函数,并且因此避免了在子类的原型(SubType.prototype)上创建不必要、多余的属性。 与此同时,原型链还能保持不变;因此能够正常使用instanceof和isPrototypeOf()。

YUI的YAHOO.lang.extend()方法采用了寄生组合式继承,从而让这种模式首次出现在一个应用非常广泛的JavaScript库中。
 
 
 
 
 
 

javascript实现继承的几种方式的更多相关文章

  1. 实现JavaScript中继承的三种方式

    在JavaScript中,继承可以通过三种手法实现原型链继承 使用apply.call方法 对象实例间的继承.     一.原型链继承 在原型链继承方面,JavaScript与java.c#等语言类似 ...

  2. javascript实现继承的三种方式

    一.原型链继承  function Parent(){} function Child(){} Child.prototype = new Parent(); 通过对象child的prototype属 ...

  3. JavaScript——实现继承的几种方式

    实现继承的6中方法: 借用构造函数 组合继承 原型式继承 寄生式继承 寄生组合式继承 拷贝继承 1. 借用构造函数 在子类型构造函数的内部调用超类构造函数.通过使用apply()和call()方法在新 ...

  4. javascript实现继承的一种方式

    function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototy ...

  5. javascript实现继承的6种方式

    /*1.原型链继承*/ function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = funct ...

  6. javascript中实现继承的几种方式

    javascript中实现继承的几种方式 1.借用构造函数实现继承 function Parent1(){ this.name = "parent1" } function Chi ...

  7. javascript(js)创建对象的模式与继承的几种方式

    1.js创建对象的几种方式 工厂模式 为什么会产生工厂模式,原因是使用同一个接口创建很多对象,会产生大量的重复代码,为了解决这个问题,产生了工厂模式. function createPerson(na ...

  8. 前端知识体系:JavaScript基础-原型和原型链-实现继承的几种方式以及他们的优缺点

    实现继承的几种方式以及他们的优缺点(参考文档1.参考文档2.参考文档3) 要搞懂JS继承,我们首先要理解原型链:每一个实例对象都有一个__proto__属性(隐式原型),在js内部用来查找原型链:每一 ...

  9. javascript创建类的6种方式

    javascript创建类的7种方式 一 使用字面量创建 1.1 示例 var obj={}; 1.2 使用场景 比较适用于临时构建一个对象,且不关注该对象的类型,只用于临时封装一次数据,且不适合代码 ...

随机推荐

  1. C专家编程学习 1

    1.C语言的基本数据类型直接与底层硬件相对应. 2#define 是可能出现问题 1 2 3 4 5 #define a(y) a_ex(y) a(x)被扩展为 a_ex(x)   #define a ...

  2. JavaWeb学习记录(三)——网页中文编码问题

    方法一: public void doGet(HttpServletRequest request, HttpServletResponse response)            throws S ...

  3. MySQL二进制日志的备份和恢复

    二进制日志:记录数据库修改的相关操作,作用是即时点回复,主从复制 可以按时间滚动,也可以按大小滚动 server-id:服务器身份标识 一.二进制文件的删除方法,千万不要手动删除 PURGE BINA ...

  4. JAVA的UML

    1. UML概念 Unified Modeling Language (UML) 又称统一建模语言或标准建模语言 是一个支持模型化和软件系统开发的图形化语言 2. UML图示 UML2.2中一共定义了 ...

  5. Git连接Github

    环境:Ubuntu Server 12.04 安装Git apt-get install git git-core 配置本机Git git config --global user.name &quo ...

  6. Python 简单爬虫

    import os import time import webbrowser as web import random count = random.randint(20,40) j = 0 whi ...

  7. PWM控制led渐变

    PWM,中文释义:脉冲宽度调制.它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术. PWM 是一种对模拟信号电平进行数字编码的方法.通过高分辨率计数器的使用,方波的占空比被调制用来对 ...

  8. Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed character class near index 0 [ ^

    Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed character clas ...

  9. 5分钟实现Android中更换头像功能

    写在前面:更换头像这个功能在用户界面几乎是100%出现的.通过拍摄照片或者调用图库中的图片,并且进行剪裁,来进行头像的设置.功能相关截图如下: 下面我们直接看看完整代码吧: 1 2 3 4 5 6 7 ...

  10. JSP Request方法大全

    协议:request.getProtocol() 输出:HTTP/1.1 服务器信息 getServletConfig().getServletContext().getServerInfo() 输出 ...