JavaScript 具有實作富含物件導向特性程式的能力,但因為他的物件導向模型和一些常見語言不同,所以經常招來一些批評。
這篇文章首先會介紹物件導向的基本概念,接著藉由這些觀念來檢視 JavaScript 的物件導向模型。最後,將會以實際範例來做說明。
檢視 JavaScript
您可以先閱讀 重新介紹 JavaScript 來做預習。
物件導向程式設計
物件導向是一種使用抽象化概念表達現實世界的程式設計模型。他使用了諸如 modularity, polymorphism, 以及 encapsulation 等種種不同技術建立起整個體系。今天許多主流的程式語言 (如 Java, JavaScript, C#, C++, Python, PHP, Ruby 以及 Objective-C) 都支援了不同程度的物件導向程式設計。
不同於既有以 function (或甚至是 instruction )為主體聚合的程式設計模型,物件導向的程式設計是以物件(彼此間互有關聯)作為基礎。在物件導向的架構中,每個物件都具有收取訊息,處理資料以及發送訊息給其他物件的能力。每個物件都可視為獨一無二的個體,他們扮演不同的角色並有不同的能力及責任。
物件導向的設計模型企圖構建一個更有彈性且易於維護的設計方法,並且已經被許多大型專案所採用。由於特重於模組化的要求,採用此一設計方法的專案可以被設計的更為直觀。2
相關名詞
- Class
- 定義了物件的特性。
- Object
- 相對於描述性的 class,object 可以視為一個真實存在的個體。
- Property
- Object 具有的屬性,例如 color。
- Method
- Object 具有的能力,例如 walk。
- Constructor
- 用來初始化物件的 method 。
- Inheritance
- 繼承。指的是一個 class 可以承繼在其他 class 中規範的屬性及能力。
- Encapsulation
- 封裝。Class 規範了其相關物件該有哪些屬性,而 method 則律定了該如何執行。
- Abstraction
- 抽象化。藉由模擬方式以物件等機制來描述現實世界。
- Polymorphism
- 多型。不同的 class 可以具有一樣的 method 或 property。
更多的相關資訊可以看 Wikipedia 的 Object-oriented programming。
Prototype-based programming
Prototype-based programming 是一種特殊的物件導向模型。在這裡我們並沒有 class 的設計,取而代之的,reuse 的概念是藉由將現有 object 做為 prototype 的方式來達成(與其說是繼承,或許更近於複製)。這種模型可以被叫做 class-less, prototype-oriented, 或 instance-based programming。
最早的 prototype-based 語言可以回溯到由 David Ungar 和 Randall Smith 開發的 Self。近年來這種 class-less programming 風格被廣泛的接受,並被許多語言所支援,如 JavaScript, Cecil, NewtonScript, Io, MOO, REBOL, Kevo, Squeak。2
JavaScript 的物件導向程式設計
核心物件
JavaScript 預先定義了一些核心物件,例如 Math, Object, Array 以及 String。下面可以給你個關於該如何使用他們的例子。這是在 Math 中的 random():
alert(Math.random());
alert
可以使用 ! 他是一個可以彈出警告字樣的 method。參閱 Core JavaScript 1.5 Reference:Global Objects 可以看到這些預先定義物件的列表。
每個 object 都來自於 object Object,因此具有其一切
properties 及 methods。
使用者自定物件
The Class
JavaScript 是基於 prototype-based language 而設計的,這可能會讓許多慣於使用 C++ 或 Java 的人感到困惑。但其實,在某種程度上你可以把 function 看做是 class。例如你可以這樣做:
function Person() { }
The Object (Class Instance)
創建物件只需要使用 new 即可。這裡我們看到 person1 及 person2 這兩個來自 Person 的物件:
function Person() { } var person1 = new Person(); var person2 = new Person();
關於 Constructor
Constructor 會在物件被產生的那一刻被呼叫。在一般人熟知的物件導向語言 (如 C++) 中, constructor 是以一個 method 的狀態存在於 class 中。但在 JavaScript 中, function 取代了這個設計。因此我們並不需要刻意標示出 constructor method,他們總是在物件創建那一刻被執行。
Constructor 是用來設定物件的 property 或者呼叫 method,以讓未來物件能正確運作。
在下面的範例中, class Person
的 constructor 會提醒你他已經被創造:
function Person() { alert('Person instantiated'); } var person1 = new Person(); var person2 = new Person();
關於 Property (object attribute)
Property 在使用上可以想成是附著在 class 內部的變數。而當物件在執行時,可以使用 this 來代表「自己」。但當你位於物件外部時,可以透過類似
InstanceName.Property 的方式來使用之。這種設計類似於
C++, Java 以及諸多其他語言的設計。
下面這個範例中,我們在初始化時替 Person
class 增加了 gender
property:
function Person(gender) { this.gender = gender; alert('Person instantiated'); } var person1 = new Person('Male'); var person2 = new Person('Female'); //display the person1 gender alert('person1 is a ' + person1.gender); // person1 is a Male
關於 Method
在邏輯上來說, methods 有著類似於 property 的概念。不同點在於他們是可以執行的 function。在呼叫上他們和 property 的使用並無二致,但記得加上 () 來引入他們所帶的參數。在定義 method 的時候,要把他們連接在 prototype
property 上。
我們來看個範例比較容易理解,在這裡,我們替 Person
class 增加了一個 sayHello()
method:
function Person(gender) { this.gender = gender; alert('Person instantiated'); } Person.prototype.sayHello = function() { alert ('hello'); }; var person1 = new Person('Male'); var person2 = new Person('Female'); // call the Person sayHello method. person1.sayHello(); // hello
在 JavaScript 中,method 其實是以 function object 的形式存在 (但他們只能透過物件被使用)。參考下們這段程式:
function Person(gender) { this.gender = gender; } Person.prototype.sayGender = function() { alert(this.gender); }; var person1 = new Person('Male'); var genderTeller = person1.sayGender; person1.sayGender(); // alerts 'Male' genderTeller(); // alerts undefined alert(genderTeller === person1.sayGender); // alerts true alert(genderTeller === Person.prototype.sayGender); // alerts true
上頭這個例子告訴我們在 JavaScript 中並沒有 "per-object methods" 的存在,實際上他們都指向同一個位置 - 放在 prototype 中的那份。JavaScript 會把目前的 object context 連結到 this 這個特殊的變數上。這結果其實等於同於呼叫一個叫做 call 的特殊 method:
genderTeller.call(person1); //alerts 'Male'
繼承
繼承的機制使得你可以自一個原始版本的 class 衍生出更多特殊版本 class (在 JavaScript 中只支援 single class inheritance)。而些被衍生出來的 class 我們通常稱之為 child,想當然耳,最初那個 class 自然就是 parent。
prototype.constructor
see Core JavaScript 1.5 Reference:Global Objects:Object:prototype property, so we must state that manually. 下面這個例子我們創造一個來自 Person 的
Student
class。並且我們重新定義了 sayHello()
method 以及加上了 sayGoodBye()
method:
// define the Person Class function Person() {} Person.prototype.walk = function(){ alert ('I am walking!'); }; Person.prototype.sayHello = function(){ alert ('hello'); }; // define the Student class function Student() { // Call the parent constructor Person.call(this); } // inherit Person Student.prototype = new Person(); // correct the constructor pointer because it points to Person Student.prototype.constructor = Student; // replace the sayHello method Student.prototype.sayHello = function(){ alert('hi, I am a student'); } // add sayGoodBye method Student.prototype.sayGoodBye = function(){ alert('goodBye'); } var student1 = new Student(); student1.sayHello(); student1.walk(); student1.sayGoodBye(); // check inheritance alert(student1 instanceof Person); // true alert(student1 instanceof Student); // true
封裝
上面的範例中, Student
並不知道來自 Person
class 的 walk()
method 到底怎樣被實作,但他卻依舊可以使用他。除此之外,除非 Student
class 想要改變其內容,否則也不必特別宣告他。這個情況我們便稱之為封裝 (encapsulation)。
抽象化
Abstraction 是一個用來解決現有問題的重要機制。他可以藉由 inheritance (特殊化 specialization) 或 composition (物件合作)來達成。
JavaScript Function class 繼承 Object class。而 Function.prototype property 也是來自 Object (composition 的概念):
var foo = function(){}; alert( 'foo is a Function: ' + (foo instanceof Function) ); alert( 'foo.prototype is an Object: ' + (foo.prototype instanceof Object) );
多型
伴隨著繼承的機制,不同的 class 可以改寫同一個名稱的 method。 Method 會被侷限在他被定義的那個 scope。這種概念只發生在有繼承關係的 class 之間 (意思是說,如果沒有繼承關係,即使 method 同名,他們本就指向不同的功能,說不上有取代的發生)。
注意
本篇文章中所述的技術並非僅適用 JavaScript,而是在描述這種物件導向模型如何運作。你可以參考其他文章以得知更進階的資訊。
參考資料
- [] Mozilla. "Core JavaScript 1.5 Guide", http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide
- [] wikipedia. "Object-oriented programming", http://en.wikipedia.org/wiki/Object-...ed_programming
- Author(s): Fernando Trasviña <f_trasvina at hotmail dot com>
- Copyright Information: © 1998-2005 by individual mozilla.org contributors; content available under a Creative Commons license