遅かれ早かれ、他の開発者の抽象的な結果、つまり他の人のコードに依存する必要があります。私は無料(依存関係のない)モジュールに頼るのが好きですが、それを達成することは困難です。作成した美しいブラックボックスコンポーネントでさえ、多かれ少なかれ何かに依存します。これがまさに依存関係の注入を大きくするものです。依存関係を効果的に管理する能力は、今では絶対に必要です。この記事では、問題といくつかの解決策の調査をまとめたものです。
1。目標
2つのモジュールがあると想像してください。最初のものはAJAXリクエストサービスの責任があり、2番目のリクエストサービスはルーターです。
コードコピーは次のとおりです。
var service = function(){
return {name: 'service'};
}
var router = function(){
return {name: 'router'};
}
これら2つのモジュールを使用する必要がある別の関数があります。
コードコピーは次のとおりです。
var dosomething = function(other){
var s = service();
var r = router();
};
それをより面白くするために、この関数は引数を受け入れます。もちろん、上記のコードを完全に使用できますが、これは明らかに十分に柔軟ではありません。 servicexmlまたはservicejsonを使用する場合、またはテストモジュールが必要な場合はどうなりますか。関数本文のみを編集しても問題を解決することはできません。まず、関数のパラメーターを介して依存関係を解決できます。今すぐ:
コードコピーは次のとおりです。
var dosomething = function(service、router、other){
var s = service();
var r = router();
};
追加のパラメーターを渡すことで必要な機能を実装しますが、これにより新しい問題が発生します。コードに散らばっている方法が散らばっていると想像してください。依存関係条件を変更する必要がある場合、関数を呼び出すすべてのファイルを変更することは不可能です。
これらのことを成し遂げるのに役立つツールが必要です。これは、依存噴射が解決しようとする問題です。依存噴射ソリューションが達成すべき目標のいくつかを書き留めましょう。
依存関係を登録できるはずです
1。注入は関数を受け入れ、必要な関数を返す必要があります
2。私たちはあまり書くことができません - 私たちは美しい文法を合理化する必要があります
3。注入は、転送された関数の範囲を維持する必要があります
4.渡された関数は、説明だけでなく、カスタムパラメーターを受け入れることができるはずです
5.完璧なリスト、以下で達成しましょう。
3。requirejs/amdメソッド
requirejsを聞いたことがあるかもしれませんが、これは依存関係の注入を解決するための良い選択です。
コードコピーは次のとおりです。
定義(['service'、 'router']、function(service、router){
// ...
});
アイデアは、最初に必要な依存関係を説明し、次に機能を記述することです。ここでのパラメーターの順序は非常に重要です。上記のように、同じ構文を受け入れることができるインジェクターと呼ばれるモジュールを書きましょう。
コードコピーは次のとおりです。
var dosomething = injector.resolve(['service'、 'router']、function(service、router、other){
expect(service()。name).to.be( 'service');
想像(router()。name).to.be( 'router');
(その他).to.be( 'other');
});
DOSOMTHING( "other");
継続する前に、私が書いたコードが私が期待したものと同じであることを確認するために、私はexpect.js(アサーションライブラリ)をはっきりと使用していることを明確に説明する必要があります。方法。
インジェクターモジュールを始めましょう。これは素晴らしいシングルトンパターンであるため、プログラムのさまざまな部分でうまく機能します。
コードコピーは次のとおりです。
var injector = {
依存関係:{}、
登録:function(key、value){
this.dependencies [key] = value;
}、
Resolve:function(deps、func、scope){
}
}
これは非常にシンプルなオブジェクトであり、2つの方法、1つはプロパティを保存するためのものです。私たちがやりたいのは、DEPSアレイを確認し、依存関係変数の回答を検索することです。残っているのは、.applyメソッドを呼び出して、以前のFUNCメソッドのパラメーターを渡すことです。
コードコピーは次のとおりです。
Resolve:function(deps、func、scope){
var args = [];
for(var i = 0; i <deps.length、d = deps [i]; i ++){
if(this.dependencies [d]){
args.push(this.dependencies [d]);
} それ以外 {
新しいエラーをスローします( 'can/' t resolve ' + d);
}
}
return function(){
func.apply(scope || {}、args.concat(array.prototype.slice.call(arguments、0)));
}
}
スコープはオプションです。Array.prototype.slice.call(arguments、0)は、引数変数を実際の配列に変換するために必要です。これまでのところ悪くはありません。私たちのテストは合格しました。この実装の問題は、必要な部品を2回記述する必要があり、注文を混乱させることはできないことです。追加のカスタムパラメーターは、常に依存関係の背後にあります。
4。反射法
ウィキペディアの定義によれば、リフレクションとは、実行時にオブジェクトの構造と動作を確認および変更するプログラムの能力を指します。簡単に言えば、JavaScriptのコンテキストでは、これは、読み取りおよび分析されるオブジェクトまたは関数のソースコードを具体的に指します。記事の冒頭で言及されていることのない関数を完成させましょう。コンソールにdosomething.toString()を出力する場合。次の文字列が表示されます。
コードコピーは次のとおりです。
「機能(サービス、ルーター、その他){
var s = service();
var r = router();
} "
この方法で返される文字列は、パラメーターを通過する能力、さらに重要なことに、名前を取得する能力を提供します。これは、実際に依存関係注入を実装するAngularの方法です。私は少し怠け者で、角度コードのパラメーターを取得する正規表現を直接傍受しました。
コードコピーは次のとおりです。
/^function/s*[^/(]*/(/s*([^//)]*)/)/m
このようなResolveコードを変更できます。
コードコピーは次のとおりです。
Resolve:function(){
var func、deps、scope、args = []、self = this;
func = arguments [0];
deps = func.tostring()。match(/^function/s*[^/(]*/(/s*([^//)]*)/)[1] .replace(//g、 '')。スプリット('、');
scope = arguments [1] ||。
return function(){
var a = array.prototype.slice.call(arguments、0);
for(var i = 0; i <deps.length; i ++){
var d = deps [i];
args.push(self.dependencies [d] && d!= ''?self.dependencies [d]:a.shift());
}
func.apply(scope || {}、args);
}
}
正規表現の実行の結果は次のとおりです。
コードコピーは次のとおりです。
["function(service、router、other)"、 "service、router、その他"]
2番目のアイテムしか必要ないようです。スペースをクリアして文字列を分割したら、DEPSアレイを取得します。大きな変更は1つだけです。
コードコピーは次のとおりです。
var a = array.prototype.slice.call(arguments、0);
...
args.push(self.dependencies [d] && d!= ''?self.dependencies [d]:a.shift());
依存関係の配列をループし、不足しているアイテムが見つかった場合は、引数オブジェクトから取得しようとします。ありがたいことに、アレイが空の場合、シフトメソッドはエラーを投げる代わりに未定義を返すだけです(これはWebのアイデアのおかげです)。インジェクターの新しいバージョンは、次のように使用できます。
コードコピーは次のとおりです。
var dosomething = injector.resolve(function(service、other、router){
expect(service()。name).to.be( 'service');
想像(router()。name).to.be( 'router');
(その他).to.be( 'other');
});
DOSOMTHING( "other");
依存関係を書き直す必要はなく、それらの順序は破壊される可能性があります。それはまだ機能しており、Angular's Magicをコピーしました。
ただし、このプラクティスは完全ではありません。これは、反射型注入に関する非常に大きな問題です。圧縮は、パラメーターの名前を変更し、正しいマッピング関係を維持できないため、ロジックが破壊されます。たとえば、ドソメット()は圧縮後にこのように見える場合があります。
コードコピーは次のとおりです。
var dosomething = function(e、t、n){var r = e(); var i = t()}
Angularチームによって提案されたソリューションは次のように見えます。
var dosomething = injector.resolve(['service'、 'router'、function(service、router){
}]);
これは、私たちが始めた解決策によく似ています。私はより良い解決策を見つけることができなかったので、両方を組み合わせることにしました。これがインジェクターの最終バージョンです。
コードコピーは次のとおりです。
var injector = {
依存関係:{}、
登録:function(key、value){
this.dependencies [key] = value;
}、
Resolve:function(){
var func、deps、scope、args = []、self = this;
if(typeof arguments [0] === 'string'){
func = arguments [1];
deps = arguments [0] .replace( / / g、 '').split( '、');
scope = arguments [2] ||。
} それ以外 {
func = arguments [0];
deps = func.tostring()。match(/^function/s*[^/(]*/(/s*([^//)]*)/)[1] .replace(//g、 '')。スプリット('、');
scope = arguments [1] ||。
}
return function(){
var a = array.prototype.slice.call(arguments、0);
for(var i = 0; i <deps.length; i ++){
var d = deps [i];
args.push(self.dependencies [d] && d!= ''?self.dependencies [d]:a.shift());
}
func.apply(scope || {}、args);
}
}
}
訪問者を解決することは、2つまたは3つのパラメーターを受け入れます。2つのパラメーターがある場合、実際には前の記事で書かれたものと同じです。ただし、3つのパラメーターがある場合、最初のパラメーターを変換してDEPSアレイを埋めます。テストの例は次のとおりです。
コードコピーは次のとおりです。
var dosomething = injector.resolve( 'router ,, service'、function(a、b、c){
(a()。name).to.be( 'router');
期待(b).to.be( 'other');
(c()。name).to.be( 'service');
});
DOSOMTHING( "other");
最初のパラメーターの後に2つのコンマがあることに気付くかもしれません - これはタイプミスではないことに注意してください。 null値は、実際には「他の」パラメーター(プレースホルダー)を表します。これは、パラメーターの順序を制御する方法を示しています。
5。範囲の直接注入
時々、操作関数の範囲(言い換えれば、このオブジェクト)を含む3番目の注入変数を使用することがあります。したがって、多くの場合、この変数は必要ありません。
コードコピーは次のとおりです。
var injector = {
依存関係:{}、
登録:function(key、value){
this.dependencies [key] = value;
}、
Resolve:function(deps、func、scope){
var args = [];
scope = scope || {};
for(var i = 0; i <deps.length、d = deps [i]; i ++){
if(this.dependencies [d]){
scope [d] = this.dependencies [d];
} それ以外 {
新しいエラーをスローします( 'can/' t resolve ' + d);
}
}
return function(){
func.apply(scope || {}、array.prototype.slice.call(arguments、0));
}
}
}
私たちが行うことは、実際にスコープに依存関係を追加することだけです。これの利点は、開発者が依存関係のパラメーターを作成する必要がなくなったことです。
コードコピーは次のとおりです。
var dosomething = injector.resolve(['service'、 'router']、function(other){
想像(this.service()。name).to.be( 'service');
想像(this.router()。name).to.be( 'router');
(その他).to.be( 'other');
});
DOSOMTHING( "other");
6。結論
実際、私たちのほとんどは依存噴射を使用していますが、気づきません。用語がわからなくても、コードで100万回使用した可能性があります。この記事があなたの理解を深めることを願っています。