目次
目標設定
requirejs/AMD方法" >requirejs/AMD方法
反射(reflection)方法
结语
ホームページ ウェブフロントエンド jsチュートリアル JavaScript の依存関係注入サンプル コードの詳細な分析

JavaScript の依存関係注入サンプル コードの詳細な分析

Mar 14, 2017 pm 03:18 PM

コンピュータープログラミングの世界は、実際には、単純な部分を常に抽象化し、それらの抽象概念を整理するプロセスです。 JavaScriptも例外ではありません。JavaScriptを使用してアプリケーションを作成するとき、私たちは皆、有名なオープンソースライブラリやフレームワークなど、他の人によって書かれたコードを使用しているのでしょうか。プロジェクトが成長するにつれて、依存する必要のあるモジュールがますます増えており、現時点では、これらのモジュールを効果的に編成する方法が非常に重要な問題となっています。 依存性注入 は、コードに依存するモジュールを効果的に編成する方法の問題を解決します。有名な フロントエンド フレームワークAngularJS など、一部のフレームワークやライブラリで「依存性注入」という用語を聞いたことがあるかもしれません。依存性注入は非常に重要な機能の 1 つです。ただし、依存関係注入はまったく新しいものではなく、PHP などの他の プログラミング言語 には以前から存在していました。同時に、依存関係の注入は想像されているほど複雑ではありません。この記事では、JavaScript における依存性注入の概念を学び、「依存性注入スタイル」のコードの書き方をわかりやすく説明します。

目標設定

今、2 つのモジュールがあるとしましょう。最初のモジュールは Ajax リクエストの送信に使用され、2 番目のモジュールは ルーター として使用されます。

var service = function() {
    return { name: 'Service' };
}
var router = function() {
    return { name: 'Router' };
}
ログイン後にコピー

この時点で、上記の 2 つのモジュールを使用する必要がある function を作成しました。

var doSomething = function(other) {
    var s = service();
    var r = router();
};
ログイン後にコピー

ここで、コードをより興味深いものにするために、このパラメータはさらにいくつかのパラメータを受け取る必要があります。もちろん、上記のコードを完全に使用することはできますが、上記のコードはどの面から見ても柔軟性が若干劣ります。使用する必要があるモジュールの名前が Service<a href="http://www.php.cn/wiki/1527.html" target="_blank">XML<code>Service<a href="http://www.php.cn/wiki/1527.html" target="_blank">XML</a>或者Service<a href="http://www.php.cn/wiki/1488.html" target="_blank">JSON</a>该怎么办?或者说如果我们基于测试的目的想要去使用一些假的模块改怎么办。这时,我们不能仅仅去编辑函数本身。因此我们需要做的第一件事情就是将依赖的模块作为参数传递给函数,代码如下所示:

var doSomething = function(service, router, other) {
    var s = service();
    var r = router();
};
ログイン後にコピー

在上面的代码中,我们完全传递了我们所需要的模块。但是这又带来了一个新的问题。假设我们在代码的哥哥部分都调用了doSomething方法。这时,如果我们需要第三个依赖项该怎么办。这个时候,去编辑所有的函数调用代码并不是一个明智的方法。因此,我们需要一段代码来帮助我们做这件事情。这就是依赖注入器试图去解决的问题。现在我们可以来定下我们的目标了:

  • 我们应该能够去注册依赖项

  • 依赖注入器应该接收一个函数,然后返回一个能够获取所需资源的函数

  • 代码不应该复杂,而应该简单友好

  • 依赖注入器应该保持传递的函数作用域

  • 传递的函数应该能够接收自定义的参数,而不仅仅是被描述的依赖项

requirejs/AMD方法

或许你已经听说过了大名鼎鼎的requirejs,它是一个能够很好的解决依赖注入问题的库:

define([&#39;service&#39;, &#39;router&#39;], function(service, router) {       
    // ...
});
ログイン後にコピー

requirejs的思想是首先我们应该去描述所需要的模块,然后编写你自己的函数。其中,参数的顺序很重要。假设我们需要编写一个叫做injector的模块,它能够实现类似的语法。

