15

これらの 4 つのパターンのいずれかを他のパターンよりも使用することを選択した場合、ボンネットの下に重要な/微妙な/重要な違いはありますか? そして、オペレーターとObject.create()対を介して「インスタンス化」された場合、それらの間に違いはありますか?new

CoffeeScript1) 「クラス」定義を翻訳するときに使用するパターン:

Animal = (function() {

  function Animal(name) {
    this.name = name;
  }

  Animal.prototype.move = function(meters) {
    return alert(this.name + (" moved " + meters + "m."));
  };

  return Animal;

})();

Knockout2)促進すると思われるパターン:

var DifferentAnimal = function(name){

    var self = this;

    self.name = name;

    self.move = function(meters){
        return alert(this.name + (" moved " + meters + "m."));
    };

}

3) 私がよく見た同様の単純なパターン:

var DifferentAnimalWithClosure = function(name){

    var name = name;

    var move = function(meters){

    };

    return {name:name, move:move};

}

Backbone4) 以下を促進するパターン:

var OneMoreAnimal= ClassThatAlreadyExists.extend({

    name:'',
    move:function(){}

});

更新 1:エリアスの応答に応じてパターン #2 を変更し、パターン #3 を追加 // マイナーフォーマット

4

2 に答える 2

8

明確にするために、JSはクラスを認識せず、オブジェクトとカスタムの自己定義コンストラクター関数のみを認識しますが、それは重要なことではありません。
簡単に言うと、あなたの質問に答えるには、そうです。ここに投稿する新しいオブジェクトを作成するさまざまな方法の間には、いくつかの小さな違いがあり、かなり大きな違いさえあります。

CoffeeScript:
これは実際には、独自のコンストラクターを作成するための最も明確で従来の方法ですが、(オプションの)クロージャー変数を使用するようにセットアップできるという意味で「最適化」されています。
基本的に、このコードが行うことは、IIFEを使用して、コンストラクター定義proptotypeメソッドの割り当ての両方を、新しいコンストラクターへの参照を返す独自のプライベートスコープでラップすることです。クリーンでシンプルなJSであり、自分で書いたものと何ら変わりはありません。

ノックアウト:
少なくとも、あなたが提供するスニペットは、モジュールパターンの一部か、パワーコンストラクターのように見えるので、これは私を少し投げました。ただし、を使用していないためstrict mode、を省略しても危険な状況になります。また、関数全体がの新しいインスタンスをnew作成する手間を省くため、2番目のオブジェクトリテラル作成し、その2番目のオブジェクトにすべてのプロパティを割り当てます。 、何かが足りないと思います。なぜなら、ここで最後のステートメントを省略しても、おそらくまったく違いはないでしょう。さらに、ご覧のとおり、本質的にコンストラクターであるメソッド()を宣言しています。これはDifferentAnimalDifferentAnimalreturn {};movemoveインスタンスには、プロトタイプから取得するのではなく、独自の関数オブジェクトが割り当てられます。
要するに、このスニペットをどこから入手したかをもう一度詳しく見て、これがフルバージョンであるかどうかを再確認してください。フルバージョンの場合は、これに対する議論しか見ることができないためです。

コンストラクター内で定義された変数の使用は単純です。クロージャ。プロパティが、いくつかの引数によって決定され、そのコンストラクターに渡される明確な初期状態を持っていると仮定します。

function MyConstructor(param)
{
     var paramInit = param/2;//or something
     this.p = paramInit;//this property can change later on, so:
     this.reInit = function()
     {//this method HAS to be inside constructor, every instance needs its own method
         this.p = paramInit;//var paramInit can't, it's local to this scope
     };
}
var foo = new MyConstructor(10);
console.log(foo.p);//5
foo.p = 'hi';
console.log(foo.p);//hi
foo.reInit();
console.log(foo.p);//5
console.log(foo.paramInit);//undefined, not available outside object: it's a pseudo-private property

本当にそれだけです。pplが何かを使用しているのを見ると、それは多くの場合、 (参照とは何ですか? 1つ以外のオブジェクトに適用されたときにメソッドは何をすべきか)var that = this;の頭痛の種に対処することなく、どこでも利用できるメインオブジェクトへの参照を作成することですもともとは?etceteraを対象としていました...)thisthis

