Как правильно привязать это к геттеру / сеттеру в Javascript

Aug 25 2020

Скажем, у меня есть класс, который хранит свойства своих экземпляров во вложенном объекте:

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

Я хотел определить специальные методы получения / установки для каждого из (вложенных) свойств, чтобы я мог добавлять проверки диапазона / автоматически обновлять свойства специфичных для экземпляра элементов HTML и т. Д. Когда я попробовал это обычным методом, я понял, что не удалось связать область видимости с аргументом в геттерах / сеттерах:

//(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
}

Приведенный выше код не запускается: не потому, что BindArgs является недопустимым прототипом, но вместо этого он не работает, потому что установщик на самом деле не является функцией . В ответе предлагалось использовать Object.defineProperty, который действительно работал:

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)
});

Теперь, когда у меня есть несколько свойств, как в приведенном выше примере, это было бы хорошо, но делать это для десятков свойств становится чрезвычайно утомительно - особенно для вложенных свойств. Есть ли другой, более аккуратный способ определения пользовательских методов получения / установки и возможности привязки к ним аргументов? Нормальный синтаксис был бы идеальным, поскольку все это было бы внутри определения объекта, а не разбросано, как Object.defineProperty. Очевидным ответом было бы использование обычных функций для получения / установки значений, но это означало бы необходимость рефакторинга большого количества кода ...

Ответы

4 Klaycon Aug 25 2020 at 00:31

Я предлагаю вам использовать прокси для проверки . Это требует минимальных изменений кода, и вы можете позаботиться о нескольких свойствах одним махом.

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);

Обратите внимание, что здесь используется ярлык (один и тот же валидатор для обоих Propertiesи Properties.Position). Если вы обнаружите, что имена свойств могут совпадать, вам может потребоваться несколько validatorобъектов.