js框架開發之旅--原型和類

不是所有的js框架都有類的概念, Douglas Crockford在他的Classical Inheritance in JavaScript中討論了基於類的對象模型。這是一個非常精彩的關於如何實現js繼承的討論。後來,他寫了另一篇文章Prototypal Inheritance in JavaScript,他的結論是:不使用類對象模型,僅通過原型,也能完整的實現js繼承。

至於爲什麼js類庫都提基於類的對象模型?具體的原因也許只有作者自己才清楚。大概一些人喜歡模仿他原來喜歡的語言的對象模型。Prototype受了Ruby的嚴重影響,提供了Class來封裝自己的代碼。實際上,Prototype也僅是自己框架本身在使用Class。
這篇文章我將介紹基於原型的繼承和基於類的繼承,並且通過類來實現面向對象的js。這些類會在我們的框架turing.js中使用。


類和原型

一些語言把一切都作爲對象來處理,數字也是對象,字符串也是對象,類也是對象。但是不要把所有的對象都作爲類的實例,我們是面向對象的編程不是面向類的對象。

js正的需要類嗎?如果你是Java或Ruby程序員,你會驚訝發現js根本沒有class這個關鍵詞。沒關係,如果需要的話,我們可以自己構建一些類似的功能。


原型的使用

基於原型的繼承看上去是這樣的:
function Vector(x, y) {
  this.x = x;
  this.y = y;
}

Vector.prototype.toString = function() {
  return 'x: ' + this.x + ', y: ' + this.y;
}

v = new Vector(1, 2);
// x: 1, y: 2 


如果你沒有接觸過js對象編程,頭幾行可能讓你感到奇怪。我定義了一個叫Vector的函數,然後用new Vector()調用它。實際上是創建了一個新的對象然後調用函數Vector,this就用來指向新的對象。
prototype屬性用來擴展實例的方法。通過這種方式,如果你已經實例化了一個對象,然後再prototype裏添加新的屬性,以前的實例也會加上同樣的屬性。有意思吧?
Vector.prototype.add = function(vector) {
  this.x += vector.x;
  this.y += vector.y;
  return this;
}

v.add(new Vector(5, 5));

// x: 6, y: 7

基於原型的繼承

js實現繼承沒有一個固定的方式,如果我們想通過繼承Vector創建一個Point類,可以像下面這樣:
function Point(x, y, colour) {
  Vector.apply(this, arguments);
  this.colour = colour;
}

Point.prototype = new Vector;
Point.prototype.constructor = Point;

p = new Point(1, 2, 'red');
p.colour;
// red
p.x;
// 1

通過使用apply,Point可以執行Vector的構造函數。你可能想知道prototype.constructor是怎麼來的。constructor這是一個prototype的屬性,用來指向創建這個對象的函數。
當你創建一個你自己的對象,你同時也從Object繼承了一些方法,如toString和hasOwnProperty:
p.hasOwnProperty('colour');
// true

基於原型還是基於類

通過原型實現繼承有好幾種方式,我們需要對這些方式進行封裝,然後提供一個統一的接口。這樣可以保持代碼的簡潔性和可讀性。

像上面這樣把js的繼承分幾行寫,在視覺上顯得非常混亂。我們最好通過明確的開頭和結尾把代碼包起來。我們這個框架也一樣,通過類可以把它包裝起來。


類的設計

上面的代碼在Prototype是這樣的:
Vector = Class.create({
  initialize: function(x, y) {
    this.x = x;
    this.y = y;
  },

  toString: function() {
    return 'x: ' + this.x + ', y: ' + this.y;
  }
});

Point = Class.create(Vector, {
  initialize: function($super, x, y, colour) {
    $super(x, y);
    this.colour = colour;
  }
});

我們現在用自己的代碼來實現這些功能。我們把代碼要提供的功能點列一下:
  1. 通過拷貝給類繼承新的方法
  2. 類的創建:使用apply和prototype.constructor運行構造函數。
  3. 決定是否繼承自某個父類的能力
  4. 封裝這些功能


繼承

你可能注意到,extend在Prototype中頻繁的使用,它所做的工作就是在不同的prototype之間進行方法的拷貝。這是我們瞭解prototype如何使用的最好的途徑,其實原理和我們想象的一樣簡單:
for (var property in source)

  destination[property] = source[property];

類的創建

我們使用create方法來創建一個新類。該方法要像之前的例子一樣,讓類有繼承的能力。
// This would be defined in our "oo" namespace
create: function(methods) {
  var klass = function() { this.initialize.apply(this, arguments); };

  // Copy the passed in methods
  extend(klass.prototype, methods);

  // Set the constructor
  klass.prototype.constructor = klass;

  // If there's no initialize method, set an empty one
  if (!klass.prototype.initialize)
    klass.prototype.initialize = function(){};

  return klass;
}

獲取代碼

我已經爲框架增加了一個基礎文件,它通過類實現了面向對象。代碼可以在github上下載到:

turing.oo.js


牧客網--讓自由職業變成一個靠譜的工作


發佈了16 篇原創文章 · 獲贊 87 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章