バックボーン:
ここでは、別のケースを扱っています。オブジェクトの拡張(つまり、メソッドの使用、既存の「クラス」(コンストラクター)または特定のインスタンスのプロパティ)は、単にオブジェクトを作成することと同じではありません。
ご存知のように、JSオブジェクトにはいつでも新しいプロパティを割り当てることができます。これらのプロパティも削除できます。場合によっては、プロトタイプのプロパティをインスタンス自体で再定義できます(プロトタイプの動作をマスクします)など。したがって、結果のオブジェクト(指定されたインスタンスを拡張する新しく作成されたオブジェクト)をどのように表示するかによって異なります。インスタンスからすべてのプロパティを取得する必要がありますか、それとも両方のオブジェクトが将来的に同じプロトタイプを使用するようにしますか?
これらは両方とも単純なJSを使用して実現できますが、自分で作成するにはもう少し手間がかかります。ただし、次のように書くと、次のようになります。

function Animal(name)
{
    this.name = name;
}
Animal.prototype.eat= function()
{
    console.log(this.name + ' is eating');
};

これは、書くことと同等と見なすことができます。

var Animal = Object.extend({name:'',eat:function()
{
    console.log(this.name + ' is eating');
}});

はるかに短いですが、コンストラクターがありません。

newvsObject.create
ええと、それは簡単なことです。Object.createそれははるかに強力ですnew。代わりに、オブジェクトを作成する必要があるときに、プロトタイプのメソッド、プロパティ(天気を含む、列挙可能かどうかなど)を定義できます。コンストラクターとプロトタイプを作成するか、オブジェクトリテラルを作成して、これらすべてのObject.defineProperty行をいじくりまわす必要があります。
欠点:まだ一部の人々ECMA5準拠のブラウザーを使用していません(IE8はまだ完全に機能していません)。私の経験では、しばらくするとかなりのスクリプトをデバッグするのが非常に難しくなります。通常のコンストラクターよりもパワーコンストラクターを使用する傾向がありますが、スクリプトの最上部に明確で明確な定義があります。非常にわかりやすい名前ですが、オブジェクトリテラルは私が「オンザフライ」で作成するものです。を使用してObject.create、オブジェクトリテラルであるかのように、実際のオブジェクトリテラルとして認定するには実際には少し複雑すぎるオブジェクトを作成する傾向があることに気付きました。

//fictional example, old:
var createSomething = (function()
{
    var internalMethod = function()
    {//method for new object
        console.log(this.myProperty || '');
    };
    return function(basedOn)
    {
        var prop, returnVal= {};
        returnVal.myProperty = new Date();
        returnVal.getCreated = internalMethod;//<--shared by all instances, thx to closure
        if (!basedOn || !(basedOn instanceof Object))
        {//no argument, or argument is not an object:
            return returnVal;
        }
        for (prop in basedOn)
        {//extend instance, passed as argument
            if (basedOn.hasOwnProperty(prop) && prop !== '_extends')
            {
                returnVal[prop] = basedOn[prop];
            }
        }
        returnVal._extends = basedOn;//<-- ref as sort-of-prototype
        return returnVal;
    };
}());

これはかなり冗長ですが、基本的なコンストラクターを用意しました。これを使用して、既存のインスタンスを拡張することもできます。単純に書くのはそれほど冗長ではないように思われるかもしれません。

var createSomething = Object.create(someObject, {getCreated:function()
{
    console.log(this.myProperty);
},
myProperty:new Date()});

しかし、IMOの場合、これにより、どのオブジェクトがどこで作成されたかを追跡することが難しくなります(主にObject.create式であり、持ち上げられないためです。
もちろん、決定的な議論にはほど遠いです。どちらにも長所と短所があります。モジュールパターン、クロージャ、パワーコンストラクタを使用することをお勧めします。そうでない場合は問題ありません。

これがあなたのために1つか2つをクリアしたことを願っています。

于 2012-12-09T18:42:36.633 に答える
3

最初の例では、すべての Animal インスタンス間で共有されるプロトタイプに move 関数を配置します。

2 番目の例では、動物のインスタンスごとに新しい move 関数を作成します。

3 番目の例では、最初の例と同様のプロトタイプで move 関数を使用して Animal クラスを生成しますが、割り当てられるコードは少なくなります。(あなたの例では、名前はすべてのインスタンス間でも共有されていますが、これはおそらく望ましくありません)

関数をプロトタイプに入れると、アニマルのインスタンス化が速くなり、JIT エンジンの動作方法により、関数の実行も速くなります。

于 2012-12-09T17:15:12.877 に答える