4

関数のプロトタイプ オブジェクトと "new" キーワードを使用するか、コンストラクター関数を完全に使用しないかを決定するのを手伝ってください。

状況:

widget()ページ上の各ウィジェットを初期化するために 10 ~ 15 回呼び出される関数。widget()かなりの数の内部メソッドが含まれています。

が呼び出されるたびwidget()に、関数はウィジェットを操作するための API として機能するオブジェクトを返す必要があります。

質問

1) すべての内部メソッドをWidget()そのプロトタイプ プロパティの下に配置しますか? widget()意味がありませんが、これの主な理由は、呼び出されるたびに内部関数を再インスタンス化しないためです。

しかし、内部関数をプロトタイプに入れると、インスタンス化された各wオブジェクト ( w = new Widget();) は内部プライベート メソッドにアクセスできます。

2) コンストラクター関数とキーワードから離れて、new以下のようにコードを構造化する場合、呼び出されるたびに内部関数が再インスタンス化されるというパフォーマンスの問題を修正するにはどうすればよいですかwidget()

function widget()
{
   var returnObj = {};

   /* Add internal functions but this will be re-instantiated every time */

   return returnObj;

}
4

2 に答える 2

4

ここで少しトレードオフがあります。すでに理解しているように、に配置するメソッド.prototypeは公開されていますが、メソッドを配置する最も効率的な場所は、そのオブジェクトのすべての新しいコピーに非常に効率的な方法で自動的に追加されるためです。.prototypefor メソッドを使用する場合、メソッドのコピーは 1 つだけであり、その単一のコピーへの参照が、そのオブジェクトのすべての新しいインスタンス化に自動的に追加されます。

ただし、javascript にはプライベート メソッドが組み込まれておらず、その唯一の回避策は.prototype、プライベート メソッドを呼び出す必要があるメソッドやメソッドに対して を使用しないことです。

Doug Crockford によるこの記事は、任意のオブジェクトのデータまたはメソッドのプライバシーを作成する方法について非常によく説明しています。

newどちらの場合でも、キーワードを使用して新しいオブジェクトを作成することを避ける理由はありません。.prototypeいずれかまたはプライベート メソッドを で動作させることができますnew

ただし、真にプライベートなメソッドを実現したい場合は.prototype、プライベート メソッドまたはそれらにアクセスする必要があるメソッドのいずれにも使用できないため、どちらが重要かを判断する必要があります。プライバシーの必要性は状況によって異なるため、正解は 1 つではありません。

私のコーディングでは、通常、プライバシーを強制せず、 と を使用.prototypenewます。名前をアンダースコアで始めることにより、プロトタイプで「非パブリック」メソッドを指定します。これは表記規則であり、アクセス強制スキームではありません。

newオペレーターの回避とメソッドの再インスタンス化に関する 2 番目の質問への回答として、なぜこれを行っているのかをお聞きしたいと思います。あなたは何を得ていますか?を使用することの欠点は認識していませんnew。コンストラクターでメソッドを使用するか、手動で作成/割り当てするかについてのあなたの決定は.prototype、プライベートメソッドの必要性に関するものであるべきだと私は理解しています。

参考までに、ここでは 15 個のオブジェクトがパフォーマンスに大きな違いをもたらすことはほとんどありません。真のプライバシーの必要性を評価し、それに基づいて決定を下します. プライバシーを強制する必要がある場合は、クロックフォード メソッドを使用してプライベート メソッドを実装します。真のプライバシーを確​​保する必要がない場合は、 を使用して.prototypeください。newどちらの場合でも使用を避ける理由はここにはありません。

于 2012-04-16T16:41:24.467 に答える
0

metaconstructor *パターンを使用して、これを回避できます。

function defineCtor(metaCtor) {

  var proto = new metaCtor();

  var ctor = proto.hasOwnProperty('constructor') ? 
             proto.constructor : new Function();

  return (ctor.prototype = proto).constructor = ctor;

}

これで、コンストラクターを作成する(または、より正確にプロトタイプを作成してコンストラクターを返す)関数ができました。

var Widget = defineCtor(function() {

  function doInternalStuff() {

    // ...cant see me

  }

  // this function ends up on the prototype

  this.getFoo = function() { return doInternalStuff(); };

});

// ...

var myWidget = new Widget();

説明

defineCtorプロパティとして単一の無名関数を取ります。で関数を呼び出し、newオブジェクトを作成します。オブジェクトを新しいコンストラクター関数(空の関数、または生成されたプロトタイプオブジェクト自体のconstructorプロパティ)のプロトタイププロパティとして割り当て、その関数を返します。

これにより、内部関数が閉じられ、質問1に対処し、コンストラクターとプロトタイプのペアを設定して、質問2に対処します。


比較

defineCtorこの手法を次の2つの例と比較してください。

この例ではプロトタイプを使用しており、問題1があります。内部のものがカプセル化されていません。

function Widget(options) {
  this.options = options;
}

Widget.prototype = {

  getFoo: function() {
    return doInternalStuff();
  }

};

// How to encapsulate this?
function doInternalStuff() { /* ... */ }

この例では、すべてをコンストラクターに設定しますが、問題2があります。オブジェクトを作成するたびに、プロパティごとに新しい関数オブジェクトをインスタンス化します。

function Widget(options) {

  this.options = options;

  function doInternalStuff() { /* ... */ }

  this.getFoo = function() {
    return doInternalStuff();
  };

}

この例では、上記の手法を使用して、プロトタイプを活用しながらカプセル化を提供します。

var Widget = defineCtor(function() { 
  //                    ^
  // This function runs once, constructing the prototype.

  // In here, `this` refers to the prototype.

  // The real constructor.
  this.constructor = function(options) {

    // In function properties, `this` is an object instance 
    // with the outer `this` in its prototype chain.

    this.options = options;

  };  

  function doInternalStuff() { /* ... */ }

  this.getFoo = function() { return doInternalStuff(); };

});

// ...

var myWidget = new Widget();

このアプローチにはいくつかの利点があり、他のアプローチよりもすぐに明らかなものもあります。

  • カプセル化を提供します。これを行うには、最初の「比較」の例をすぐに呼び出される関数でラップしますが、このアプローチは、チーム設定でよりクリーンで「強制」される可能性があります。

  • それは拡張可能です。「metaconstructor」関数に、「extends」、「mixin」などの関数プロパティを使用して独自のプロトタイプを与えることができます。次に、の本体の中に、のmetaCtorようなものを書くことができますthis.extends(BaseWidget)。これdefineCtorを行うためにAPIを変更する必要はありません。

  • これは、Google Closureコンパイラ、Eclipse、jsdocなどを「トリック」して、「メタ関数」ではなく実際のコンストラクター関数を定義していると思い込ませます。これは、特定の状況で役立つ場合があります(コードは、これらのツールが理解できる方法で「自己文書化」されています)。

*私の知る限り、「メタコンストラクター」という言葉は完全に構成されています。

于 2012-04-16T16:59:24.287 に答える