var doSomething = injector.resolve([&#39;service&#39;, &#39;router&#39;], function(service, router, other) {
    expect(service().name).to.be(&#39;Service&#39;);
    expect(router().name).to.be(&#39;Router&#39;);
    expect(other).to.be(&#39;Other&#39;);
});
doSomething("Other");
ログイン後にコピー

在继续往下之前,需要说明的一点是在doSomething的函数体中我们使用了expect.js这个断言库来确保代码的正确性。这里有一点类似TDD(测试驱动开发)的思想。

现在我们正式开始编写我们的injector または < になった場合code> サービスJSON

どうすればよいですか?あるいは、テスト目的で偽のモジュールを使用したい場合はどうなるでしょうか。この時点では、関数自体を編集するだけでは済みません。したがって、最初に行う必要があるのは、依存モジュールをパラメーターとして関数に渡すことです。コードは次のとおりです: 🎜
var injector = {
    dependencies: {},
    register: function(key, value) {
        this.dependencies[key] = value;
    },
    resolve: function(deps, func, scope) {

    }
}
ログイン後にコピー
ログイン後にコピー
🎜 上記のコードでは、必要なモジュールを正確に渡します。しかし、これは新たな問題を引き起こします。コードのすべての部分で doSomething メソッドを呼び出すとします。この時点で、3 番目の依存関係が必要な場合はどうなるでしょうか。現時点では、すべての関数呼び出しコードを編集するのは賢明な考えではありません。したがって、これを行うにはコードが必要です。これは、依存性インジェクターが解決しようとしている問題です。これで目標を設定できます: 🎜
🎜🎜js/AMD メソッドが必要です🎜 🎜おそらく依存性注入の問題をうまく解決できるライブラリである、有名な requirejs について聞いたことがあるでしょう: 🎜
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]);
        } else {
            throw new Error(&#39;Can\&#39;t resolve &#39; + d);
        }
    }
    return function() {
        func.apply(scope || {}, args.concat(Array.prototype.slice.call(arguments, 0)));
    }        
}
ログイン後にコピー
ログイン後にコピー
🎜 requirejs の考え方は、最初に必要なモジュールを記述し、それから独自の関数を記述する必要があるということです。中でもパラメータの順序は重要です。同様の構文を実装する injector というモジュールを作成する必要があるとします。 🎜
"function (service, router, other) {
    var s = service();
    var r = router();
}"
ログイン後にコピー
ログイン後にコピー
🎜次に進む前に、説明する必要があるのは、doSomething の関数本体では、コードの正確性を保証するために Expect.js アサーション ライブラリを使用しているということです。 TDD (テスト
駆動🎜開発)の考え方に似たものがここにあります。 🎜🎜ここで、injector モジュールの作成を正式に開始します。まず、アプリケーションのすべての部分で同じ機能を持たせるために、モノリスである必要があります。 🎜
var injector = {
    dependencies: {},
    register: function(key, value) {
        this.dependencies[key] = value;
    },
    resolve: function(deps, func, scope) {

    }
}
ログイン後にコピー
ログイン後にコピー

这个对象非常的简单,其中只包含两个函数以及一个用于存储目的的变量。我们需要做的事情是检查deps数组,然后在dependencies变量种寻找答案。剩余的部分,则是使用.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]);
        } else {
            throw new Error(&#39;Can\&#39;t resolve &#39; + d);
        }
    }
    return function() {
        func.apply(scope || {}, args.concat(Array.prototype.slice.call(arguments, 0)));
    }        
}
ログイン後にコピー
ログイン後にコピー

如果你需要指定一个作用域,上面的代码也能够正常的运行。

在上面的代码中,Array.prototype.slice.call(arguments, 0)的作用是将arguments变量转换为一个真正的数组。到目前为止,我们的代码可以完美的通过测试。但是这里的问题是我们必须要将需要的模块写两次,而且不能够随意排列顺序。额外的参数总是排在所有的依赖项之后。

反射(reflection)方法

根据维基百科中的解释,反射(reflection)指的是程序可以在运行过程中,一个对象可以修改自己的结构和行为。在JavaScript中,简单来说就是阅读一个对象的源码并且分析源码的能力。还是回到我们的doSomething方法,如果你调用doSomething.to<a href="http://www.php.cn/wiki/57.html" target="_blank">String</a>()方法,你可以获得下面的字符串:

"function (service, router, other) {
    var s = service();
    var r = router();
}"
ログイン後にコピー
ログイン後にコピー

这样一来,只要使用这个方法,我们就可以轻松的获取到我们想要的参数,以及更重要的一点就是他们的名字。这也是AngularJS实现依赖注入所使用的方法。在AngularJS的代码中,我们可以看到下面的正则表达式

