O mundo da programação de computadores é na verdade um processo de abstração constantemente de peças simples e organizar essas abstrações. JavaScript não é exceção. Quando usamos o JavaScript para escrever aplicativos, usamos o código escrito por outras pessoas, como algumas famosas bibliotecas ou estruturas de código aberto. À medida que nossos projetos crescem, mais e mais módulos em que precisamos confiar. No momento, como organizar efetivamente esses módulos se tornou uma questão muito importante. A injeção de dependência resolve o problema de como organizar efetivamente os módulos de dependência do código. Você deve ter ouvido falar do termo "injeção de dependência" em algumas estruturas ou bibliotecas, como a famosa estrutura de front-end AngularJS. A injeção de dependência é uma das características muito importantes. No entanto, a injeção de dependência não é novidade, ela existe há muito tempo em outras linguagens de programação, como o PHP. Ao mesmo tempo, a injeção de dependência não é tão complicada quanto imaginada. Neste artigo, aprenderemos o conceito de injeção de dependência em JavaScript e explicaremos de uma maneira fácil de entender como escrever o código de "estilo de injeção de dependência".
Configuração de destino
Suponha que temos dois módulos agora. A função do primeiro módulo é enviar solicitações de AJAX, enquanto a função do segundo módulo é usar como roteamento.
A cópia do código é a seguinte:
var serviço = function () {
return {name: 'Service'};
}
var roteter = function () {
return {nome: 'roteador'};
}
No momento, escrevemos uma função que precisa usar os dois módulos mencionados acima:
A cópia do código é a seguinte:
var doSomething = function (outros) {
var s = service ();
var r = roteador ();
};
Aqui, para tornar nosso código mais interessante, esse parâmetro precisa receber vários outros parâmetros. Obviamente, podemos usar o código acima, mas não importa de qualquer aspecto, o código acima parece um pouco menos flexível. O que devemos fazer se o nome do módulo que precisamos usar se tornar Servicexml ou ServiceJson? Ou e se quisermos usar alguns módulos falsos para fins de teste? Neste momento, não podemos simplesmente editar a própria função. Portanto, a primeira coisa que precisamos fazer é passar no módulo dependente como parâmetros para a função, o código é o seguinte:
A cópia do código é a seguinte:
var dosomething = function (serviço, roteador, outro) {
var s = service ();
var r = roteador ();
};
No código acima, passamos os módulos de que precisamos completamente. Mas isso traz à tona um novo problema. Suponha que chamamos o método dosomething na parte do irmão do código. Neste momento, o que devemos fazer se precisarmos de uma terceira dependência. No momento, não é uma maneira sábia de editar todo o código de chamada de função. Então, precisamos de um pedaço de código para nos ajudar a fazer isso. Esse é o problema que o injetor de dependência está tentando resolver. Agora podemos definir nossas metas:
1. Deveríamos ser capazes de registrar dependências
2. O injetor de dependência deve receber uma função e depois retornar uma função que pode obter os recursos necessários
3. O código não deve ser complicado, mas deve ser simples e amigável
4. O injetor de dependência deve manter o escopo da função passada
5. A função passada deve ser capaz de receber parâmetros personalizados, não apenas as dependências descritas
requestJS/Método AMD
Talvez você já tenha ouvido falar dos famosos requisitos, que é uma biblioteca que pode resolver bem o problema de injeção de dependência:
A cópia do código é a seguinte:
define (['serviço', 'roteador'], função (serviço, roteador) {
// ...
});
A idéia de requerjs é que, primeiro, devemos descrever os módulos necessários e depois escrever suas próprias funções. Entre eles, a ordem dos parâmetros é muito importante. Suponha que precisamos escrever um módulo chamado injetor que possa implementar sintaxe semelhante.
A cópia do código é a seguinte:
var doSomething = injetor.Resolve (['serviço', 'roteador'], função (serviço, roteador, outro) {
espera (serviço (). nome) .to.be ('serviço');
Espere (Router (). Nome) .to.be ('roteador');
Espere (outros) .to.be ('Outro');
});
doSomething ("outro");
Antes de continuar, uma coisa a observar é que, no corpo de função do dosomething, usamos a biblioteca de asserção espera.js para garantir a correção do código. Aqui é um pouco semelhante ao TDD (desenvolvimento orientado a testes).
Agora começamos oficialmente a escrever nosso módulo injetor. Primeiro, deve ser um monômero para que possa ter a mesma funcionalidade em todas as partes do nosso aplicativo.
A cópia do código é a seguinte:
var injector = {
Dependências: {},
registro: function (chave, valor) {
this.dependências [key] = value;
},
Resolva: function (deps, func, escopo) {
}
}
Este objeto é muito simples, com apenas duas funções e uma variável para fins de armazenamento. O que precisamos fazer é verificar a matriz de depósito e, em seguida, procurar a resposta nas variáveis de dependências. O resto é usar o método .Apply para chamar a variável FUNC que passamos:
A cópia do código é a seguinte:
Resolva: function (deps, func, escopo) {
var args = [];
for (var i = 0; i <deps.Length, d = deps [i]; i ++) {
if (this.dependências [d]) {
args.push (this.dependências [d]);
} outro {
lançar um novo erro ('pode/' t resolver ' + d);
}
}
Return function () {
func.apply (escopo || {}, args.concat (array.prototype.slice.call (argumentos, 0)));
}
}
Se você precisar especificar um escopo, o código acima também será executado normalmente.
No código acima, a função de Array.prototype.slice.call (argumentos, 0) é converter a variável de argumentos em uma matriz real. Até agora, nosso código passou no teste perfeitamente. Mas o problema aqui é que precisamos escrever os módulos necessários duas vezes e não podemos organizá -los arbitrariamente. Os parâmetros extras são sempre seguidos por todas as dependências.
Método de reflexão
De acordo com a explicação na Wikipedia, a reflexão refere -se ao fato de que um objeto pode modificar sua própria estrutura e comportamento durante a corrida. No JavaScript, basta que seja a capacidade de ler o código -fonte de um objeto e analisar o código -fonte. Ou volte para o nosso método do Dosomething, se você ligar para o método Dosomething.ToString (), poderá obter a seguinte sequência:
A cópia do código é a seguinte:
"função (serviço, roteador, outro) {
var s = service ();
var r = roteador ();
} "
Dessa forma, enquanto usamos esse método, podemos facilmente obter os parâmetros que queremos e, mais importante, seus nomes. Este também é o método usado pelo AngularJS para implementar a injeção de dependência. No código AngularJS, podemos ver a seguinte expressão regular:
A cópia do código é a seguinte:
/^function/s*[^/(]*/(/s*([^/)]*)/)/m
Podemos modificar o método Resolve para o seguinte código:
A cópia do código é a seguinte:
Resolva: function () {
var func, deps, escopo, args = [], self = this;
func = argumentos [0];
deps = func.toString (). Match (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .Rplace (//g, '') .split (',');
escopo = argumentos [1] || {};
Return function () {
var a = array.prototype.slice.call (argumentos, 0);
for (var i = 0; i <deps.Length; i ++) {
var d = deps [i];
args.push (self.dependências [d] && d! = ''? self.dependências [d]: a.shift ());
}
func.apply (escopo || {}, args);
}
}
Usamos a expressão regular acima para corresponder à função que definimos e podemos obter o seguinte resultado:
A cópia do código é a seguinte:
["função (serviço, roteador, outros)", "serviço, roteador, outros"]
Neste ponto, precisamos apenas do segundo item. Mas uma vez que removemos os espaços extras e corte a corda, obtemos a matriz de DEPS. O código a seguir é a parte que modificamos:
A cópia do código é a seguinte:
var a = array.prototype.slice.call (argumentos, 0);
...
args.push (self.dependências [d] && d! = ''? self.dependências [d]: a.shift ());
No código acima, atravessamos o projeto de dependência, se houver itens ausentes, se houver peças ausentes no projeto de dependência, obtemos -o do objeto Argumentos. Se uma matriz for uma matriz vazia, o uso do método de mudança retornará apenas indefinido sem lançar um erro. Até agora, a nova versão do Injetor se parece com o seguinte:
A cópia do código é a seguinte:
var doSomething = injetor.Resolve (função (serviço, outro, roteador) {
espera (serviço (). nome) .to.be ('serviço');
Espere (Router (). Nome) .to.be ('roteador');
Espere (outros) .to.be ('Outro');
});
doSomething ("outro");
No código acima, podemos confundir a ordem das dependências à vontade.
Mas nada é perfeito. Há um problema muito sério com a injeção de dependência dos métodos de reflexão. Um erro ocorre quando o código é simplificado. Isso ocorre porque, durante a simplificação do código, o nome das alterações do parâmetro, o que fará com que as dependências sejam resolvidas. Por exemplo:
A cópia do código é a seguinte:
var doSomething = function (e, t, n) {var r = e (); var i = t ()}
Então, precisamos da solução a seguir, como no AngularJS:
A cópia do código é a seguinte:
var doSomething = injetor.Resolve (['serviço', 'roteador', função (serviço, roteador) {
}]);
Isso é muito semelhante à solução AMD que vi no início, para que possamos integrar os dois métodos acima, e o código final é o seguinte:
A cópia do código é a seguinte:
var injector = {
Dependências: {},
registro: function (chave, valor) {
this.dependências [key] = value;
},
Resolva: function () {
var func, deps, escopo, args = [], self = this;
if (typeof argumentos [0] === 'string') {
func = argumentos [1];
DEPS = argumentos [0] .Place ( / / g, '') .split (',');
escopo = argumentos [2] || {};
} outro {
func = argumentos [0];
deps = func.toString (). Match (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .Rplace (//g, '') .split (',');
escopo = argumentos [1] || {};
}
Return function () {
var a = array.prototype.slice.call (argumentos, 0);
for (var i = 0; i <deps.Length; i ++) {
var d = deps [i];
args.push (self.dependências [d] && d! = ''? self.dependências [d]: a.shift ());
}
func.apply (escopo || {}, args);
}
}
}
Esta versão do método Resolve pode aceitar dois ou três parâmetros. Aqui está um código de teste:
A cópia do código é a seguinte:
var doSomething = injetor.Resolve ('roteador, serviço', função (a, b, c) {
Espere (a (). Nome) .to.be ('roteador');
Espere (b) .to.be ('Outro');
espere (c (). nome) .to.be ('serviço');
});
doSomething ("outro");
Você deve ter notado que não há nada entre duas vírgulas, e isso não é um erro. Esta vaga é deixada para o outro parâmetro. É assim que controlamos a ordem dos parâmetros.
Conclusão
No conteúdo acima, introduzimos vários métodos de injeção de dependência em JavaScript. Espero que este artigo possa ajudá -lo a começar a usar a técnica de injeção de dependência e escrever código de estilo de injeção de dependência.