// public methods
this.method1 = method2;
this.method2 = function() {
alert(this.second);
}
// constructor
{
method1();
method2();
}
}
// public method
class1.prototype.method3 = function() {
alert(this.first);
}
var o = new class2();
o.method1();
o.method2();
o.method3();
alert(o.first);
我們發現這個例子是在 class1 的例子上做了一些補充。給它添加了公有實例字段和公有實例方法,我們把它們通稱為公有實例成員。
我們應該已經發現,創建公有實例成員其實很簡單,一種方式是通過在類中給 this.memberName 來賦值,如果值是函數之外的類型,那就是個公有實例字段,如果值是函數類型,那就是公有實例方法。另外一種方式則是通過給 className.prototype.memberName 賦值,可賦值的類型跟 this.memberName 是相同的。
到底是通過 this 方式定義好呢,還是通過 prototype 方式定義好呢?
其實它們各有各的用途,它們之間不是誰比誰更好的關系。在某些情況下,我們只能用其中特定的一種方式來定義公有實例成員,而不能夠使用另一種方式。原因在于它們實際上是有區別的:
1、prototype 方式只應該在類外定義。this 方式只能在類中定義。
2、prototype 方式如果在類中定義時,則存取私有實例成員時,總是存取最后一個對象實例中的私有實例成員。
3、prototype 方式定義的公有實例成員是創建在類的原型之上的成員。this 方式定義的公有實例成員,是直接創建在類的實例對象上的成員。
基于前兩點區別,我們可以得到這樣的結論:如果要在公有實例方法中存取私有實例成員,那么必須用 this 方式定義。
關于第三點區別,我們后面在討論繼承時再對它進行更深入的剖析。這里只要知道有這個區別就可以了。
我們還會發現,公有實例成員和私有實例成員名字是可以相同的,這樣不會有沖突嗎?
當然不會。原因在于它們的存取方式不同,公有實例成員在類中存取時,必須要用 this. 前綴來引用。而私有實例成員在類中存取時,不使用也不能夠使用 this. 前綴來存取。而在類外存取時,只有公有成員是可以通過類的實例對象存取的,私有成員無法存取。
2.3 公有靜態成員
公有靜態成員的定義很簡單,例如:
代碼如下:
class3 = function() {
// private fields
var m_first = 1;
var m_second = 2;
// private methods
function method1() {
alert(m_first);
}
var method2 = function() {
alert(m_second);
}
// constructor
{
method1();
method2();
}
}
// public static field
class3.field1 = 1;
// public static method
class3.method1 = function() {
alert(class3.field1);
}
class3.method1();
這個例子的 class3 跟 class1 很像。不同的是 class3 的外面,我們又給 class3 定義了一個靜態字段和靜態方法。
定義的方式就是給 className.memberName 直接賦值。
這里定義的靜態字段和靜態方法都是可以被直接通過類名引用來存取的,而不需要創建對象。因此它們是公有靜態成員。
不過有點要記住,一定不要將公有靜態成員定義在它所在的類的內部,否則你會得到非你所期望的結果。我們可以看下面這個例子:
代碼如下:
class4 = function() {
// private fields
var m_first = 1;
var m_second = 2;
var s_second = 2;
// private methods
function method1() {
alert(m_first);
}
var method2 = function() {
alert(m_second);
}
class4.method1 = function() {
s_second++;
}
class4.method2 = function() {
alert(s_second);
}
}
var o1 = new class4();
class4.method2(); // 2
class4.method1();
class4.method2(); // 3
var o2 = new class4();
class4.method2(); // 2
class4.method1();
class4.method2(); // 3
這個例子中,我們期望 s_second 能夠扮演一個私有靜態成員的角色,但是輸出結果卻不是我們所期望的。我們會發現 s_second 實際上是 class4 的一個私有實例成員,而不是私有靜態成員。而 class4 的 method1 和 method2 所存取的私有成員總是類的最后一個實例對象中的這個私有實例成員。
問題出在哪兒呢?
問題出在每次通過 new class4() 創建一個對象實例時,class4 中的所有語句都會重新執行,因此,s_second 被重置,并成為新對象中的一個私有實例成員。而 class4.method1 和 class4.method2 也被重新定義了,而這個定義也將它們的變量作用域切換到了最后一個對象上來。這與把通過 prototype 方式創建的公有實例方法定義在類的內部而產生的錯誤是一樣的。
所以,一定不要將公有靜態成員定義在它所在的類的內部!也不要把通過 prototype 方式創建的公有實例方法定義在類的內部!
那如何定義一個私有靜態成員呢?
2.4 私有靜態成員
前面在基本概念里我們已經清楚了,只有用 function 創建函數,才能創建一個新的作用域,而要創建私有成員(不論是靜態成員,還是實例成員),都需要通過創建新的作用域才能夠起到數據隱藏的目的。下面所采用的方法就是基于這一點來實現的。
實現私有靜態成員是通過創建一個匿名函數函數來創建一個新的作用域來實現的。
通常我們使用匿名函數時都是將它賦值給一個變量,然后通過這個變量引用該匿名函數。這種情況下,該匿名函數可以被反復調用或者作為類去創建對象。而這里,我們創建的匿名函數不賦值給任何變量,在它創建后立即執行,或者立即實例化為一個對象,并且該對象也不賦值給任何變量,這種情況下,該函數本身或者它實例化后的對象都不能夠被再次存取,因此它唯一的作用就是創建了一個新的作用域,并隔離了它內部的所有局部變量和函數。因此,這些局部變量和函數就成了我們所需要的私有靜態成員。而這個立即執行的匿名函數或者立即實例化的匿名函數我們稱它為靜態封裝環境。
下面我們先來看通過直接調用匿名函數方式來創建帶有私有靜態成員的類的例子:
代碼如下:
class5 = (function() {
// private static fields
var s_first = 1;
var s_second = 2;
// private static methods
function s_method1() {
s_first++;
}
var s_second = 2;
function constructor() {
// private fields
var m_first = 1;
javascript面向對象全新理練(二)
var m_second = 2;
// private methods
function method1() {
alert(m_first);
}
var method2 = function() {
alert(m_second);
}
// public fields
this.first = "first";
this.second = ['s','e','c','o','n','d'];
// public methods
this.method1 = function() {
s_second--;
}
this.method2 = function() {
alert(this.second);
}
// constructor
{
s_method1();
this.method1();
}
}
// public static methods
constructor.method1 = function() {
s_first++;
alert(s_first);
}
constructor.method2 = function() {
alert(s_second);
}
return constructor;
})();
var o1 = new class5();
class5.method1();
class5.method2();
o1.method2();
var o2 = new class5();
class5.method1();
class5.method2();
o2.method2();
這個例子中,通過
(function() {
...
function contructor () {
...
}
return constructor;
})();
來創建了一個靜態封裝環境,實際的類是在這個環境中定義的,并且在最后通過 return 語句將最后的類返回給我們的全局變量 class5,然后我們就可以通過 class5 來引用這個帶有靜態私有成員的類了。
為了區分私有靜態成員和私有實例成員,我們在私有靜態成員前面用了 s_ 前綴,在私有實例成員前面加了 m_ 前綴,這樣避免了重名,因此在對象中總是可以存取私有靜態成員的。
但是這種命名方式不是必須的,只是推薦的,私有靜態成員可以跟私有實例成員同名,在重名的情況下,在類構造器和在類中定義的實例方法中存取的都是私有實例成員,在靜態方法(不論是公有靜態方法還是私有靜態方法)中存取的都是私有靜態成員。
在類外并且在靜態封裝環境中通過 prototype 方式定義的公有實例方法存取的是私有靜態成員。
在靜態封裝環境外定義的公有靜態方法和通過 prototype 方式定義的公有實例方法無法直接存取私有靜態成員。
另外一種方式通過直接實例化匿名函數方式來創建帶有私有靜態成員的類的例子跟上面的例子很相似:
代碼如下:
new function() {
// private static fields
var s_first = 1;
var s_second = 2;
// private static methods
function s_method1() {
s_first++;
}
var s_second = 2;
class6 = function() {
// private fields
var m_first = 1;
var m_second = 2;
// private methods
function method1() {
alert(m_first);
}
var method2 = function() {
alert(m_second);
}
// public fields
this.first = "first";
this.second = ['s','e','c','o','n','d'];
// public methods
this.method1 = function() {
s_second--;
}
this.method2 = function() {
alert(this.second);
}
// constructor
{
s_method1();
this.method1();
}
}
// public static methods
class6.method1 = function() {
s_first++;
alert(s_first);
}
class6.method2 = function() {
alert(s_second);
}
};
var o1 = new class6();
class6.method1();
class6.method2();
o1.method2();
var o2 = new class6();
class6.method1();
class6.method2();
o2.method2();
這個例子的結果跟通過第一種方式創建的例子是相同的。只不過它的靜態封裝環境是這樣的:
new function() {
...
};
在這里,該函數沒有返回值,并且對于 class5 的定義是直接在靜態封裝環境內部通過給一個沒有用 var 定義的變量賦值的方式實現的。
當然,也完全可以在
(function() {
...
})();
這種方式中,不給該函數定義返回值,而直接在靜態封裝環境內部通過給一個沒有用 var 定義的變量賦值的方式來實現帶有私有靜態成員的類的定義。
這兩種方式在這里是等價的。
2.5 靜態類
所謂的靜態類,是一種不能夠被實例化,并且只包含有靜態成員的類。
在 JavaScript 中我們通過直接實例化一個匿名函數的對象,就可以實現靜態類了。例如:
代碼如下:
class7 = new function() {
// private static fields
var s_first = 1;
var s_second = 2;
// private static method
function method1() {
alert(s_first);
}
// public static method
this.method1 = function() {
method1();
alert(s_second);
}
}
class7.method1();
大家會發現,class7 其實就是個對象,只不過這個對象所屬的是匿名類,該類在創建完 class7 這個對象后,就不能再被使用了。而 class7 不是一個 function,所以不能夠作為一個類被實例化,因此,這里它就相當于一個靜態類了。
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com