JavaScript 物件導向介紹

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();
請參閱 Object.create 以得到更多相關資訊。

關於 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'
更多資訊請參見 Function.call 及 Function.apply

繼承

  繼承的機制使得你可以自一個原始版本的 class 衍生出更多特殊版本 class (在 JavaScript 中只支援 single class inheritance)。而些被衍生出來的 class 我們通常稱之為 child,想當然耳,最初那個 class 自然就是 parent。

註: JavaScript does not detect the child class 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,而是在描述這種物件導向模型如何運作。你可以參考其他文章以得知更進階的資訊。

參考資料

  1. [] Mozilla. "Core JavaScript 1.5 Guide", http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide
  2. [] wikipedia. "Object-oriented programming", http://en.wikipedia.org/wiki/Object-...ed_programming

Original Document Information

  • 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

文件標籤與貢獻者

 此頁面的貢獻者: fscholz, jackblackevo, nightsnow0918, irvinfly, sailplaneTW
 最近更新: jackblackevo,