跟着书本学JavaScript设计模式(继承)

122 阅读3分钟

JavaScript(JS) 继承

JS继承是指子对象可以通过继承父对象的方式调用父对象的共有属性和共有方法,主要的实现继承的方式有以下几种:


1.子类的原型对象--类式继承

子类通过原型(prototype)绑定父类实例的方式实现继承,这种方式叫做类似继承。

    // 声明父类
    function SuperClass() {
      this.superValue = true;
    }
    // 为父类添加共有方法
    SuperClass.prototype.getSuperValue = function () {
      return this.superValue;
    }

    // 声明子类
    function SubClass() {
      this.subValue = false;
    }

    // 继承父类
    SubClass.prototype = new SuperClass();
    //  为子类添加共有方法
    SubClass.prototype.getSubValue = function () {
      return this.subValue;
    }

这种继承方式有两个缺点

  1. 由于子类通过其原型prototype对父类实例化,继承了父类,所以说父类中的共有属性钥匙引用类型,就会在子类中被所有实例共用。一个子类实例个更改子类原型从父类构造函数中继承来的共有属性就会直接影响到其他子类。
  2. 由于子类实现的继承是靠原型prototype对父类的实例化实现的,因此在创建父类的时候,无法向父类船机参数,因此在实例化父类的时候也无法对父类构造函数内的属性进行初始化。
function ParentClass() {
    this.books = ['JavasScript', 'html', 'css']; // this.book 是一个数组属于引用类型
}

function ChildClass() { };
ChildClass.prototype = new ParentClass();
var instance1 = new ChildClass();
var instance2 = new ChildClass();
console.log(instance2.books); // ["Javascript", "html", "css"]
instance1.books.push('设计模式');
console.log(instance2.books); //  ["Javascript", "html", "css", "设计模式"]

2.创建即继承-构造函数继承

通过call方法改变函数的作用环境,因此在子类中,通过调用call方法,就相当于在子类中父类调用了这个方法,然后将子类的变量传入父类进行this绑定属性。

    function SuperClass(id) {
      // 引用类型共有属性
      this.books = ['JavaScript', 'html', 'css'];
      // 值类型共有属性
      this.id = id;
    }

    // 父类声明原型方法
    SuperClass.prototype.showBooks = function () {
      console.log(this.books);
    }

    function SubClass(id) {
      SuperClass.call(this, id);
    }
    var instance1 = new SubClass(10);
    var instance2 = new SubClass(11);
    instance1.books.push("设计模式");
    console.log(instance1.books); // ["JavaScript", "html", "css", "设计模式"]
    console.log(instance1.id); // 10
    console.log(instance2.books); //  ["JavaScript", "html", "css"] 
    console.log(instance2.id); // 11
    instance1.showBooks(); // TypeError: instance1.showBooks is not a function

缺点:这种继承方式没有设计到原型prototype,所以在父类中的原型方法,子类不能进行调用


3.将优点为我所用-组合继承

将类式继承与构造函数继承进行组合


    // 声明父类
    function FatherClass(name) {
      this.name = name;
      this.books = ['html', 'css', 'JavaScript'];
    }

    // 父类原型共有方法
    FatherClass.prototype.getName = function () {
      console.log(this.name);
    }

    //声明子类
    function SonClass(name, time) {
      FatherClass.call(this, name);
      this.time = time;
    }
    SonClass.prototype = new FatherClass();
    SonClass.prototype.getTime = function () {
      console.log(this.time);
    }
    var son1 = new SonClass('js book', 2014);
    son1.books.push('设计模式');
    console.log(son1.books); // ["html", "css", "JavaScript", "设计模式"]
    son1.getName(); // js book
    son1.getTime(); //2014

    var son2 = new SonClass('css book', 2013);
    console.log(son2.books); // ["html", "css", "JavaScript"]
    son2.getName(); // css book
    son2.getTime(); // 2013

缺点:子类call方法时调用了一次父类构造函数,prototype继承时又调用了父类构造函数,重复调用了两次父类构造函数。


4.洁净的继承者-原型式继承

通过创建一个过渡类进行缓存,每次进行创建是通过过渡类进行创建,开销相对重复调用构造函数小。

    function inheritObject(o) {
      function F() { }
      F.prototype = o;
      return new F();
    }

    var book = {
      name: "js book",
      alikeBook: ["css book", "html book"]
    };
    var newBook = inheritObject(book);
    newBook.name = "ajax book";
    newBook.alikeBook.push("xml book");

    var otherBook = inheritObject(book);
    otherBook.name = "flash book";
    otherBook.alikeBook.push("as book");

    console.log(newBook.name);
    console.log(newBook.alikeBook);
    console.log(otherBook.name);
    console.log(otherBook.alikeBook);
    console.log(book.name);
    console.log(book.alikeBook);

缺点:无法添加基类的其他属性和方法,需要提前定义好基类。


5.如虎添翼-寄生式继承

通过这种方式是原型式继承的封装,这样可以在新创建的对象中新增新的属性和方法;


    // 声明基类
    var book = {
      name: "js book",
      alikeBook: ["css book", "html book"]
    };

    function createBook(obj) {
      var o = new inheritObject(obj);

      o.getName = function () {
        console.log(name);
      }

      return o;
    }

6.终极继承者-寄生组合式继承

是构造函数继承与寄生式继承的组合

    function inheritPrototype(subClass, superClass) {
      // 复制一份父类的原型副本保存在变量中
      var p = inheritObject(superClass.prototype);
      // 修正重写子类原型导致子类的constructor属性被修改
      p.constructor = subClass;
      // 设置子类原型
      subClass.prototype = p;
    }

    function SuperClass(name) {
      this.name = name;
      this.colors = ["red", "blue", "green"];
    }

    SuperClass.prototype.getName = function () {
      console.log(this.name);
    };

    function SubClass(name, time) {
      SuperClass.call(this.name);
      this.time = time;
    }

    inheritPrototype(SubClass, SuperClass);
    SuperClass.prototype.getName = function () {
      console.log(this.time);
    }

    var instance1 = new SubClass("js book", 2014);
    var instance2 = new SubClass("css book", 2013);

    instance1.colors.push("black")

    console.log(instance1.colors);
    console.log(instance2.colors);
    instance1.getName();
    instance2.getName();