2009-07-25 14 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のデザインですが、完全を期すために、私はあなたが内からprivateへのアクセス

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

を持っているあなたは、民間と公共の「メンバー」の間で表示されます動作の違いを説明しますprivateの位置は、JSエンジンの静的な検索だけで十分に正確に判断できるため、関数のどれもがpublicにアクセスするよりもはるかに高速になります。 publicにアクセスしようとするとルックアップが必要になります。ほとんどの現代のJSエンジンはある程度のルックアップキャッシュを実行しますが、シンプルスコープのvarアクセス​​よりもまだ高価です。

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

同じルックアップ規則は、上記の変数と同じように、これらの機能に適用されますが、アクセス、(あなたの例では)唯一の本当の違いは、あなたの関数が呼び出される場合は、this.publicfn()privatefn()と言うことです、privatefnは常にグローバルを取得しますthisのオブジェクトです。誰かが

f = foo.publicfn; 
f(); 

を行う場合だけでなく、その後fへの呼び出しは、private変数を変更することができるようになりますthisとしてグローバルオブジェクトを持っていますが。

ただし、パブリック関数をより一般的に行う方法(プライベートメンバーの問題を修正する分離されたパブリック関数を解決する)は、パブリック関数をプロトタイプに入れることです。

個人情報を変更しないようにパブリック関数を強制
Foo.prototype.publicfn = function() { ... } 

- が、これはオプションではありませんいくつかの時間があるが、それはまた少しのメモリ使用量を減らすよう、それは良い習慣だ、取る:

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

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

対あなたがFoo1のすべてのインスタンスのための関数オブジェクト(すべてではないエモリー、単にオブジェクトのコピーを持って、例えば、 new Foo1().f !== new Foo2().f)、Foo2には単一の関数オブジェクトしかありません。

3

これまでのところうまくいきましたが、別のアクセスレベルがあります。

this.publicfnは、プライベートメンバーや機能にアクセスできるため、実際にはプライベートな方法です。

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

ノートこのメソッドはFooClassのプライベートメンバーにアクセスすることはできませんが、それはFooClassの任意のインスタンスを介してアクセス可能であること:、priveleged公共のではなく、あるメソッドを追加し、次のようにプロトタイプを変更するには

これを達成する別の方法は、基本的には、データのこれらのメソッドを使用すると、伝統的なOOP技術に準拠し、ヘルプを隠すコンストラクタ

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(); 

からこれらのメソッドを返しています。一般的に言えば、クラス内のデータ隠蔽とカプセル化を改善することは良いことです。低い結合性を保証することで、路面を変えることがずっと簡単になるので、できるだけ公に公開することが本当に効果的です。

簡単な概要については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

3

主な欠点は、FooClassのインスタンスごとにpublicfnのコピーがあることです。あなたはFooClassオブジェクトの多くを作成することがあります場合は、それはそれはあなたのニーズと相対的な性能に依存 書き込み

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

publicfnを作成する2つの方法は同等ではありません。 1つは有権者であり、もう1つは有権者ではない –

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"); 

をプライベートスコープのメンバーは、名前だけでプライベートなこの場合は、「_」プレフィックスは、プライベートメンバ変数を意味すると考えられています。メンバが本当にプライベートになるのを防ぐという欠点がありますが、オブジェクトのメモリ内のパッチ適用が可能です。もう1つの主な利点は、すべての関数がインタープリタとエンジンによって一度作成され、型に対して何度も再利用されることです。関数参照自体が同じであっても、 "this"キーワードは型のインスタンスを参照します。アクションの違いを見るために

一つの方法は、

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 
(あなたはASP.NET AJAXを持っていない場合、あなたは()registerClassを呼び出すFooClass2の最後の行を無視することができます)両方のタイプでこれを試してみることです

そのタイプの安全性とメンバーの可視性の問題パフォーマンスとメモリへのパッチ適用能力

0

prototypeを使用すると、後でコードに追加のメソッドを追加してexternスコープから機能を拡張することができます。小さなメリットとして、すべてのメソッドを含む全身が毎回レンダリングされることはないため、コンパイル時のパフォーマンスが向上します。すでに関数内に宣言されているメソッドがすべてある場合、これはレンダリングにもっと時間がかかります。だから、私のアドバイスは、現在のコードで関連するようになったときにだけ、後でこれらを適用してください。

例:

// 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(); 
関連する問題