ホームページ > ウェブフロントエンド > jsチュートリアル > Javascript_javascript スキルにおける依存関係の挿入についての深い理解

Javascript_javascript スキルにおける依存関係の挿入についての深い理解

WBOY
リリース: 2016-05-16 16:55:02
オリジナル
1037 人が閲覧しました


遅かれ早かれ、他の開発者の抽象化を使用する必要があります。つまり、他の人のコードに依存することになります。依存関係のない (依存関係のない) モジュールが望ましいですが、それを実現するのは困難です。あなたが作成する素晴らしいブラック ボックス コンポーネントであっても、多かれ少なかれ何かに依存しています。ここで依存性注入が登場します。依存関係を効果的に管理する機能は、今や絶対的に必要なものになっています。この記事では、この問題についての私の調査とその解決策のいくつかを要約します。

1. 目標
2 つのモジュールがあると想像してください。 1 つ目は 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();
};

見た目をさらに面白くするために、この関数は 1 つのパラメーターを受け入れます。もちろん、上記のコードを完全に使用することはできますが、明らかに十分な柔軟性がありません。 ServiceXML または ServiceJSON を使用したい場合、またはテスト モジュールが必要な場合はどうすればよいでしょうか。関数本体を編集するだけでは問題を解決できません。まず、関数のパラメーターを通じて依存関係を解決できます。つまり、
コードをコピー コードは次のとおりです。

var doSomething = function(service) , router, other ) {
var s = service();
var r = router();
};

追加のパラメータを渡すことで目的を達成しますが、これは新たな問題をもたらすことになる。 doSomething メソッドがコード全体に散在している場合を想像してください。依存関係を変更する必要がある場合、関数を呼び出すすべてのファイルを変更することはできません。

これを行うのに役立つツールが必要です。これは依存関係注入が解決しようとしている問題です。依存関係注入ソリューションが達成すべき目標をいくつか書き留めてみましょう:

依存関係を登録できるはずです
1. インジェクションは関数を受け取り、必要な関数を返す必要があります
2. あまり多くを書くことはできません - 合理化された美しい構文が必要です
3 .インジェクションは渡された関数のスコープを維持する必要があります
4. 渡された関数は依存関係の説明だけでなくカスタムパラメータを受け入れることができる必要があります
5. これは完璧なチェックリストです。
3. RequireJS/AMD メソッド
RequireJS について聞いたことがあるかもしれませんが、これは依存関係の注入を解決するための良い選択です。

コードをコピー コードは次のとおりです。

define(['service', 'router ']、関数(サービス、ルーター) {ここでのパラメータの順序は重要です。上で述べたように、同じ構文を受け入れる injector というモジュールを作成しましょう。



コードをコピー
コードは次のとおりです。var doSomething = injector.resolve([' service', 'router'], function(service, router, other) { Expect(service().name).to.be('Service');
Expect(router().name)。 to.be ('Router');
Expect(other).to.be('Other');
});

doSomething("Other");
すべきだdoSomething 関数本体の内容を明確に説明するために、少し TDD (テスト駆動開発) メソッドを反映して、作成したコードが期待どおりに動作することを確認するために、expect.js (アサーション ライブラリ) を使用します。
インジェクター モジュールから始めましょう。これは優れたシングルトン パターンなので、プログラムのさまざまな部分でうまく機能します。




コードをコピー
コードは次のとおりです:

var injector = {
依存関係: {},
register: function(key, value) {
this.dependency[key] = value;
},
解決: function(deps, func,scope) {

}
}


これは、プロパティを保存するための 2 つのメソッドを備えた非常に単純なオブジェクトです。私たちが行うことは、deps 配列をチェックし、依存関係変数で答えを検索することです。残っているのは、.apply メソッドを呼び出して、前の func メソッドから引数を渡すことだけです。
コードをコピー コードは次のとおりです。

resolve: function(deps, func,scope ) {
var args = [];
for(var i=0; i if(this.dependency[d]) {
args.push (this.Dependency [d]);
} else {
新しいエラーをスロー ('解決できません' d);
func.apply(scope || {}, args.concat(Array.prototype.slice.call(arguments, 0)));
}
}

スコープはオプションです はい、Array.prototype.slice.call(arguments, 0)引数変数を実数配列に変換するには必要です。ここまでは順調ですね。私たちのテストは合格しました。この実装の問題は、必要な部分を 2 回記述する必要があり、その順序を間違えることができないことです。追加のカスタム パラメーターは常に依存関係の後に来ます。
4. リフレクション方法
Wikipedia の定義によれば、リフレクションとは、実行時にオブジェクトの構造と動作を検査および変更するプログラムの機能を指します。簡単に言えば、JavaScript のコンテキストでは、これは特にオブジェクトまたは関数のソース コードを読み取って分析することを指します。記事の冒頭で述べた doSomething 関数を完成させましょう。 doSomething.tostring() をコンソールに出力した場合。次の文字列を取得します:



コードをコピーします コードは次のとおりです:" function (service , router, other) {
var s = service();
var r = router();
}"

このメソッドによって返される文字列は、次の機能を提供します。パラメータを走査すること、そしてさらに重要なことに、パラメータの名前を取得できることです。これは実際に、Angular が依存関係の注入を実装する方法です。私は少し怠け者だったので、Angular コードのパラメータを取得するための正規表現を直接インターセプトしました。


コードをコピー コードは次のとおりです:/^functions*[^(]* (s* ([^)]*))/m
解決コードは次のように変更できます:


コードをコピー コードは次のとおりです: resolve: function() {
var func, deps,scope, args = [], self = this;
func = argument [0];
deps = func.toString().match(/^functions*[^(]*(s*([^)]*))/m)[1].replace(/ /g, '').split (',');
スコープ = argument[1] {};
return function() {
var a = Array.prototype.slice.call(arguments, 0) );
for(var i=0; i var d = deps[i];
args.push(self.dependency[d] && d != ' ' ? self.dependency [d]: a.shift());
}
func.apply(scope || {}, args);
}
}

正規表現を実行します。 other)", "service, router, other"]
の結果
必要なのは 2 番目の項目だけのようです。スペースをクリアして文字列を分割すると、deps 配列が得られます。大きな変更点は 1 つだけです:
コードをコピーします コードは次のとおりです:

