2009-07-25 45 views
8

在這種模式下使用JavaScript「class」有什麼缺點嗎?JavaScript「classes」

var FooClass = function() 
{ 
    var private = "a private variable"; 
    this.public = "a public variable"; 

    var privatefn = function() { ... }; 
    this.publicfn = function() { ... }; 
}; 

var foo = new FooClass(); 
foo.public = "bar"; 
foo.publicfn(); 

回答

14

你在做什麼在你的例子不是「類」模式的人認爲在JS - 一般人所想的更「正常」類的Java/C#/ C++ /等模型,它可以是僞造圖書館。

而是你的榜樣其實是相當正常和良好的JS設計,但爲了完整性,我將討論行爲的差異,你會在私人和公共的「成員」之間看你有沒有

var private = "a private variable"; 
this.public = "a public variable"; 

從內部訪問private你的任何函數都比訪問public要快很多,因爲只要用JS引擎進行靜態查找,就可以合理確定private的位置。試圖訪問public需要查找,大多數現代JS引擎執行一定程度的查找緩存,但它比簡單的範圍內var訪問更昂貴。

var privatefn = function() { ... }; 
this.publicfn = function() { ... }; 

相同的查找規則適用於這些功能與上述變量的訪問中,唯一真正的區別(在你的例子)是,如果你的函數被調用,說privatefn() VS this.publicfn()privatefn總是會得到全球對象爲this。而且如果有人

f = foo.publicfn; 
f(); 

然後調用f將有全局對象爲this它將能夠修改private變量。

更正常的方式做公益功能(然而它解決了分離的公共職能修改private成員的問題)是把公共職能的原型,如。

Foo.prototype.publicfn = function() { ... } 

這迫使公共職能不修改個人信息 - 有一些時間在那裏,這是不是一種選擇,但它是很好的做法,因爲它也減少了內存使用咯,採取:

function Foo1() { 
    this.f = function(){ return "foo" }; 
} 

VS

function Foo2() { 
} 
Foo2.prototype.f = function(){ return "foo" }; 

Foo1你有函數對象的副本的Foo1每個實例(不是所有的埃默裏大學,只是對象,例如。 new Foo1().f !== new Foo2().f)而在Foo2中只有一個函數對象。

3

目前爲止還不錯,但還有其他的訪問級別被忽略。

this.publicfn真是priveleged方法,它可以訪問私有成員和職能。

要添加哪些是公共的,但不是priveleged方法,修改原型如下:

FooClass.prototype.reallypublicfn = function() { ... }; 

注意,這種方法並不能訪問FooClass的私有成員,但它是通過FooClass的任何實例訪問。

實現這一點的另一種方式是從構造

var FooClass = function() 
{ 
    var private = "a private variable"; 
    this.public = "a public variable"; 

    var privatefn = function() { ... }; 
    this.publicfn = function() { ... }; 

    return { 
    reallypublicfn: function() { ...} 
    } 
}; 

var foo = new FooClass(); 
foo.public = "bar"; 
foo.publicfn(); 

基本上返回這些方法,數據的這些方法隱藏幫助你堅持傳統的OOP技術。一般來說,在你的類中改進數據隱藏和封裝是一件好事。確保低耦合性使事情變得更容易,因此儘可能少地公開曝光對您而言是真正的好處。

關於如何完成這些事情詳見https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript一個簡單的概述和http://www.crockford.com/javascript/private.html

+1

你FooClass例如不正確。 new的語義是有效的:「var foo = {}; foo .__ proto__ = FooClass.prototype; var temp = FooClass.call(temp); if(IsObject(temp))foo = temp;」。這意味着在你的例子中,foo將是新對象「{reallypublicfn:function(){...}}」,所以它不會有FooClass原型或publicfn函數 – olliej 2009-07-25 11:15:23

3

主要的缺點是,你會得到每個FooClass實例的publicfn副本。如果您要創建大量FooClass的對象,這將是更有效地 寫

