2建立自訂物件
JavaScript 定義了許多服務必要任務的物件:Math、String 等。但是,有時候我們希望能有自訂的物件類型,以便貼切地表示特定應用程式要處理的資料類型,在典型的 OOP 語言中,自訂物件透過類別的定義而完成,類別成為一種範本,然後程式碼產生該類別的實例──物件,因為 JavaScript 是原型化的 OOP 語言(不基於類別),所以需要不同的方法,對此有兩種選擇:
可依需求建立單個自訂物件,或者是同一個自訂物件的多個實例。
建立單個自訂物件
若想建立單個自訂物件,
只需要產生一個 Object 類型的新變數:
var employee = {
firstName: 'Joseph',
lastName: 'Doe',
department: 'Accounting',
hireDate: new Date(),
getName: function(){
return this.lastName + ', ' + this.firstName;
}
};
上述程式碼適用於多種情況,但是不能以這種方式建立兩個類似的自訂物件(例如,兩個單獨的員工)。
建立自訂物件的多個實例
建立自訂物件的替代方式是
使用一個函數作為物件產生器。下列程式重寫了員工範例:
function Employee(firstName, lastName, department){
this.firstName = firstName;
this.lastName = lastName;
this.department = department;
this.hireDate = new Date();
this.getName = function(){
return this.firstName + ' ' + this.lastName;
};
}
詳細了解發生的事情,首先,當成物件產生器的函數(稱為建構子函數)遵循慣例,名稱的第一個字元為大寫字母,其次,函數中透過 this 引用變數,本例的 this 始終引用目前的物件,最後,此函數的重要特徵是不返回任何值。
針對這個例子,預期目標是在第一次呼叫函數時傳入不同員工的內容,接著存放於變數中,Employee 函數本身也定義了一個函數
getName(),目的是回傳員工的名和姓,兩者之間以空格分隔。
函數的呼叫是透過 new 關鍵字建立一個物件:
var e1 = new Employee('Jane', 'Doe', 'Accounting');
上述程式中的 new 關鍵字,以具體的參數值產生一個 Employee 函數定義的副本。
可以利用下列程式建立更多的員工物件:
var e2 = new Employee('John', 'Smith', 'Human Resources');
使用建構子函數時一定要藉助 new 關鍵字,否則可能會發生奇怪的副作用!
現在便能使用物件的屬性和方法,就像對待其他物件一樣:
e1.firstName; //回傳 Jane
e2.getName(); //回傳 John Smith
實際上,整個概念和建立 String、Number 或 Object 變數的方法(但通常不這樣做)相同:
var lang = new String('JavaScript');
甚至可以將自訂物件傳遞給函數,就像其他任何物件一樣,底下是另一個物件的定義,其中一個方法以物件為參數:
function Department(name){
this.name = name;
this.employees = [];
this.addEmployee = function(emp){
this.employee.push(emp);
emp.department = this.name;
};
//另一種方案是依照索引位置返回員工
this.getEmployee(index){
return employees[index];
}
}
var hr = new Department('Human Resources'); //建立一個新物件
hr.addEmployee(e2); //新增一個 employee 物件
hr.employees[0].lastName; //回傳 Smith
hr.getEmployee(0).lastName; //一樣回傳 Smith
更完整的自訂物件
為了與 JavaScript 的其他物件有一致的行為,可以為自訂物件定義
toString() 和
valueOf() 方法,
toString() 方法總式返回物建的字串呈現形式,針對自訂物件,合適的呈現形式由開發人員決定:
function Employee(firstName, lastName, department){
this.toString = function(){
return 'Name: ' + this.firstName + ' ' + this.lastName + '\nDepartment: ' + this.department;
}
}
valueOf() 方法返回物件及其內容的最簡形式,對許多物件來說,這兩種方法返回相同的結果,但對某些物件而言則不然,例如,Date 的
valueOf() 方法返回一個數字:日期的時間戳記──從 UNIX 時間戳記以來的毫秒數,而 Date 物件的
toString() 方法則回傳一個代表日期和時間的字串。
就 Employee 物件而言,個人傾向以
valueOf() 返回員工姓名,而不加上任何標籤或附加資訊,如果合適,
valueOf() 方法也可以返回同樣唯一而有意義的值,例如員工的 ID 號碼。
整合
加強的任務管理應用程式的另一個版本,這個版本的每個任務都有三個屬性:任務名稱、優先順序、是否完成:
//定義 Task() 函數,將建立一個新的 Task 物件,它有兩個參數:任務名稱、優先順序
function Task(name, priority) {
'use strict';
//將參數指派給內部變數
this.name = name;
this.priority = priority;
this.completed = false;
//定義 toString() 函數,以「任務(優先順序)」的格式回傳任務
this.toString = function() {
return this.name + ' (' + this.priority + ')';
};
}
//定義視窗載入事件處理函數
window.onload = function(){
'use strict';
//取得各個元素的參照
var task = document.getElementById('task');
var priority = document.getElementById('priority');
var output = document.getElementById('output');
//宣告 tasks 變數,同腳本的前一個版本一般,這個陣列儲存所有任務
var tasks = [];
//定義處理表單提交的函數,當提交表單時建立一個新的 Task 物件,並新增到 tasks 陣列
document.getElementById('theForm').onsubmit = function() {
var t = new Task(task.value, priority.value); //建立一個新的 Task 物件
tasks.push(t); //並新增到陣列
//更新輸出 DIV,並完成兩個匿名函數
output.innerHTML = 'There are now ' + tasks.length + ' item(s) in the to-do list. Just added:
' + t.toString();
return false;
};
};