Implementando JavaScript GenericsProblema Você deseja implementar "genéricos" em JavaScript. Linguagens de programação Theory, como Java e C # tem um conceito de programação chamada genéricos. A idéia por trás de genéricos é escrever o código de tal forma que um identificador é definido como um tipo geral, que é manipulada em uma classe ou método. Por exemplo, o seguinte código seria uma classe genérica em Java ou C #: classe Container A classe Container tem um parâmetro genérico, tipo, o que pode fazer referência a qualquer outro tipo. E na declaração de Container, o membro de dados _MANAGED é o mesmo tipo como o parâmetro genérico. Em uma noção abstrata, Container gerencia qualquer tipo. Um programador de JavaScript pode olhar o código e pensar: "Ei, eu tenho este já em JavaScript!" Aqui é o mesmo código, desta vez em JavaScript: function Container () (this._managed = / * qualquer tipo * /) Como ilustrado na Receita 2-8, você pode ter referência com base tipagem pato ou baseado em valor de digitação pato. Execução "genéricos" que o JavaScript é como a implementação de um pré-processamento, o que nós queremos baseado em valor de digitação pato. O exemplo de código a seguir mostra o que pode dar errado quando você mistura e pato valor de referência de digitação em conjunto: proxy: função (por exemplo, funcIdentifier, newFunc) (if (instance! [funcIdentifier]) (throw new Error ( "Cannot proxy método inexistente (" + + funcIdentifier ")");) eval ( "var generatedOrigFunc =" exemplo + [ funcIdentifier]. toString ()); eval ( "var generatedProxyFunc =" + newFunc.toString ());instância [funcIdentifier] = function () ( origFunc var = generatedOrigFunc; proxyFunc var = generatedProxyFunc;var args = new Array (); for (var c1 = 0; c1 <arguments.length; c1 + +) (args.push (argumentos [c1]);) args.push (origFunc); args.push (argumentos) ; proxyFunc.apply (this, args);)) Este código-fonte é um exemplo de como não escrever uma implementação do padrão Proxy. O problema da implementação é mostrado no código a negrito. No início do código em negrito são duas declarações eval, que estão gerando os valores das variáveis generatedOrigFunc e generatedProxyFunc. Na instância função incorporada [funcIdentifier], as variáveis são referenciadas, e devido ao encerramento, as variáveis origFunc e proxyFunc de referência serão as variáveis corretas. O conceito do padrão Proxy é que quando um método é chamado, primeiro ele chama a função referida por proxyFunc, que depois chama a função referenciado por origFunc. Porque proxyFunc é chamado em primeiro lugar, ele tem a capacidade para pré-ou pós-processar os dados.
O código escrito como vai funcionar com uma única aplicação do padrão Proxy, mas ele vai fracassar se o padrão proxy é aplicado várias vezes. Por exemplo, imagine o código gerado incorporação do padrão Proxy em um padrão Proxy já aplicadas. Este código é um exemplo de um "gotcha" pedaço de código em JavaScript. Olhe atentamente as declarações das variáveis generatedOrigFunc e generatedProxyFunc o que eles estão fazendo referência? Para entender o erro, acho que o código de proxy do lado esquerdo como a implementação de proxy 1 (PI1) eo código do lado direito como a implementação de proxy 2 (PI2). Se um chamador genéricos foram chamar PI1, a seguinte seqüência de eventos, terá lugar: 1. PI1 é chamado. 2. ProxyFunc PI1 é chamado. 3. OrigFunc PI1 é chamado, que é PI2. 4. ProxyFunc PI2 é chamado, que é proxyFunc PI1's. 5. ProxyFunc PI1 é chamada, chamando origFunc PI1's. Seguindo esses passos, você irá notar que uma recursão está ocorrendo. A recursividade é devido à forma como as variáveis generatedOrigFunc e generatedProxyFunc são declarados. Essas variáveis são declaradas sem qualquer âmbito e alcance, porque a função de proxy é amix de usar referências de código eval. Esta é uma prática ruim, pois algumas variáveis serão serializados e outros não. Nós queremos uma solução como o pré-processador C, onde "genéricos" representam a substituição física de um identificador com um tipo desejado. Não queremos um sistema de templates. Um sistema de templating seria descrita como segue: <% for (var count = 0; count <10; count + +) (%> série [<% count% =>] = GenSeries (<% count% =>) <%}%> Este não é o que queremos, pois a legibilidade de código diminui drasticamente e, em geral, não é necessário. JavaScript é uma linguagem dinâmica de código, e muitas das construções templating pode ser implementado no código. É mais difícil de implementar a mudança de uma variável de uso de referências à utilização de valores da solução. Para fins de ilustração, vamos simplificar o padrão proxy para um único método com uma única chamada de método, como segue. Fonte: / website / ROOT / ajaxrecipes / javascript / generics.html função EmbeddedReplace () (var func = __toCall; info ( "Substituir", "Olá"); func ();) A função EmbeddedReplace tenha declarado uma variável local, func. A func local referências variável a variável __toCall. Após a declaração de função é uma chamada para a função de informações. A chamada última função é a função, que é uma chamada de função para __toCall. De olhar apenas ao uso de __toCall, você não sabe muito sobre a declaração de que não é uma variável global. Para o alcance desta receita, __toCall é um identificador que será substituído. Uma chamada de expansão JavaScript seria o seguinte (vou discutir os detalhes em breve): EmbeddedReplace = Generics.expand (EmbeddedReplace, (__toCall: function () (info ( "ter em Substituir", "Olá ");}}); EmbeddedReplace (); O método Generics.expand tem dois parâmetros. O primeiro parâmetro é a função EmbeddedReplace, que terá sua implementação modificados. O segundo parâmetro é a estrutura do objeto que representa o que os identificadores serão substituídos. A estrutura do objeto é definida de tal forma que o identificador de propriedade define o identificador de ser substituído, eo valor associado à propriedade é o valor substituído.Nota Todo o código aqui apresentado usa o padrão JavaScript técnicas de programação. Você não precisa usar amortecedores especiais ou tags que confundir os editores e tornar mais complicada a construção de código, confiável sustentável. Quando o método Generics.expand tiver terminado a execução, a declaração da função é gerada a seguinte: GeneratedEmbeddedReplace function () (var func = function (() (info ( "ter em Substituir", "Olá");)); info ( "Substituir", "Olá"); func ();) O buffer gerado é o que esperamos, eo comportamento é o que esperamos. Quando GeneratedEmbeddedReplace é chamado, a função incorporada é chamado. Para efeitos de comparação, vamos analisar como isso poderia ter funcionado sem expandir uma função incorporada: __toCall function () (info ( "ter em Substituir", "Olá");); EmbeddedReplace função func () (var = __toCall; info ( "Substituir", "Olá"); func ();) GeneratedEmbeddedReplace Calling irá chamar a função __toCall e comportar-se, olhar e sentir como o exemplo expandida. Mas por que você deveria fazer isso? A referência baseado em expansão não é uma expansão em todos. Você tem simplesmente criado uma variável global (__toCall) que pode ser atribuída a execução. Com um valor de expansão do tipo, você pode ter múltiplas funções, com diferentes funcionalidades que não estão em conflito um com o outro. Porque o comportamento é determinado pela tarefa, você pode correr para a situação descrita na Receita 2-14, onde as funções são compartilhadas. Se as funções são compartilhadas, em seguida, um conflito pode surgir se a funcionalidade da função é alterada. Ou você pode ter uma situação em que a função faz uma coisa de uma vez, e outra coisa é outra época. Para ilustrar a "fazer uma coisa agora, mas não mais tarde" o problema, considere o cenário seguinte código: __toCall function () (info ( "ter em Substituir", "Olá");); função EmbeddedReplace () (info ( "Substituir", "Olá"); __toCall ();) function Caller () (EmbeddedReplace (); __toCall = function () () EmbeddedReplace ();) Quando o chamador é chamado, a primeira chamada para EmbeddedReplace resulta em alguma saída. Então __toCall é transferido, e chamando EmbeddedReplace novamente resulta em outra saída a ser gerado. Talvez este seja o efeito desejado, mas muito provavelmente não é. Uma maneira de resolver o problema é restringir o uso das capacidades dinâmicas de JavaScript. No entanto, neste momento gostaria de pedir, por que você está usando JavaScript e Ajax? Minha opinião é que o JavaScript e Ajax representa uma evolução na programação e, assim, você deve usar as suas capacidades de comportamento dinâmico. EmbeddedReplace tinha sido ampliado, em seguida, modificando __toCall teria nenhum efeito sobre EmbeddedReplace. E é isso que você precisa para considerar quando estiver a escrever código JavaScript. Às vezes, você vai usar referências, e às vezes você irá utilizar expansões, como os propostos por genéricos JavaScript. Gostaria de acrescentar mais um ponto. Não é uma regra, mas uma idéia de que em contextos específicos podem ter o seu lugar. Imagine escrever uma aplicação onde o pagamento é calculado com base na actual taxa de juro diária. Na maioria das linguagens de programação, o pagamento de juros atual seria uma variável que seriam carregadas e atribuída a execução. Usando generics JavaScript e comportamento do código base, o seguinte código expandida poderia ser usado: CalculateInterestPayment função (valor) (return quantidade * 0,04;) Este código é o que outras linguagens de programação chamada codificados. É considerado codificado, pois o número 0,04 é compilado para o aplicativo e não pode ser mudado. Mas aqui é a vantagem do comportamento do código-base: a utilização de genéricos JavaScript, o valor embutido é trivial mudar, e mantendo-se um modelo de programação tradicional, quando você não precisa não faz qualquer sentido. Você usa uma estratégia de programação codificados, quando os dados são leitura principalmente, Ou quando os dados são lidos mais frequentemente do que está escrito. Tendo os dados codificados pode ser um ganho de desempenho, oferecer maior flexibilidade e simplificar o algoritmo. Usando generics JavaScript hoje, o cálculo de juros é um simples número, mas amanhã o cálculo pode ser composto de um cálculo que usa uma regra de correr com base no valor que é processado. Tendo explicado o porquê, como e quando de JavaScript "genéricos", Vou agora abordar como JavaScript "genéricos" são implementadas. É um processo muito simples, que envolve pesquisa e substituição de um identificador em um buffer. A implementação completa é a seguinte. Fonte: / website / root / scripts / Common.js Generics = (var imagem: function (toProcess, itemsToInject) (var bufferToProcess = ops.singleSerialize (toProcess); for (itemToReplace em itemsToInject) (var recurFind = function (startIndex) (var offset = bufferToProcess.indexOf (itemToReplace, startIndex) if (offset == -1) (return;) var left = bufferToProcess.slice (0, offset); direito bufferToProcess.slice var = (offset + itemToReplace.length); meio var = ops.simpleSerialize (itemsToInject [itemToReplace]); bufferToProcess + meio = esquerda + direita; offset + +; recurFind (offset);) recurFind (0);) genBuffer var = "cls var =" + bufferToProcess + ";" eval (genBuffer); cls retorno;)) Para expandir um objeto de JavaScript, o objeto tem que primeiro ser convertido para um buffer. O ops.singleSerialize método converte qualquer objeto de JavaScript em um buffer, e no caso do exemplo anterior, ele converte o parâmetro toProcess em um buffer. Após a conversão para um buffer, o processo de encontrar um identificador e substitui-lo usando um ciclo é iniciado. Com cada iteração do loop armazenado na variável itemToReplace é o identificador para substituir, no bufferToProcess. No circuito, as referências recurToFind variável de uma função que é chamada recursivamente. O objectivo da recursividade é a pesquisa incremental do buffer para o identificador de substituir. Nota Estratégia de recursão é coberto de Receita 2-6. Para encontrar um identificador dentro bufferToProcess, o método indexOf é usado. indexOf tem dois parâmetros: o primeiro parâmetro é o identificador de encontrar, eo segundo parâmetro é o índice para iniciar a busca. IndexOf Se não encontrar um identificador, em seguida, um valor de -1 é devolvido e as paradas de recursão. IndexOf se encontra o identificador, o índice de onde começa o identificador é retornado. Tendo encontrado o identificador, o buffer é dividido em uma parte da esquerda e uma parte da direita. As partes esquerda e direita são combinadas com o texto a ser substituído, e um bufferToProcess novo é criado. Identificadores Quando não forem encontrados, a reserva é considerada expandida. O último passo é converter o restante do buffer em uma instância do objeto. Convertendo a reserva é um problema porque executar o return eval (bufferToProcess) comando pode levar a situações imprevisíveis. Para fazer a conversão previsível, bufferToProcess é concatenado com uma atribuição à variável CLS. Então, quando o novo buffer é executada usando a instrução eval, o CLS variável local é instanciado, e pode ser devolvido para o chamador. O código de ampliar e implementar JavaScript "genéricos" é relativamente simples, mas os efeitos são profundos. Ao utilizar JavaScript "genéricos", manter os seguintes pontos em mente: • referências JavaScript são mais fáceis de implementar e, em muitos casos, suficiente, bom. No entanto, como apontado na Receita 2-14, as referências não constituem um "gotcha" problema. • Você tem que tomar uma decisão entre o uso de referências para tudo e usando o eval para tudo. "Tudo" aqui não se refere a um aplicativo inteiro, mas tudo no âmbito de um tipo ou função. Não misturar referências com a instrução eval, isso irá causar problemas. • Em geral, o código é mais robusto e estável, se você usar eval-como técnicas. Referências de trabalho para casos simples, mas se incorpora o seu código de código várias vezes, os erros podem resultar. • Com referências, você corre o risco de que várias partes de código de referência será o mesmo código e as mesmas variáveis. Isto pode causar a corrupção e não deve ser subestimado. Eval • Utilizando-como declarações torna a depuração mais fácil, porque se você serializar uma função ou objeto, você irá obter o estado atual do código e, portanto, ser capaz de seguir o que é o problema. Se você usar referências, você vai precisar de um depurador, e com o código que determina o seu comportamento em tempo de execução, depuração pode ser entediante. • Utilizando JavaScript "genéricos", como descrito nesta receita é IDE-friendly, porque você não está criando buffers à mão, e você não estiver a escrever código com as tags especiais que IDEs como X-desenvolver, Visual Studio, Komodo, e Visual SlickEdit fazer Não entendo. um artigo submetido por Sonja Lande Isenção de responsabilidade:O nosso site não se responsabiliza pelo conteúdo deste artigo. Webarticles é uma fonte de informação livre. Importante: Este artigo "Aplicação JavaScript Generics" foi traduzida por um software automático. Nós sentimos muito por quaisquer erros de ortografia que pode ter ocorrido. Obrigado pela sua compreensão.
|
|||||
| Online: 315 users browsing the articles directory |
|
|