內容要點:

一.JS中的類

1.JAVA或其他類似強型別 面嚮物件語言的 類成員的模樣

例項欄位:它們是基於例項的屬性或變數,用以儲存獨立物件的狀態。

例項方法: 它們是類的所有例項所共享的方法,由每個獨立的例項呼叫

類欄位:這些屬性或變數是屬於類的,而不是屬於類的某個例項的。

類方法:這些方法是屬於類的,而不是屬於類的某個例項的

2.JS中的類牽扯三種不同的物件,三種物件的屬性的行為和下面三種類成員非常相似:

建構函式物件:

之前提到,建構函式(物件)為JS的類定義了名字。任何新增到這個建構函式物件中的屬性都是類字元和類方法(如果屬性值是函式的話就是類方法)。

原型物件:

原型物件的屬性被類的所有例項所繼承,如果原型物件的屬性值是函式的話,這個函式就作為 類的例項的方法 來呼叫

例項物件:

類的每個例項都是一個獨立的物件,直接給這個例項定義的屬性是不會為所有例項物件所共享的。定義在例項上的非函式屬性,實際上是例項的欄位。

3.JS中定義類的步驟可以縮減為一個分三步演算法。

第一步,先定義一個建構函式,並設定初始化新物件的例項屬性。

第二步,給建構函式的prototype物件定義例項的方法。

第三步,給建構函式定義類欄位和類屬性。

我們可以將這三個步驟封裝進一個簡單的defineClass()函式中(這裡用到了例6-2中的extend()函式和例8-3中的改進版)

//一個用以定義簡單類的函式

function defineClass(constructor,  //用以設定例項的屬性的函式

methods, //例項的方法,複製至原型中

static)   //類屬性,複製至建構函式中

{

if (methods) extend( constructor,prototype,methods );

if (statics) extend( constructor,statics );

return constructor;

}

//這是Range類的另一個實現

var SimpleRange = defineClass(function(f,t){ this.f=f;this.t=t; },

{

includes:function(x){ return this.f <=x && x<=this.t;}

toString:function(){ return this.f + "..." +this.t; }

},

{

upto:function(t){ return new SimpleRange(o,t); }});

二.例9-3 Complex.js : 表示複數的類

/*Complex.js : 這個檔案定義了Complex類,用來描述複數。回憶一下,複數是實數和虛數的和,並且虛數i是-1的平方根*/

/*這個建構函式為它所建立的每個例項定義了例項欄位r和i,這兩個欄位分別儲存複數的實部和虛部,它們是物件的狀態*/

function Complex(real,imaginary){

if(isNaN(real) || isNaN(imaginary)) throw new TypeError();   //確保兩個實參都是數字,如果不都是數字則丟擲錯誤

this.r = real; //複數的實部

this.i = imaginary; //複數的虛部

}

/*類的例項方法 定義為原型物件的函式值屬性,這裡定義的方法可以被所有例項繼承,併為它們提供共享的行為。需要注意的是,JS類額例項方法必須使用關鍵字this來存取例項的欄位。*/

//當前複數物件加上另一個物件,並返回一個新的計算和值後的複數物件

Complex.prototype.add = function(that){ return new Complex(this.r + that.r , this.i + that.i); };

//當前複數乘以另一個複數,並返回一個新的計算乘積之後的複數物件

Complex.prototype.mul = function(that){ return new Complex(this.r * that.r - this.i*that.i , this.r*that.i + this.i*that.r); };

//計算複數的模,複數的模定義為原點(0 , 0)到複平面的距離

Complex.prototype.mag = function(){ return new Complex(this.r * this.r + this.i * this.i); };

//複數的求負運算

Complex.prototype.neg = function(){ return new Complex(-this.r , -this.i); };

//將複數物件轉換為一個字串

Complex.prototype.toString = function(){ return "{" +this.r + "," +this.i + "}"; };

//檢測當前複數物件是否和另外一個複數值相等

Complex.prototype.equals = function(that){ return that !=null &&          //必須有定義且不能是null

that.constructor === Complex &&     //並且必須是Complex的例項,

this.r === that.r && this.i === that.i; //並且必須包含相同的值

};

/*類欄位(比如常量)和類方法 直接定義為建構函式的屬性。需要注意的是,類的方法通常不使用關鍵字this,它們只對其引數進行操作*/

//這裡預定義了一些對複數運算有幫助的類欄位,它們的命名全都是大寫,用以表明它們是常量(在ES5中,還能設定這些類欄位的屬性為只讀)

Complex.ZERO = new Complex(0,0);

Complex.ONE = new Complex(1,0);

Complex.I = new Complex(0,1);

//這個類方法將由例項物件的toString方法返回的字串格式解析為一個Complex物件,或者丟擲一個型別錯誤異常

Complex.parse = function(s){

try{     //假設解析成功

var m = Complex._format.exec(s); //利用正則表示式進行匹配

return new Complex(parseFloat(m[1]),parseFloat(m[2]));

}catch(x){      //如果解析失敗則丟擲異常

throw new TypeError("Can't parse ' " + s + " ' as a complex number.");

}

};

//定義類的"私有欄位",這個欄位在Complex.parse()中用到了下劃線字首表明它是類內部使用的,而不屬於類的公有API的部分

Complex._format = /^\{(^,]+),([^}]+)\}$/;

程式碼分析:

從例9-3中所定義的Complex類可以看出,我們用到了建構函式、例項欄位、例項方法、類欄位和類方法,看一下這段例項程式碼:

var c = new Complex(2,3); //使用建構函式建立新的物件

var d = new Complex(c.i , c.r); //用到了c的例項屬性

c.add(d).toString(); //=>"{5,5}":使用了例項的方法

//這個稍微複雜的表示式用到了類方法和類欄位

Complex.parse(c.toString()).         //將c轉換為字串

add(c.neg()).          //加上它的負數

equals(Complex.ZERO)  //結果應當永遠是“零”