origem
Alguns dias atrás, eu estava assistindo a implementação de algumas estruturas populares MVVM (como estruturas mais leves, como Avalon.js, vue.js, em vez de estruturas mais pesadas, como Angularjs e Emberjs). As estruturas populares MVVM modernas geralmente eliminam a ligação bidirecional de dados (ligação de dados de duas vias), que é um ponto de venda da própria estrutura (o Ember.js parece não suportar a ligação bidirecional de dados) e os métodos de implementação da ligação de dados bidirecionais de cada estrutura não são muito consistentes. Por exemplo, o Anguarjs usa a verificação suja internamente, enquanto a essência da implementação interna do Avalon.js é definir acessadores de propriedades.
Não pretendemos discutir a implementação específica da ligação de dados bidirecionais por cada estrutura aqui. Só falaremos sobre vários métodos comuns para implementar a ligação de dados bidirecionais no front end e focar na seleção técnica do Avalon.js para implementar a ligação de dados bidirecionais.
A implementação convencional da ligação de dados bidirecional
Primeiro, vamos falar sobre o que é a ligação de dados bidirecionais front-end. Simplificando, é a camada do controlador da estrutura (a camada do controlador aqui é um termo geral, que pode ser entendido como o middleware que controla o comportamento da visualização e conecta a camada do modelo) e a camada de exibição da interface do usuário (camada de exibição) para estabelecer um canal de dados bidirecional. Quando qualquer uma dessas duas camadas mudar, a outra camada fará automaticamente as alterações correspondentes imediatamente (ou parecem ser imediatamente).
De um modo geral, para perceber essa relação de ligação de dados bidirecional (o processo de correlação entre a camada do controlador e a camada de exibição), atualmente existem três maneiras no front-end.
1. Verificação suja
2. Mecanismo de observação
3. Encapsular o acessador de propriedades
Verificação suja
Dizemos que o AngularJS (aqui se refere especificamente à versão AngularJS 1.xx, que não representa a versão AngularJS 2.xx) é uma implementação técnica da ligação de dados bidirecional. O princípio geral é que o AngularJS manterá uma sequência e colocará todos os atributos que precisam ser monitorados nessa sequência. Quando determinados eventos específicos ocorrem (observe que isso não é cronometrado, mas acionado por alguns eventos especiais), o AngularJS chamará o método $ Digest. A lógica dentro desse método é atravessar todos os observadores, comparar os atributos monitorados e comparar se o valor do atributo mudou antes e depois da chamada do método. Se mudar, o manipulador correspondente será chamado. Existem muitos artigos na Internet que analisam o princípio de implementação da ligação de dados bidirecional do AngularJS, como este artigo, e assim por diante.
As desvantagens deste método são óbvias. A Traversing and Training Watchers é muito consumido, especialmente quando o número de monitoramento de uma única página atinge uma ordem de magnitude.
Mecanismo de observação
O blogueiro teve um artigo reimpresso e traduzido, a alteração de ligação de dados provocada pelo object.observe (), que se refere ao uso do método Object.ObServe no ECMAScript7 para monitorar e observar o objeto (ou suas propriedades). Depois de mudar, o manipulador correspondente será executado.
Esta é a maneira mais perfeita de monitorar as alterações de dados do atributo no momento. O suporte nativo do idioma (navegador) não é nada melhor do que isso. O único arrependimento é que a atual variedade de apoio não é boa o suficiente e precisa ser totalmente promovida.
Acessador de propriedades de encapsulamento
Existe um conceito de métodos mágicos no PHP, como os métodos __get () e __set () no PHP. Existe um conceito semelhante em JavaScript, mas não é chamado de método mágico, mas um acessador. Vejamos um código de amostra.
var dados = {name: "erik", getName: function () {return this.name;}, setName: function (name) {this.name = name;}};A partir do código acima, podemos ter um vislumbre do salto, como os métodos getName () e setName () nos dados. Podemos simplesmente considerá -lo como o acessador (ou um acessador) de dados.name.
De fato, para o código acima, se for mais rigoroso, não poderá acessar diretamente a propriedade Data.name. Toda a leitura e escrita para data.name devem ser passadas pelos métodos Data.getName () e Data.SetName (). Portanto, imagine que, uma vez que uma propriedade não permita a leitura e a escrita diretas, mas deve ser lido e escrito através do acessador, é claro que faço alguns extras reescrevendo o método acessador da propriedade, como o monitoramento da alteração do valor da propriedade. Este é o princípio do uso de acessores de propriedades para fazer a ligação de dados bidirecional.
Obviamente, esse método também tem suas desvantagens. O mais proeminente é que toda vez que um monitoramento de atributos é adicionado, um método de acessador correspondente deve ser adicionado a esse atributo; caso contrário, a alteração desse atributo não será capturada.
Método Object.DefineProperty
O princípio da estrutura doméstica da MVVM Avalon.js que implementa a ligação bidirecional de dados é o acessador da propriedade. Mas é claro que não será tão original quanto o código de exemplo acima. Ele usa o método padrão do objeto de propriedade. Em resposta às condições do mercado doméstico, alguns não suportam objeto.DefineProperty. Os navegadores de baixo nível usam o VBScript para compatibilidade perfeita, diferentemente de outras estruturas MVVM que gradualmente abandonaram o apoio a navegadores de ponta.
Vamos primeiro definir o método Object.DefineProperty no MDN.
O método Object.DefineProperty () define uma nova propriedade diretamente em um objeto ou modifique uma propriedade existente em um objeto e retorna o objeto.
O significado é claro e o método objeto.DefineProperty fornece uma maneira direta de definir propriedades do objeto ou modificar as propriedades de objeto existentes. O protótipo do método é o seguinte:
Object.DefineProperty (OBJ, prop, descritor)
em,
obj, objeto a ser modificado
prop, com nome de atributo modificado
descritor, a descrição relevante dos atributos a serem modificados
O descritor exige que um objeto seja passado, seu valor padrão é o seguinte
/*** @{param} descritor*/{Configurável: false, enumerável: false, gravidade: false, valor: nulo, set: indefinido, get: indefinido}Configurável, seja a propriedade configurável. Os significados configuráveis incluem: se o atributo pode ser excluído, seja as propriedades graváveis, enumeráveis e configuráveis do atributo podem ser modificadas.
enumerável, se o atributo é enumerável. O significado de enumerável inclui: se ele pode ser atravessado por ... in e se pode ser obtido através do método Object.Keys ().
gravável, se o atributo pode ser reescrito. O significado de reescrita inclui: se o atributo pode ser transferido.
valor, o valor padrão da propriedade.
Set, um reescritor de propriedade (por enquanto, é isso). Depois que o atributo é reatribuído, esse método é chamado automaticamente.
Obtenha, o leitor da propriedade (por enquanto, é isso). Depois que a propriedade é acessada e lida, esse método é chamado automaticamente.
Aqui está um código de amostra,
var o = {}; object.DefineProperty (o, 'nome', {value: 'erik'}); console.log (object.getownPropertyDescriptor (o, 'nome')); // objeto {value: "Erik", gravável: falso, enumerável: falso, configurável: false} object.DefineProperty (o, 'idade', {value: 26, configurável: verdadeiro, escritor: true}); console.log (o.age); // 26o.age = 18; console.log (O.age); // 18. Porque a propriedade AGE é um console de reescrito.log (object.Keys (O)); // []. Nem o nome nem a propriedade da idade são um objeto enumerável.DefineProperty (O, 'Sex', {Value: 'masculino', gravável: false}); o.sex = 'feminino'; // A tarefa aqui é na verdade console ineficaz.log (O.sex); // 'masculino'; excluir o.sex; // FALSO, a ação de exclusão de propriedade também é inválidaApós o exemplo acima, em circunstâncias normais, o uso de object.DefinePropert () é relativamente simples.
No entanto, ainda há uma coisa que precisa de atenção adicional. Quando o método object.DefineProperty () define propriedades, a propriedade não pode declarar os atributos do acessador (set e get) e os atributos graváveis ou de valor ao mesmo tempo. Isso significa que, se uma determinada propriedade for definida com um atributo gravável ou de valor, essa propriedade não poderá declarar Get and Set e vice -versa.
Porque quando o Object.DefineProperty () declara uma propriedade, mais de dois tipos de controles de acesso não são permitidos para a mesma propriedade.
Código de exemplo,
var o = {}, myName = 'Erik'; object.DefineProperty (o, 'nome', {value: myName, set: function (name) {myName = name;}, get: function () {return myName;}});O código acima parece estar bem, mas relatará um erro quando for realmente executado, e o erro será relatado da seguinte forma.
TypeError: propriedade inválida. Uma propriedade não pode ter acessadores e ser gravável ou ter um valor, #<ject>
Como o atributo de nome aqui declara o atributo de valor, o conjunto e obtém atributos ao mesmo tempo, os quais fornecem dois controles de leitura e gravação no atributo de nome. Se o recurso de valor não for declarado aqui, mas o recurso gravável for declarado, o resultado será o mesmo e um erro será relatado.