Como vincular isso corretamente a um getter / setter em Javascript

Aug 25 2020

Digamos que eu tenha uma classe que armazena as propriedades de suas instâncias em um objeto aninhado:

this.Properties = {
  "Position":{
    "X": 400,
    "Y": 100
  },
  "Colour": "#007fff7f"
};

Eu queria definir getters / setters especiais para cada uma das propriedades (aninhadas) para que eu pudesse adicionar verificações de intervalo / atualizar automaticamente as propriedades de elementos HTML específicos da instância, etc. Quando tentei com o método normal, percebi que não conseguiu vincular o escopo a um argumento nos getters / setters:

//(based on https://stackoverflow.com/a/16400626)
//Define function prototype for binding an argument without overriding the old this:
Function.prototype.BindArgs = function(...boundArgs){
  const targetFunction = this;
  return function (...args) { return targetFunction.call(this, ...boundArgs, ...args); };
};

//...

{
  get X(){
    return this.__X__;
  },
  set X(Scope, Value){
    this.__X__ = Value;
    Scope.HTMLElement.style.left = Value + "px";
  }.BindArgs(this)  //This is incorrect syntax
}

O código acima não funciona: não porque BindArgs seja um protótipo inválido, mas, em vez disso, não funciona porque o configurador não é realmente uma função . A resposta sugeriu usar Object.defineProperty, que realmente funcionou:

Object.defineProperty(this.Properties.Position, "X", {
  "get": function(){
    return this.__X__;
  }
  "set": function(Scope, Value){
    this.__X__ = Value;
    Scope.HTMLElement.style.left = Value + "px";
  }.BindArgs(this)
});

Agora, quando tenho algumas propriedades como no exemplo acima, não há problema, mas ter que fazer isso para dezenas de propriedades torna-se extremamente tedioso - especialmente para propriedades aninhadas. Existe outra maneira mais organizada de definir getters / setters personalizados e ser capaz de vincular argumentos a eles? A sintaxe normal teria sido ideal, pois estaria tudo dentro da definição do objeto e não espalhado como Object.defineProperty. A resposta óbvia seria usar funções normais para obter / definir os valores, mas fazer isso significaria ter que refatorar muito código ...

Respostas

4 Klaycon Aug 25 2020 at 00:31

Eu sugiro que você use Proxies para validação . Exige mudanças de código mínimas e você pode cuidar de várias propriedades de uma só vez.

let validator = {
  set: function(obj, prop, value) {
    //in any of these cases you can return false or throw an error to refuse the new value
    switch(prop) {
      case "X":
        Scope.HTMLElement.style.left = value + "px";
        break;
      case "Y":
        Scope.HTMLElement.style.top = value + "px";
        break;
      case "Colour":
        Scope.HTMLElement.style.color = value;
    }

    obj[prop] = value;

    return true;
  }
};

this.Properties.Position = new Proxy(this.Properties.Position, validator);
this.Properties = new Proxy(this.Properties, validator);

Observe que ele usa um atalho (o mesmo validador para Propertiese Properties.Position), se você achar que pode haver sobreposições de nome de propriedade, pode precisar de vários validatorobjetos.