注意

以下內容是大爺學習Javascript的整理筆記,絕無侵犯作者版權之意,如作者不希望小弟截錄,請來信告知謝謝。

以下內容截錄「Javascript大全-第五版」這本書。部份章節內容拷貝至其它網站,都在標題有文章的相關連結,如果內容有錯誤,麻煩還留個言,謝謝。

 

1.建構式

建構式的任務是初始化一個新建立的物件,在物件被使用前設定所需的任何屬性。
//定義建構式
//注意如何把this參考的物件初始化
function Rectangle (w, h){
    this.width = w;
    this.height = h;
    //這裡沒有回傳的敘述
}
//呼叫建構式建立兩個長方形物件
//把width和height傳給建構式
//這樣就可以適當地初始化每個新物件
var rect1 = new Rectangle(2, 4); //rect1 = { width:2, height:4 };
var rect2 = new Rectangle(8.5, 11); // rect2 = { width:8.5, height:11};

2.原型與繼承
每 個Javascript 物件包含一個內部參考指標指到另一個物件(稱為其原型物件 (prototype object))。任何原型物件的屬性,看起來就像是受其原型而成之物件的屬性。另一種說法是,Javascript 物件是從原型物件 繼承 (inherit)它的屬性。


3.物件屬性、物件method、prototype 屬性、prototype method、類別屬性、類別method的分別
如果在「建構式內」定義物件的屬性和method,則「每個新增的物件」都會擁有自已屬性和method,而這就是「物件屬性」和「物件method」例如建構式:
function Rectangle (w, h){
    this.width = w; //物件屬性
    this.height = h; //物件屬性

    //物件method
    this.area = function () {
        return this.width * this.height; 
    }
}

//有了這個建構式之後,就可以這樣的新增物件
var r = new Rectangle(8.5, 11);
var a = r.area(); //a = 8.5 * 11 //呼叫物件method

從上面的例子來看,每個新增的物件都會包含三個屬性,width、height和area method。

但是因為一般的物件定義的method都是固定的,如果每個物件都新增一次像上面area 的method,是很沒效率的。
所以這裡就會提到prototype object這個原型物件。prototype object很像是「新增的物件的父類別」,可是又不真的是。


只是我們新增的物件會去「繼承」這個prototype object,所以我們可以將我們要通用的method定義在這個物件上面,這樣
就能達到「共用method的方法了」,也就是只會產生一次這個method,這樣就能增加效率了。

上面的範例可以改成這樣:
function Rectangle(w, h){
    this.width  = w; //每個物件都有自已的寬
    this.height = h; //每個物件都有自已的高
}

//將共用的method定義在prototype object這個類似父類別裡面
Rectangle.prototype.area = function (){
    return this.width * this.height; //別忘了要加「this」這個關鍵字。
}

這樣一來在新增物件之後,都會共用area這個method。


那什麼又是「類別屬性」、「類別method」呢?
「類別屬性」和「類別method」是跟「類別本身」有關的(而不是和實體),「類別屬性」和「類別method」是要由類別本身呼叫,而不是透過類別的特定實體呼叫。一定要用「建構式物件」呼叫它,例如:Date.parse()。

類別屬性」和「類別method」都是屬於「廣域的」,所以它的優點是不會被其他同名的變數或method所覆寫。

類別屬性」和「類別method的作用通常是為了提供「共享的資料」或「工具方法」,例如將數學常用常數或計算公式,以類別宣告並撰寫,之後您可以把這個類別當作工具,透過「類別名稱」來管理與取用這些靜態資料或方法,例如像J2SE (這邊是以Java語言來提供例子)所提供的Math類別上,就有Math.PI這個靜態常數,以及Math.Exp()Math.Log()Math.Sin()等靜態方法可以直接使用,另外還有像Integer.parseInt()Integer. MAX_VALUE等也都是靜態方法與靜態資料成員的實際例子。

一般來說,實體的method(物件method或prototype method)和類別的method採用的時機是,如果是要「存取物件內的屬性」,則用「實體的method(物件method或prototype method)」,換句話說就是若採用這種方式,就不需要再傳入額外的參數。而用「類別屬性則相反」,是「處理需傳入物件的參數」。

不過這個論點是參考書上範例的解釋,我也不太確定是不是這種用法,書裡面也沒講的很清楚,不過我想程式是活的,反正就是看自已喜歡怎麼用,就怎麼用囉,如果還有看到其他人寫的文章有講到這一塊再來做補充囉。

以下是截錄書裡的範例
物件method:

Complex.prototype.magnitude = function() {
    return Math.sqrt(this.x*this.x + this.y*this.y); //實體method在this關鍵字上運作。
}

