《JavaScript高级程序设计》--继承

来源:互联网 发布:毛昆仑 常凯申 知乎 编辑:程序博客网 时间:2024/05/29 20:01

最近看《JavaScript高级程序设计》看到了继承一章, 在JavaScript中也是非常重要的一个概念, 所以觉得需要在这里总结一下. 继承的方式一共有6种:1. 原型链 2.借用构造函数 3.组合继承 4.原型式继承 5.寄生式继承 6.寄生组合式继承

一. 原型链

其基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法, 实现方法如下:

  1. function SuperType(){
  2.    this.property = true;
  3. }
  4. SuperType.prototype.getSuperValue = function(){
  5.    return this.property;
  6. };
  7. function SubType(){
  8.    this.subproperty = false;
  9. }
  10. //继承了SuperType
  11. SubType.prototype = new SuperType();
  12. SubType.prototype.getSubValue = function(){
  13.    return this.subproperty;
  14. }
  15. var instance = new SubType();
  16. alert(instance.getSuperValue());                   //true

缺点: 1. 通过原型链的话, 超类型的引用类型的属性会被子类型的所有实例共享, 那么改变其中一个子类型实例的属性就会使所有实例的属性都一起发生变化.

2. 在创建子类型的实例时, 无法向超类型的构造函数中传递参数, 因此在实践中很少会单独使用原型链

二. 借用构造函数

基本思想: 在子类型的构造函数的内部调用超类型构造函数, 实现方法如下:

  1. function SuperType(name){
  2.    this.name = name;
  3. }
  4. function SubType(){
  5.    //继承了SuperType, 同时传递了参数
  6.    SuperType.call(this, "Nicholas");
  7.    this.age = 29;
  8.    this.sayName = function(){
  9.         alert(this.name);
  10.     }
  11. }
  12. var instance = new SubType();
  13. alert(instance.name);         //Nicholas
  14. alert(instance.age);            //29
  15. var sub1 = new SubType();
  16. var sub2 = new SubType();
  17. console.log(sub1.sayName === sub2.sayName); //false

缺点:方法都在构造函数中定义, 因此函数复用就无从谈起(17行的结果是false), 而且在超类型的原型中定义的方法, 对子类型而言也是不可见的, 结果所有的类型都只能使用构造函数模式, 因此借用构造函数的技术也是很少单独使用的.

三. 组合继承:

基本思路是: 使用原型链实现对原型属性和方法的继承, 而通过借用构造函数类实现对实例属性的继承, 实现方法如下:

  1. function SuperType(name){
  2.    this.name = name;
  3.    this.colors = ['red', 'blue', 'green'];
  4. }
  5. SuperType.prototype.sayName = function(){
  6.    alert(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.sayAge = function(){
  15.    alert(this.age);
  16. }

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

四. 原型式继承

思路:借助原型可以基于已有的对象创建新对象, 同时还不比因此创建自定义类型, 实现方法如下:

  1. function object(o){
  2.    function F(){};
  3.    F.prototype = o;
  4.    return new F();
  5. }
  6. var person = {
  7.    name: "Nicholas",
  8.    friends: ["Shelby", "Court", "Van"]
  9. };
  10. var anotherPerson = object(person);
  11. anotherPerson.name = "Greg";
  12. anotherPerson.friends.push("Rob");
  13. alert(person.friends);              //Shelby, Court, Van, Rob, Barbie

缺点:包含引用类型值的属性始终都会共享相应的值, 就像使用原型模式一样

五. 寄生式继承

基本思路:创建一个仅用于封装继承过程的函数, 该函数在内部以某种方式来增强对象,最后返回对象, 实现方式如下:

  1. function createAnother(original){
  2.    var clone = object(original);
  3.    clone.sayHi = function(){
  4.       alert("hi");
  5.    };
  6.    return clone;
  7. }

缺点:由于不能做到函数复用而降低效率

六. 寄生组合式继承

前面说过, 组合继承是JavaScript中最常用的继承模式, 不过他也有自己的不足. 组合继承最大的问题就是无论什么情况下, 都会调用两次超类型构造函数, 而寄生组合式继承就要解决这个问题, 提高效率. 基本思路: 不必为了指定子类型的原型而调用超类型的构造函数, 我们所需要的无非就是超类型原型的一个副本而已. 本质上, 就是使用寄生式继承来继承超类型的原型, 然后将结果指定给子类型的原型. 实现方式如下:

  1. function inheritPrototype(subType, superType){
  2.    var prototype = object(superType.prototype);        //原型链方式
  3.    prototype.constructor = subType;
  4.    subType.prototype = prototype;
  5. }
  6. function SuperType(name){
  7.    this.name = name;
  8.    this.colors = ['red', 'blue', 'green'];
  9. }
  10. SuperType.prototype.sayName = function(){
  11.    alert(this.name);
  12. }
  13. function SubType(name, age){
  14.    SuperType.call(this, name);
  15.    this.age = age;
  16. }
  17. inheritPrototype(SubType, SuperType);
  18. SubType.prototype.sayAge = function(){
  19.    alert(this.age);
  20. }

这个例子的高效率体现在它只调用了一次SuperType构造函数, 并且因此避免了在SubType.prototype上面创建不必要的, 多余的属性. 在此同时, 原型链还能保持不变. 开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式.

0 0