FooClass.prototype.publicfn = function() { ... }; 
+1

請注意,這兩種創建publicfn的方法是不等價的。一個是priveleged,另一個不是 – 2009-07-25 04:10:31

1

這取決於你的需求和相對錶現。 Javascript不是最安全的語言,對於會員的可見性並不強。傳統上,您可以在Javascript類型中擁有「私有」,「公共」特權「和」公共「可見性。

可以使用聲明的私人和公共特權的成員:

function FooClass() 
{ 
    var privateVar = 1; 
    function privateFn() 
    { 
     return privateVar; // etc... 
    } 
    this.publicVar = 2; 
    this.publicFn = function() 
    { 
     return privateFn(); 
    } 
} 

本示例使用功能關閉,它由一個函數聲明,其包括從其中函數定義的範圍的值。當成員可見性是必要的時候這是可以接受的,但可能導致開銷。由於JavaScript解釋器引用了外部作用域中的變量或函數,因此無法爲每個實例重用privateFn或publicFn定義。結果,FooClass的每個實例都會爲privateFn和publicFn提供額外的存儲空間。如果該類型使用頻繁或適度,則性能損失可忽略不計。如果該類型在頁面中經常使用,或者如果頁面更多的是「AJAX」樣式,由於頁面未被卸載,內存不會頻繁釋放,那麼懲罰就會更加明顯。

另一種方法是使用原型成員。這些都是沒有特權的公衆成員。由於Javascript並不是完全類型安全的,並且在加載後相對容易修改,因此鍵入安全性和成員可見性對於控制對內部類型的訪問來說並不可靠。出於性能原因,一些框架(如ASP.NET Ajax)使用成員命名來推斷可見性規則。例如,在ASP.NET AJAX同類型可能看起來像:

function FooClass2() 
{ 
    this._privateVar = 1; 
    this.publicVar = 2; 
} 
FooClass2.prototype = 
{ 
    _privateFn : function() 
    { 
     return this._privateVar; 
    }, 
    publicFn : function() 
    { 
     return this._privateFn(); 
    } 
} 
FooClass2.registerClass("FooClass2"); 

在這種情況下,私人範圍的成員是名存實亡的「_」前綴被認爲是指一個私有成員變量私有。它具有防止成員真正私有的缺點,但是允許內存修補對象的好處。另一個主要的好處是所有的函數都是由解釋器和引擎創建的,並且一遍又一遍地重複使用這個類型。即使函數引用本身相同,「this」關鍵字也會引用該類型的實例。在行動中看到的差別

一種方法是用兩種類型試試這個(如果你沒有ASP.NET AJAX,可以忽略FooClass2的最後一行調用的registerClass())

var fooA = new FooClass(), fooB = new FooClass(); 
alert(fooA.publicFn===fooB.publicFn); // false 

var foo2A = new FooClass2(), foo2B = new FooClass2(); 
alert(foo2A.publicFn===foo2B.publicFn); // true 

因此,它是一個類型安全和成員知名度vs.性能和在內存中修補的能力

0

另外,如果我可能會提到一些對此有用的東西 - 通過prototype您可以在代碼中添加其他方法以從外部範圍擴展功能。作爲一個微小的好處,所有方法的整個機構將不會每次渲染,因此它加快了編譯性能。如果您已經在函數中使用了所有已聲明的方法,則稍微需要更多時間才能呈現。所以,我的建議是,只有當它們在當前的代碼中變得相關時才應用它們。

實施例:

// inside 
function fn(arg) { 
    this.val = arg; 
    fn.prototype.getVal =()=> { 
     console.log(this.val); 
    } 
} 
var func = new fn('value'); 
func.getVal(); 


// declare extern methods 
function fn2(arg) { 
    this.val = arg; 
} 
fn2.prototype.getVal =()=> { 
    console.log(this.val); 
} 
var func2 = new fn2('value'); 
func2.getVal();