-
OOP
- Object-oriented Language,物件導向程式語言
-
特性
- 封裝Encapsulation
- 繼承Inheritance
- 多形Polymorphism
-
Object(物件)
- 讓正真有需要的原始碼接觸資料,減少資料曝露的程度
- 程式碼再利用
- 物件結合資料與行為,建立一種新的資料型別,
其中可以儲存(store)資料,也可以根據資料而行動(act),
所以物件是個容器,其中儲存資料,並聯繫資料與依據資料行動的原始碼
-
資料(變數)+行為(函式)=物件
- 物件內的資料稱「property」(屬性、特性)
- 物件內的行為稱「method」(方法)
- property + method = Object
-
.(點)
- 點號運算子('.' dot operator)用於取用物件property與method
-
Object + . + property/method
- mail.from = "...";
mail.to = "...";
...
mail.send();
-
物件可以包含其他物件
- var mail = new Mail(new Date(), "from", ...);
- 在Mail建構式中傳入一個Date物件
- 物件Object在一個儲存容器內聯繫起變數與函式
-
建構式(constructor)
-
property
- 物件具有資料,資料必須在物件建立時「初始化」,每個自訂物件都需要自己的建構式,名稱與「物件相同」。
-
function Mail(from,to,body,subject){
this.from = from;
this.to = to;
this.body = body;
this.subject = subject;
}
- 建構式使用首字母大寫
-
建立property需使用JavaScript的關鍵字「this」,
關鍵字this區分物件property與一般變數
- this指定物件property的擁有權,同時設定property的初始值
- 建立屬於「這個(this)」物件的property
- this.from = from;
- 將收到的參數指定給新property
- 沒有this,建構式不會知道你正在建立物件property
-
以建構式建立物件時,我們使用new運算子,它呼叫物件的建構式,開啟物件建立過程。
- var mail = new Mail("from","to","body","subject");
-
var mail
- 新物件儲存在變數裡
-
new Mail(...)
- new運算子建立新Mail物件(呼叫建構式)
-
"from","to","body",...
- 傳入參數給建構式,以設定property
-
method
-
function Mail(from,to,body,subject){
// Object method
this.send = function(){
...
}
}
-
該如何知道指令碼(函式)應放入method?
- method就是根據物件的property而採取某些行動,
換言之,如果指令碼需要取用物件內部資料,就非常
合適放入method
-
把函式轉為方法
- 宣告method
- 使用this建立method,與建立property類似
- this.send = function(){
}
- 把現有程式碼移入method裡
- 修改使用物件property
-
關於選用功能
- function Mail(from,to,body,subject,smtp,port){
...
//smtp與port為選用功能
this.smtp = smtp;
this.port = port;
}
- 當某個參數未傳入給function、method、constructor時,
在任何試圖使用參數值的原始碼裡,它的值都是null,
在缺少參數相對應的property將會被設為null
-
確認選用參數在function參數清單的最後面
- 例如:當funcion有兩個參數
- 兩個都傳入
- 只傳入第一個
- 兩個都不傳
- 無法只傳第二個
-
複本
-
Mail物件的send()方法
- Mail物件的method是在建構式裡,利用關鍵字this建立,但依此建立的Mail物件都會各有一份物件方法的複本
- 關鍵字"this"用於設置「instance」擁有的property和method
-
var mail1 = new Mail(...);
var mail2 = new Mail(...);
var mail3 = new Mail(...);
- 每個Mail物件(mail1,mail2,mail3),都會有自己的property與method複本
-
等於會有三份method
- 這是浪費又沒效率
- property為每個物件儲存獨特資料,method應該為物件所共享
-
Class與instance
-
類別Class,是物件的描述,樣版、藍圖
- function Mail(...)
-
實例Instance,實際物件,依Class建立
- var mail = new Mail(...);
- mail是依Mail來建立的物件(instance)
- 每個instance的property多半都不一樣,
所以每個instance才需要有自己的property複本,
但method沒有必要複製到每個instance中
-
Class擁有method
-
class-owned instance method
- 當method為Class所擁有時,所有該Class的instance都可以取用此method,而不需要另外複製一份
-
在Class使用prototype
-
在JavaScript中每個物件都有一個prototype物件(以property存在),
prototype用以設定Class level(類別層級)的property與method,
而非屬於instance
- 也就是說,我們需要把method的「擁有權」指派給Class,而不是指派小單一instance
-
Mail.prototype.send = function(){}
- prototype使用需在建構式之外
- send()方法直接附加到Mail class本身,而非隸屬於某個instance,
這樣無論Mail class建立多少份instance,都只會有一份send()方法
- 當send()接受呼叫時,將在class內執行
- mail1.send();
mail2.send();
mail3.send();
- 當其他Mail物件的instance也呼叫了send()方法,
仍會執行Class內的同一方法
-
class property
- 類別屬性,此property只需要在類別儲存一次,但能被所有instance存取
- Mail.prototype.smtp = "smtp.kkbruce.com";
Mail.prototype.port = "25";
- 此property只有一個值,讓所有instance共享
- 想成,它是class的全域變數,但只能讓class對應的instance取用
-
標準物件能也使用prototype
- 每個物件都有prototype,它允許在Class Level增加property與method
-
範例:擴充Date物件
- Date.prototype.shortFormat = function(){
return this.getFullYear() +'/'+(this.getMonth()+1)+'/'+ this.getDate();
}
- var d = new Date();
d.shortFormat();
- 傳回「年/月/日」格式日期
-
Class自己的method
-
類別方法,class method
- 它屬於Class,只能取用class property,不能取用instance property
-
建立class method
- 直接為Class設定method,而不使用prototype
- Mail.showSMTP = function(){
alert('SMTP:'+Mail.prototype.smtp+' ,\n Port:'+Mail.prototype.port);
}
...
Mail.showSMTP();
- Mail.showSMTP
- 建立Mail物件的showSMTP方法
- Mail.prototype.smtp
- 為了從class method取用class property,你必須下探至prototype
- smtp是個Mail class property,所以Mail class method可以取用
- Mail.showSMTP();
- 直接透過Class名稱Mail來呼叫方法
-
回到建構式
- 讓建構式專注在property的建立與初始化
-
OOP範例
-
ajax物件
-
function AjaxRequest() {
if (window.XMLHttpRequest) {
try {
this.request = new XMLHttpRequest();
} catch(e) {
this.request = null;
}
} else if (window.ActiveXObject) {
try {
this.request = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
this.request = null;
}
}
if (this.request == null)
alert("建立Ajax物件錯誤.\n" + "細節:" + e.message);
}
- AjaxRequest.prototype.send = function(type, url, handler, postDataType, postData) {
//type, url, handler為get必要參數
//postDataType, postData為post必要參數
if (this.request != null) {
this.request.abort();
url += "?dummy=" + new Date().getTime();
try {
this.request.onreadystatechange = handler;
this.request.open(type, url, true); // always asynchronous (true)
if (type.toLowerCase() == "get") {
this.request.send(null);
} else {
this.request.setRequestHeader("Content-Type", postDataType);
this.request.send(postData);
}
} catch(e) {
alert("Ajax程式錯誤.\n" + "細節:" + e);
}
}
}
- AjaxRequest.prototype.getReadyState = function() {
return this.request.readyState;
}
- AjaxRequest.prototype.getStatus = function() {
return this.request.status;
}
- AjaxRequest.prototype.getResponseText = function() {
return this.request.responseText;
}
- AjaxRequest.prototype.getResponseXML = function() {
return this.request.responseXML;
}
-
宣告
- var ajax = new AjaxRequest();