/^function\s*[^\(]*\(\s*([^\)]*)\)/m
ログイン後にコピー

我们可以将resolve方法修改成如下所示的代码:

resolve: function() {
    var func, deps, scope, args = [], self = this;
    func = arguments[0];
    deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, &#39;&#39;).split(&#39;,&#39;);
    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 != &#39;&#39; ? self.dependencies[d] : a.shift());
        }
        func.apply(scope || {}, args);
    }        
}
ログイン後にコピー

我们使用上面的正则表达式去匹配我们定义的函数,我们可以获取到下面的结果:

["function (service, router, other)", "service, router, other"]
ログイン後にコピー

此时,我们只需要第二项。但是一旦我们去除了多余的空格并以,来切分字符串以后,我们就得到了deps数组。下面的代码就是我们进行修改的部分:

var a = Array.prototype.slice.call(arguments, 0);
...
args.push(self.dependencies[d] && d != &#39;&#39; ? self.dependencies[d] : a.shift());
ログイン後にコピー

在上面的代码中,我们遍历了依赖项目,如果其中有缺失的项目,如果依赖项目中有缺失的部分,我们就从arguments对象中获取。如果一个数组是空数组,那么使用shift方法将只会返回undefined,而不会抛出一个错误。到目前为止,新版本的injector看起来如下所示:

var doSomething = injector.resolve(function(service, other, router) {
    expect(service().name).to.be(&#39;Service&#39;);
    expect(router().name).to.be(&#39;Router&#39;);
    expect(other).to.be(&#39;Other&#39;);
});
doSomething("Other");
ログイン後にコピー

在上面的代码中,我们可以随意混淆依赖项的顺序。

但是,没有什么是完美的。反射方法的依赖注入存在一个非常严重的问题。当代码简化时,会发生错误。这是因为在代码简化的过程中,参数的名称发生了变化,这将导致依赖项无法解析。例如:

var doSomething=function(e,t,n){var r=e();var i=t()}
ログイン後にコピー

因此我们需要下面的解决方案,就像AngularJS中那样:

var doSomething = injector.resolve([&#39;service&#39;, &#39;router&#39;, function(service, router) {

}]);
ログイン後にコピー

这和最一开始看到的AMD的解决方案很类似,于是我们可以将上面两种方法整合起来,最终代码如下所示:

var injector = {
    dependencies: {},
    register: function(key, value) {
        this.dependencies[key] = value;
    },
    resolve: function() {
        var func, deps, scope, args = [], self = this;
        if(typeof arguments[0] === &#39;string&#39;) {
            func = arguments[1];
            deps = arguments[0].replace(/ /g, &#39;&#39;).split(&#39;,&#39;);
            scope = arguments[2] || {};
        } else {
            func = arguments[0];
            deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, &#39;&#39;).split(&#39;,&#39;);
            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 != &#39;&#39; ? self.dependencies[d] : a.shift());
            }
            func.apply(scope || {}, args);
        }        
    }
}
ログイン後にコピー

这一个版本的resolve方法可以接受两个或者三个参数。下面是一段测试代码:

var doSomething = injector.resolve(&#39;router,,service&#39;, function(a, b, c) {
    expect(a().name).to.be(&#39;Router&#39;);
    expect(b).to.be(&#39;Other&#39;);
    expect(c().name).to.be(&#39;Service&#39;);
});
doSomething("Other");
ログイン後にコピー

你可能注意到了两个逗号之间什么都没有,这并不是错误。这个空缺是留给Other这个参数的。这就是我们控制参数顺序的方法。

结语

在上面的内容中,我们介绍了几种JavaScript中依赖注入的方法,希望本文能够帮助你开始使用依赖注入这个技巧,并且写出依赖注入风格的代码。

以上がJavaScript の依存関係注入サンプル コードの詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 Dec 17, 2023 pm 02:54 PM

WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 はじめに: 技術の継続的な発展により、音声認識技術は人工知能の分野の重要な部分になりました。 WebSocket と JavaScript をベースとしたオンライン音声認識システムは、低遅延、リアルタイム、クロスプラットフォームという特徴があり、広く使用されるソリューションとなっています。この記事では、WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法を紹介します。

WebSocket と JavaScript: リアルタイム監視システムを実装するための主要テクノロジー WebSocket と JavaScript: リアルタイム監視システムを実装するための主要テクノロジー Dec 17, 2023 pm 05:30 PM

WebSocketとJavaScript:リアルタイム監視システムを実現するためのキーテクノロジー はじめに: インターネット技術の急速な発展に伴い、リアルタイム監視システムは様々な分野で広く利用されています。リアルタイム監視を実現するための重要なテクノロジーの 1 つは、WebSocket と JavaScript の組み合わせです。この記事では、リアルタイム監視システムにおける WebSocket と JavaScript のアプリケーションを紹介し、コード例を示し、その実装原理を詳しく説明します。 1.WebSocketテクノロジー

JavaScript と WebSocket を使用してリアルタイムのオンライン注文システムを実装する方法 JavaScript と WebSocket を使用してリアルタイムのオンライン注文システムを実装する方法 Dec 17, 2023 pm 12:09 PM

JavaScript と WebSocket を使用してリアルタイム オンライン注文システムを実装する方法の紹介: インターネットの普及とテクノロジーの進歩に伴い、ますます多くのレストランがオンライン注文サービスを提供し始めています。リアルタイムのオンライン注文システムを実装するには、JavaScript と WebSocket テクノロジを使用できます。 WebSocket は、TCP プロトコルをベースとした全二重通信プロトコルで、クライアントとサーバー間のリアルタイム双方向通信を実現します。リアルタイムオンラインオーダーシステムにおいて、ユーザーが料理を選択して注文するとき

WebSocketとJavaScriptを使ったオンライン予約システムの実装方法 WebSocketとJavaScriptを使ったオンライン予約システムの実装方法 Dec 17, 2023 am 09:39 AM

WebSocket と JavaScript を使用してオンライン予約システムを実装する方法 今日のデジタル時代では、ますます多くの企業やサービスがオンライン予約機能を提供する必要があります。効率的かつリアルタイムのオンライン予約システムを実装することが重要です。この記事では、WebSocket と JavaScript を使用してオンライン予約システムを実装する方法と、具体的なコード例を紹介します。 1. WebSocket とは何ですか? WebSocket は、単一の TCP 接続における全二重方式です。

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 Dec 17, 2023 pm 05:13 PM

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 はじめに: 今日、天気予報の精度は日常生活と意思決定にとって非常に重要です。テクノロジーの発展に伴い、リアルタイムで気象データを取得することで、より正確で信頼性の高い天気予報を提供できるようになりました。この記事では、JavaScript と WebSocket テクノロジを使用して効率的なリアルタイム天気予報システムを構築する方法を学びます。この記事では、具体的なコード例を通じて実装プロセスを説明します。私たちは

簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 Jan 05, 2024 pm 06:08 PM

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

JavaScript と WebSocket: 効率的なリアルタイム画像処理システムの構築 JavaScript と WebSocket: 効率的なリアルタイム画像処理システムの構築 Dec 17, 2023 am 08:41 AM

JavaScript は Web 開発で広く使用されているプログラミング言語であり、WebSocket はリアルタイム通信に使用されるネットワーク プロトコルです。 2 つの強力な機能を組み合わせることで、効率的なリアルタイム画像処理システムを構築できます。この記事では、JavaScript と WebSocket を使用してこのシステムを実装する方法と、具体的なコード例を紹介します。まず、リアルタイム画像処理システムの要件と目標を明確にする必要があります。リアルタイムの画像データを収集できるカメラ デバイスがあるとします。

Golang 関数パラメータ渡しにおける依存性注入パターン Golang 関数パラメータ渡しにおける依存性注入パターン Apr 14, 2024 am 10:15 AM

Go では、依存関係注入 (DI) モードは、値の受け渡しやポインターの受け渡しなど、関数パラメーターの受け渡しを通じて実装されます。 DI パターンでは、依存関係は通常、分離を改善し、ロック競合を軽減し、テスト容易性をサポートするためにポインターとして渡されます。ポインターを使用すると、関数はインターフェイスの種類にのみ依存するため、具体的な実装から切り離されます。また、ポインターの受け渡しにより、大きなオブジェクトを渡す際のオーバーヘッドが削減されるため、ロックの競合が軽減されます。さらに、DI パターンでは依存関係を簡単にモックできるため、DI パターンを使用した関数の単体テストを簡単に作成できます。

See all articles