內容要點:
一.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) //結果應當永遠是“零”