JavaScript定義類

參考了阮一峯老師的博客:http://www.ruanyifeng.com/blog/2012/07/three_ways_to_define_a_javascript_class.html

JavaScript語言部支持“類”,但是可以用一些變通的方法,模擬出“類”。

有三種方法:構造函數、Object.create()、極簡主義(推薦)

一、構造函數的方式

構造函數其實就是普通的函數,只不過在內部用this關鍵字指代實例對象,生成實例的時候需要使用new關鍵字,並且可以在構造函數的prototype對象上定義實例的公共屬性。

  function Cat() {
    this.name = "大毛";
  }

  var cat1 = new Cat();
  alert(cat1.name); // 大毛

  Cat.prototype.makeSound = function(){
    alert("喵喵喵");
  }

使用這種方法實現繼承,一共有5中方法:1.構造函數綁定;2. prototype模式; 3. 直接繼承prototype; 4. 利用空對象作爲中介; 5. 拷貝繼承

比如,現在有一個“動物”對象的構造函數

  function Animal(){
    this.species = "動物";
  }

還有一個“貓”對象的構造函數

  function Cat(name,color){
    this.name = name;
    this.color = color;
  }

如何讓“貓”繼承“動物”呢?

1.構造函數綁定

使用call或apply方法,將父對象的構造函數綁定在子對象上,即在子對象中加一行:

  function Cat(name,color){
    Animal.apply(this, arguments);
    this.name = name;
    this.color = color;
  }
  var cat1 = new Cat("大毛","黃色");
  alert(cat1.species); // 動物

2. prototype模式

把貓的prototyhpe對象,指向一個Animal的實例,那麼所有貓的實例,就能繼承Animal了。但是,任何一個prototype對象都有一個constructor,指向它的構造函數,爲了保證繼承鏈不紊亂,必須使新的prototype對象的constructor屬性指回原來的構造函數:

  Cat.prototype = new Animal();
  Cat.prototype.constructor = Cat;
  var cat1 = new Cat("大毛","黃色");
  alert(cat1.species); // 動物

  Cat.prototype = new Animal();

  Cat.prototype.constructor = Cat;

3. 直接繼承 prototype

這種方法是對方法2的改進,由於Animal對象中,不變的屬性都可以寫入Animal.prototype。所以,可以讓Cat直接繼承Animal.prototype

  function Animal(){ }
  Animal.prototype.species = "動物";

  Cat.prototype = Animal.prototype;
  Cat.prototype.constructor = Cat;
  var cat1 = new Cat("大毛","黃色");
  alert(cat1.species); // 動物

與方法2對比,這樣做不用建立Animal實例,所以省內存,效率比較高,缺點是Cat.prototype和Animal.prototype指向了同一個對象,那麼任何對於Cat.prototype的修改,都會反應到Animal.prototype。

所以上述代碼中Cat.prototype.constructor = Cat;是有問題的,這一句把Animal.prototype的constructor屬性也改了!

4. 利用空對象作爲中介

針對方法3的缺點,可以利用一個空對象作爲中介

  var F = function(){};
  F.prototype = Animal.prototype;
  Cat.prototype = new F();
  Cat.prototype.constructor = Cat;

F是空對象,所以幾乎不佔內存,可以把上述方法封裝成一個函數,便於使用:

  function extend(Child, Parent) {

    var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype;
  }

使用的時候方法如下:

  extend(Cat,Animal);
  var cat1 = new Cat("大毛","黃色");
  alert(cat1.species); // 動物

這個extend函數,就是YUI庫如何實現繼承的方法。

另外 Child.uber = Parent.prototype;意思是爲字段想設置一個uber屬性,uber是一個德語詞,意思是“上一層”。等於在子對象上打開一條通道,可以直接調用父徐i想的方法。這一行只是爲了實現繼承的完備性,純屬備用性質。

5. 拷貝繼承

把父對象的所有屬性和方法,拷貝進子對象,也能實現繼承

  function Animal(){}
  Animal.prototype.species = "動物";

  function extend2(Child, Parent) {
    var p = Parent.prototype;
    var c = Child.prototype;
    for (var i in p) {
      c[i] = p[i];
      }
    c.uber = p;
  }

  extend2(Cat, Animal);
  var cat1 = new Cat("大毛","黃色");
  alert(cat1.species); // 動物

二、 Object.create()

用這個方法,類是一個對象,而不是函數。這個方法不能實現私有屬性和私有方法,實例對象之間也不能共享數據,對類的模擬不夠全面。

  var Cat = {
    name: "大毛",
    makeSound: function(){ alert("喵喵喵"); }
  };

  var cat1 = Object.create(Cat);
  alert(cat1.name); // 大毛
  cat1.makeSound(); // 喵喵喵

如果使用版本比較低的瀏覽器:

  if (!Object.create) {
    Object.create = function (o) {
       function F() {}
      F.prototype = o;
      return new F();
    };
  }

三、極簡主義

它也是用一個對象模擬類,這個類裏面,定義一個構造函數createNew(),用來生成實例。

  var Cat = {
    createNew: function(){
      // some code here
    }
  };


然後在createNew()裏,頂一個實例對象,把這個對象作爲返回值。

  var Cat = {
    createNew: function(){
      var cat = {};
      cat.name = "大毛";
      cat.makeSound = function(){ alert("喵喵喵"); };
      return cat;
    }
  };


要讓一個類繼承另外一個類,只要在前者的createNew()中調用後者的createNew()方法即可。

  var Animal = {
    createNew: function(){
      var animal = {};
      animal.sleep = function(){ alert("睡懶覺"); };
      return animal;
    }
  };

  var Cat = {
    createNew: function(){
      var cat = Animal.createNew();
      cat.name = "大毛";
      cat.makeSound = function(){ alert("喵喵喵"); };
      return cat;
    }
  };

  var cat1 = Cat.createNew();
  cat1.sleep(); // 睡懶覺


在createNew()方法中,只要不是定義在cat對象上的方法和屬性,都是私有的。

  var Cat = {
    createNew: function(){
      var cat = {};
      var sound = "喵喵喵";
      cat.makeSound = function(){ alert(sound); };
      return cat;
    }
  };

  var cat1 = Cat.createNew();
  alert(cat1.sound); // undefined


把需要共享的數據封裝在類對象的裏面、createNew()方法的外面即可。

  var Cat = {
    sound : "喵喵喵",
    createNew: function(){
      var cat = {};
      cat.makeSound = function(){ alert(Cat.sound); };
      cat.changeSound = function(x){ Cat.sound = x; };
      return cat;
    }
  };

  var cat1 = Cat.createNew();
  var cat2 = Cat.createNew();
  cat1.makeSound(); // 喵喵喵

  cat2.changeSound("啦啦啦");
  cat1.makeSound(); // 啦啦啦


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章