類別method:
Complex.sum = function (a, b){
    return new Complex(a.x + b.x, a.y + b.y); //類別method不用this關鍵字,只在參數上處理

}


4.私有成員
javascript要將屬性變成私有的比較麻煩,需要透過特別的accessor method來予以讀寫。而這個方式就是「用closure來模擬」。不過要是使用這種方式的話,accessor method就必須儲存在各個物件實體,沒辨法用原型物件繼承,也就是說不能用prototype method。

function ImmutableRectangle(w, h){
    //這個建構式沒有把寬和高的屬性,儲存在它初始化的物件內,相反的,只在物件內定義 accessor method。
    //這些是closure 的 method,width 和 height 的值存放在它們的範圍鏈
    this.getWidth = function () { return w; }
    this.getHeight = function () { return h; }
}

// 注意,類別可以在原型物件存放一般的 method。
ImmutableRectangle.prototype.area = function(){
    return this.getWidth() * this.getHeight();
}


5.超類別與子類別
Javascript 支援的是原型繼承,而不是類別繼承,不過Javascript還是有對應類別階層的機制。在Javascript中,Object類別是最一般化的,而其他所有的類別,都是此類別的特化版本或子類別。換句話說,Object是所有內建類別的超類別,全部的類別都從Object繼承了一些基本的method。

繼承Javascript類別範例
先定義一個包含長、寬的類別,然後再定義一個包含描述它的位置屬性的PositionedRectangle類別。

//一個簡單的 Rectangle 類別
// 它有 widht 和 height 屬性,可算出它的面積。
function Rectangle ( w, h){
    this.width = w;
    this.height = h;
}

Rectangle.prototype.area = function () { return this.width * this.height; } 

//繼承Rectangle的PositionedRectangle類別
function PositionedRectangle(x, y, w, h){
    //利用「call method」,才能把 constructor 作為物件的 method 呼叫,並初始化。
   Rectangle.call(this, w, h); //在新物件上呼叫超類別建構式,以便初始化width、height,這稱為建構式之鏈    

    //儲存長方形左方角的位置
    this.x = x;
    this.y = y;
}

//要繼承 Rectangle 類別,必須明確地建立原型物件
PositionedRectangle.prototype = new Rectangle();

//如果不想繼承某些屬性,則可刪除它
delete PositionedRectangle.prototype.width;
delete PositionedRectangle.prototype.height;

//由於原型物件是以 Rectangle() 建構式建立的,它含有建構式屬性指向它的建構式。
//如果想讓PositionedRectangle物件擁有不同的建構式屬性,需再分配這個預設的建構式屬性
PositionedRectangle.prototype.constructor = PositionedRectangle;

//設定好原型物件後,就能將實體 method 加給子類別
PositionedRectangle.prototype.contains = function( x, y){
    return ( x > this.x && x < this.x + this.width &&  y > this.y && y < this.y + this.height);
}

