anglejsでの$ injection.instantiate実装の例を使用してオブジェクトを作成するためのユニバーサル関数

使用しているangleJSタイプをインスタンス化する方法について疑問に思ったことはありますか? コントローラー、ファクトリー、サービス、デコレーター、値-文字通り、それぞれが最終的に実行のために$injector



インスタンス化関数に渡され、そこでかなり興味深い構造が待っています。これについては今日お話ししたいと思います。









つまり、次の行について説明します。



 return new (Function.prototype.bind.apply(ctor, args))();
      
      





その動作の原理はすぐに明らかになりますか? 答えがはいの場合、注意と時間をありがとう:)



さて、javascriptで犬を食べたすべての読者が私たちを去ったとき、私は自分の質問に答えたいと思います:この行を最初に見たとき、私は混乱し、これらすべての関係とbind



apply



new



()



関数の複雑さについて何も理解しませんでした。 正しくしましょう。 つまり、反対から始めることをお勧めします。パラメータ化されたコンストラクタを作成して、インスタンスを作成します。



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





新しい



「もっと簡単にできること」-あなたは言う、あなたは正しい: var dog = new Animal('Dog', 'Woof!');



new



演算子は、 Animal



コンストラクターの呼び出しのインスタンスを取得するために最初に必要なものです。 新しい機能の仕組みに関する小さな余談:



新しいFoo(...)が実行されると、次のことが起こります。



1. Foo.prototypeを継承する新しいオブジェクトが作成されます。

2.コンストラクターが呼び出されます-指定された引数とこれを持つFoo関数は、新しく作成されたオブジェクトにバインドされます。 new Fooはnew Foo()と同等です。つまり、引数が指定されていない場合、Fooは引数なしで呼び出されます。

3.新しい式の結果は、コンストラクターによって返されるオブジェクトです。 コンストラクターがオブジェクトを明示的に返さない場合は、手順1のオブジェクトが使用されます(通常、コンストラクターは値を返しませんが、オブジェクトを作成する通常のプロセスをオーバーライドする必要がある場合は値を返すことができます)。

詳細


それでは、 Animal



コンストラクターの呼び出しを関数でラップして、初期化コードがすべての必要な呼び出しに共通するようにします。



 function CreateAnimal(name, sound) { return new Animal(name, sound); }
      
      





時間が経つにつれて、動物だけでなく人も作成したいと思うようになりました(この例は最も成功していないことに同意します)。つまり、少なくとも2つの選択肢があります。



  1. 必要なタイプに応じて、必要なインスタンスを作成するファクトリーを実装します。
  2. コンストラクター関数をパラメーターとして渡し、それに基づいて、既にバインドされている引数が渡された新しい関数を作成します( bind



    関数を使用すると、 bind



    ことができます)。


また、 $injector.instantiate



場合、2番目のパスが選択されました。



縛る



 function Create(ctorFunc, name, sound) { return new (ctorFunc.bind(null, name, sound)); } console.log( Create(Animal, 'Dog', 'Woof') ); console.log( Create(Human, 'Person') );
      
      





bind



機能に関する小さな余談:



bind()メソッドは、呼び出されると、指定された値を実行コンテキストとして設定する新しい関数を作成します。 このメソッドは、バインドされた関数が呼び出されたときに渡される引数の前に設定される一連の引数も渡します。

詳細


この場合、コンテキストとしてnull



を渡しnull



bind



をnew演算子で使用して作成された新しい関数を使用する予定です。これはthis



を無視し、空のオブジェクトを作成します。 バインド関数を実行した結果は、すでにバインドされた引数を持つ新しい関数になります(つまりreturn new fn;



fn



bind



の呼び出し結果です)。



これで、関数を使用して、コンストラクターがname



sound



パラメーターを取得する動物や人物を作成できるようになりました。 「しかし、動物に必要なすべての議論が人間に必要なわけではない」とあなたは言います、そしてあなたは正しいでしょう、2つの問題が醸成されています:



  1. コンストラクターの引数(順序や番号など)を変更し始めることができるため、コンストラクターの署名、 Create



    関数呼び出しの行、およびインスタンス作成行return new (ctorFunc.bind(null, name, sound ))



    を一度に変更する必要がありCreate



    ;
  2. コンストラクターが多いほど、それらを作成するために異なる引数が必要になる可能性が高くなり、単一の関数を使用できなくなります(または、それらすべてをリストし、必要なもののみを記入する必要があります)。


適用する



これらの問題の解決策は、作成関数からコンストラクター、つまりコンストラクターと必要な引数の配列を受け取り、これらの引数がバインドされている新しい関数を返すユニバーサル関数に引数を直接渡すことです。 これにはjavascriptにすばらしいapply



関数がありapply



(引数の数が事前にわかっている場合は、そのアナログcall



)。



applyの動作に関する小さな余談:



apply()メソッドは、指定されたthis値と引数を配列(または配列のようなオブジェクト)として提供して関数を呼び出します。



この関数の構文はcall()関数とほとんど同じですが、2つの基本的な違いは、call()関数が引数のリスト(リスト)を受け取り、apply()関数が引数の配列を(単一のパラメーターとして)受け取ることです。

詳細


ここで、おそらく最も難しい部分が始まります。なぜなら、 applyを使用してbind



関数のコンストラクターコンテキストを設定する必要があり( ctorFunc.bind



類似)、 bind



関数の引数として(最初の引数が設定されるコンテキストであることを忘れないで)、 ctorArgs.unshift(null)



を使用して1つ右にシフトしたコンストラクターパラメーター配列をctorArgs.unshift(null)











bind



機能は、 Create



実行のコンテキストでは使用できません。 これはwindow



オブジェクトですが、 Function.prototype



関数のプロトタイプを介してアクセスできます。



最終結果は、次の汎用関数になります。



 function Create(ctorFunc, ctorArgs) { ctorArgs.unshift(null); return new (Function.prototype.bind.apply(ctorFunc, ctorArgs )); } console.log( Create(Animal, ['Dog', 'Woof']) ); console.log( Create(Human, ['Person', 'John', 'Engineer', 'Moscow']) );
      
      





angularJSに戻ると、たとえば、 Animal



Human



は工場や他のタイプのデザイナーであり、依存関係の名前によって引数の配列['Dog', 'Woof']



が見つかる(解決される)ことがわかります。



 angular .module('app') .factory(function($scope) { // constructor });
      
      





または



 angular .module('app') .factory(['$scope', function($scope) { // constructor }]);
      
      





完全な$injector.instantiate



メソッドを実装するために行うべきことは、コンストラクター関数を見つけて必要な引数を取得することだけです。



All Articles