2015-05-03

Javascript 的 prototype

這裡沒有討論 Javascript 到底是不是一個物件導向的語言,只想去瞭解一下 Javascript 的 prototype 是什麼以及怎麼用。

拿物件導向的 Java 來比較一下,倒是一個不錯的出發點。

Java 用的是 class(類別),Javascript 用的是 prototype(原型)。

Java 是靜態的,Javascript 是動態的。

Java 有多型與繼承,Javascript 也有。

多才多藝的 function


Javascript 的 function 有很多種用法,可以是一般的 fucntion,也可以是 constructor(建構函式)。

function hello(name) {
  return 'Hello ' + name;
}

hello('Neil'); // 直接呼叫

function Book(title, author) {
  this.title = title; // instance 變數
  this.author = author; // instance 變數
}

var b = new Book('Javascript', 'Rhino'); // 透過 new 呼叫
hello() 是最常見的用法,而透過 new 呼叫的話,就變成是 constructor 了。

prototype


變身 constructor 以後,除了 function 裡可以用 this 將變數存下來以外,最重要的是多了一個 prototype 屬性,從這裡就開始進入 Javascript 的 prototype 世界了。

可以透過這個 prototype 定義共用的 function(我把這種 function 叫做 prototype function),就像是 Java class 裡的 method,在這些 function 裡可以透過 this 存取到 instance 變數或者其他 prototype function。
Book.prototype.toString = function() {
  return this.title + "/" + this.author; // 存取在 constructor 裡定義的 instance 變數
}

b.toString(); // Javascript/Rhino

Book.prototype.view = function() {
  return this.toString(); // 存取其他 prototype function
}

b.view(); // Javascript/Rhino

動態語言

Javascript 與 Java 很大不同的一點是,Javascript 是動態語言,可以邊跑邊組裝。

try {
  b.print();
}
catch (e) {
  console.log(e); // TypeError: b.print is not a function {stack: (...), message: "b.print is not a function"}
}

Book.prototype.print = function() {
  return '[' + this.title + '] written by ' + this.author;
};

console.log(b.print()); // [Javascript] written by Rhino
即使在 b 已經產生之後,再去修改 Book.prototype,b 馬上得到修改後的結果。

其實這樣講有點失真,正確的講法應該是 Javascript 在查找屬性或 function 時,是從 instance 開始找起,如果沒找到就往 prototype 找去,本身的 prototype 如果還是沒找到,就會往「Super class」的 prototype 找去,這裡我還不知道怎麼稱呼這個角色,就先借用 Java 的用詞,若以這裡的 Book 為例,就是指 Object.prototype,最常見的就是 toString() 與 valueOf() 這兩個來自 Object.prototype 的 function。

可以請瀏覽器顯靈給我們瞧瞧這神秘的過程。
console.log(b);


Book 的 prototype 是 Book.prototype,而 Book.prototype 的 prototype 就是 Object.prototype

多型與繼承

從這裡就可以看到 Javascript 的物件本質是有繼承的,而且也可以是多型。
console.log(b instanceof Book); // true
console.log(b instanceof Object); // true

接著來看看怎麼從 instance 取得 prototype。
console.log(b.prototype); // undefined
console.log(Object.getPrototypeOf(b) == Book.prototype); // true
console.log(b.__proto__ == Book.prototype); // true
prototype 屬性只能從 constructor Book 取得,不能從 instance b 取得,在 Javascript 1.5 以前可以用非標準的 __proto__ 屬性取得,在 Javascript 1.5 以後,就可以用標準的 Object.getPrototypeOf()  取得。

最後用 Java 的角度來描述一下 Javascript 的 prototype,Javascript 裡雖然沒有類別(class),但有類似的觀念,就是一個用來存放 instance 變數的 constructor function 加上用來設定共用 method 的 prototype 屬性



---
雖然一般人不會用 OOP 的方法來要求或使用 Javascript,這邊只是純粹好奇而已,那 Javascript 能實做出 Java 的 static method 和常數嗎?從前面的 Object.getPrototypeOf() 可以知道 static method 的確存在,只是我還不知道怎麼實做。

從前面的顯靈過程往下挖,的確可以找到一堆 static method。

改天再來看看怎麼做了!
---
---
---

沒有留言:

張貼留言