//定義PositionedRectangle
var r = new PositionedRectangle(2,2,2,2);
print ( r.contains (3,3); //呼叫實體 method
print(r.area()); //呼叫繼承的實體 method

//使用類別實體欄位
print ( r.x + "," + r.y + "," + r.width + "," + r.height);


6.建構式之鏈
在範例顯示出,positionedRectangle() 建構式必須明確地呼叫類別建構式。這種就稱為建構式之鏈 ( constructor chaining) ,也可加入 superclass 屬性名稱,來簡化建構式之鏈:

//改寫上面範例的建構式之鏈部份
function PositionedRectangle ( x, y, w, h){
    this.superclass (w, h );
    this.x = x;
    this.y = y;
}


7.呼叫覆寫Method
method 覆寫另一個 method 通常是要「增加覆寫 method」的功能,而非完全予以取代,因為 method 必須能夠呼叫其所覆寫的method。就某種意義而言,這也算是一種method之鏈。

假設Rectangle定義了一個toString() method

PositionedRectangle.prototype.toString = function() {
    return "[" + this.width + "," + this.height + "]";
}

引用超類別的屬性:
PositionedRectangle.prototype.toString = funciton() {
    return "(" + this.x + "," + this.y + ")"  + 
    Rectangle.prototype.toString.apply(this); //連結至超類別
}

或是:
PositionedRectangle.prototype.toString = function(){
    return "(" + this.x + "," + this.y + ")" + 
    this.superclass.prototype.toString.apply(this);
}


8.不用繼承的擴充
因為Javascript 函式是資料值,所以可以從一個類別複製(或「借用」)函式,在另一個類別中使用。

借用method到另一個類別中使用:
//引數必須是類別的建構式。
//內建型態 (諸如:Object、Array、Date、以及RegExp) 的 method 是不可列舉的,因此,無法在此 method 內被借用。
function borrowMethods ( borrowFrom, addTo) {
    var from = borrowFrom.prototype; //借用的原型物件
    var to = addTo.prototype;             //要擴充的原型物件

    //讀遍所有的原型物件
    for(m in from) {
        if (typeof from[m] != "function" ) continue; //略過非函式
        to[m] = from[m]; //複製 method
    }
}

很多method都和定義的類別相關,所以在其他類別內是沒有用的。如果是設計成「可以借的類別」,就稱為「mixin類別」。



9.確認物件型態
typeof 運算子主要是用於區別基本型態和物件,不過typeof有些奇怪的地方。
typeof null 是 "object"。
typeof undefined 是 "undefined"。
任何陣列的型態都是 "object",因為所有的陣列都是物件。
任何的函式型態都是 "function" 即使函式是物件也是。


10. instanceof 和建構式
一但確定一個值是「物件」,而不是基本型態或函式,就可以使用「instanceof 運算子」,來進一步確認。
例如,如果 x 是陣列,則下面的程式會求出true:

x instanceof Array

instanceof 的左邊是「要測試的值」,而右邊則是「定義類別的建構式」。
注意到,物件是其自已的類別和任何超類別的實體。所以,對任何物件 o 來說, o instanceof Object 一定會是 true。
instanceof 也適用於函式,所以對任何函式 f 來說,下面三條運算式都會是true:

typeof f == "function"
f instanceof Function
f instanceof Object

如果想測試物件是否為特定類別的實體,而不是某個子類別的實體,則可以檢查它的constructor 屬性

var d = new Date() ;                          //Date物件,Date物件是擴充至 Object
var isobject = d instanceof Object     //值為true
var realobject = d.constructor == Object //值為false


11.Object.toString() 供物件定型之用
instanceof 運算子」和「constructor 屬性」的缺點是「只能針對已知的類別去測試物件」。對於檢視未知的物件而言沒有什麼用處。
但在除錯時,可以這麼做。有個技巧是「使用Object.toString()預設實作的method」。

Object 有個定義預設的 toString() method。任何類別若沒有定義這個 method,就會繼承 Object 預設的實作 method。
而「預設的 toString() method」有個特徵是「會顯示有關內建物件的一些內資型態資訊」。
ECMAScript的規格要求預設的toString() method 一定要傳回下列形式的字串:

    [object Class]

class 是物件的內部型態,通常會對應至該物件的建構式名稱。
例如,陣列的 class 為 "Array" ,函式的 class 為 "Function",而Date 物件的 class 為 "Date"。 內建的 Math 物件的 class 為 "Math"。所有 Error 物件 ( 包括各種 Error 子類別實體 ) 的 class 都是 "Error" 。 
客戶端Javascript 物件以及任何由 Javascript 實作品所定義的其他物件,都會具有實作品所定義之 class (例如 "window"、"Document" 、或 "Form" )。
然而,使用者定義的型態物件的 class 一定是 "Object" ,所以這種 toString() 技巧只適用於「內建物件型態」而已。
因為多數的類別都會覆寫預設的toString() method,所以無法直接在物件上使用,而希望找到該 Class 名稱。
相反的,只能指向 Object.prototype 中的預設函式,然後使用 apply(),在感興趣的型態的物件上啟用:

Object.prototype.toString.apply(o);     //總是啟用預設的 toString()

//強化typeof範例
function getType(x){
    //如果 x 為 null,就傳回 null
    if ( x == null ) return "null";
    
    //測試 typeof 運算子
    var t = typeof x;
    //如果不相等,則傳回
    if ( t != "object" ) return t;

    //否則, x 是物件。用預設的 toString() method 來取得物件的 class 值
    var c = Object.prototype.toString.apply ( x ); //傳回 "[object class]"
    c = c.substring(8, c.length - 1);                     //截取 "[object" 和 "]"
    
    //如果 class 不相等,就傳回。
    if (c != "Object") return c;

    //判斷到這裡,c 就是 "object" 。 檢查值 x 是否真為通用物件
    if (x.constructor == Object ) return c; //型態為 "Object"
        
    //就使用者定義的類別,尋找classname 的屬性
    if ("classname" in x.constructor.prototype &&  typeof x.constructor.prototype.classname == "string" )
        return x.constructor.prototype.classname;    

    //如果無法定義,則傳回未知型態
    return "<unknown type>";
}

12.鴨式定型 ( duck typing)

如果它走路像個鴨子,游起來像個鴨子,叫聲也像鴨子,那它就是鴨子」,這就稱為鴨式定型。意思是說:
如果物件擁有類別 X 所定義的屬性,那麼你可以將該物件視為類別 X 的實體,即使該物件實際上並非由 X() 建構式所生成的。
arrow
arrow
    全站熱搜

    大爺 發表在 痞客邦 留言(0) 人氣()