var a = Array.prototype .slice.call(arguments, 0);
...
args.push(self.dependency[d] && d != '' ? self.dependency[d] : a. SHIFT());

依存関係配列をループし、欠落している項目が見つかった場合は引数オブジェクトから取得しようとします。ありがたいことに、配列が空の場合、shift メソッドはエラーをスローするのではなく、単に未定義を返します (Web のアイデアのおかげです)。新しいバージョンのインジェクターは次のように使用できます:
コードをコピーします コードは次のとおりです:

var doSomething = injector.resolve(function(service, other, router) {
Expect(service().name).to.be('Service');
Expect(router().name ).to.be( 'ルーター');
Expect(other).to.be('その他');
});
doSomething("その他");

依存関係を書き直す必要はなく、順序が崩れる可能性があります。それは今でも機能しており、Angular の魔法を再現することに成功しました。

しかし、このアプローチは完璧ではなく、反射型注射では非常に大きな問題となります。圧縮するとパラメーター名が変更され、正しいマッピングを維持できなくなるため、ロジックが壊れます。たとえば、 doSometing() は圧縮すると次のようになります:

コードをコピーします コードは次のとおりです:

var doSomething=function(e,t,n){var r=e();var i=t()}
Angular チームが提案した解決策は次のようになります:

var doSomething = injector.resolve(['service', 'router', function(service, router) {

}]);


これは、最初のソリューションとよく似ています。より良い解決策が見つからなかったので、2 つのアプローチを組み合わせることにしました。以下はインジェクターの最終バージョンです。
コードをコピー コードは次のとおりです:

var injector = {
依存関係: {},
register: function(key, value) {
this.dependency[key] = value;
},
solve: function() {
var func, deps,scope , args = [], self = this;
if(引数の種類[0] === 'string') {
func = argument[1];
deps = argument[0].replace( / /g , '').split(',');
スコープ = 引数[2] || {};
} else {
func = argument[0];
deps = func.toString ().match(/^functions*[^(]*(s*([^)]*))/m)[1].replace(/ /g, '').split(',' );
スコープ = 引数[1] i=0; i
var d = deps[ i]; : a.shift());

解決ビジターは 2 つまたは 3 つのパラメーターを受け入れます。パラメーターが 2 つある場合、実際にはこの記事で前述したものと同じです。ただし、パラメータが 3 つある場合は、最初のパラメータが変換され、deps 配列が埋められます。テスト例は次のとおりです:
コードをコピー コードは次のとおりです:

var doSomething = injector.resolve('router,,service', function(a, b, c) {
Expect(a().name) ).to .be('Router');
Expect(b).to.be('Other');
Expect(c().name).to.be('Service');
} );
doSomething("Other");

最初のパラメータの後にカンマが 2 つあることに気づくかもしれませんが、これはタイプミスではないことに注意してください。 null 値は実際には「Other」パラメータ (プレースホルダ) を表します。これは、パラメーターの順序を制御する方法を示しています。

5. スコープを直接注入する
操作関数 (つまり this オブジェクト) のスコープに関係する 3 番目の注入変数を使用することがあります。したがって、多くの場合、この変数を使用する必要はありません。

コードをコピー コードは次のとおりです:

var injector = {
依存関係: {},
register: function(key, value) {
this.dependency[key] = value;
},
solve: function(deps, func,scope) {
var args = [ ];
スコープ = スコープ {};
for(var i=0; i if(this.dependency [d] ) {
scope[d] = this.dependency[d]; }
return function() {
func.apply(scope || {}, Array.proto type.slice.call(引数、0)); スコープに依存関係を追加するだけです。この利点は、開発者が依存関係パラメーターを記述する必要がなくなり、依存関係パラメーターがすでに関数スコープの一部になっているということです。




コードをコピー

コードは次のとおりです。

var doSomething = injector.resolve([' service', 'router'], function(other) { Expect(this.service().name).to.be('Service'); Expect(this.router().name)。 to.be ('ルーター'); Expect(other).to.be('その他');});doSomething("その他");
6.結論
実際、私たちのほとんどは依存性注入を使用したことがありますが、それに気づいていませんでした。この用語を知らなくても、おそらくコード内で何百万回も使用したことがあるでしょう。この記事で理解を深めていただければ幸いです。
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート