Автоматический запуск функции получения JS

#javascript

#javascript

Вопрос:

У меня есть упрощенный код ниже:

 function ValueTime(birthDate, presentValue, maxMonths = 1200){
    this.birthDate = birthDate;
    this.presentValue = presentValue;
    this.maxMonths = maxMonths;
    this.maxYears = 0;

    Object.defineProperties(this, {
        "calcValue": {
            "get": function(){
                this.maxYears = this.maxMonths/12;
                return this.presentValue * 1.25;
            }
        }
    });

}

const myVariable = new ValueTime(new Date(1985,06,23),1000);

console.log(myVariable);
console.log(myVariable.calcValue);
console.log(myVariable.maxYears);
 

Если я сначала запущу myVariable.calcValue, как в примере, я получу правильно установленное значение maxYears. Есть ли способ правильно получить максимальные годы, не инициируя функцию get вручную ( myVariable.calcValue ). Т.е. Заставить функцию get запускаться автоматически. Не беспокойтесь о значении maxYears, я знаю, что это просто установить в противном случае, но фактический код намного сложнее. Мой вопрос касается запуска getter при инициализации функции конструктора.

Комментарии:

1. Обычно вы действительно не хотите, чтобы геттеры изменяли состояние, которое действительно сбивает с толку людей, читающих / использующих ваш код.

2. Я понимаю, к чему клонят два других ответа. Речь идет не о фактическом значении maxYears. В реальном коде средство получения возвращает массив объектов на основе тысяч вычислений. maxYears — это текущий итог, и я стараюсь избежать необходимости выполнять вычисления дважды, чтобы установить каждый возврат.

Ответ №1:

Просто вызовите calcValue из своей функции конструктора:

 function ValueTime(birthDate, presentValue, maxMonths = 1200) {
  this.birthDate = birthDate;
  this.presentValue = presentValue;
  this.maxMonths = maxMonths;
  this.maxYears = 0;

  Object.defineProperties(this, {
    "calcValue": {
      "get": function() {
        this.maxYears = this.maxMonths / 12;
        return this.presentValue * 1.25;
      }
    }
  });
  
  console.log(this.calcValue); // Or this.maxYears, etc.
}

const myVariable = new ValueTime(new Date(1985, 06, 23), 1000); 

Комментарии:

1. Я понимаю, к чему клонят два других ответа. Речь идет не о фактическом значении maxYears. В реальном коде средство получения возвращает массив объектов на основе тысяч вычислений. maxYears — это текущий итог, и я пытаюсь избежать необходимости выполнять вычисления дважды, чтобы установить каждый возврат.

Ответ №2:

Я не совсем уверен, почему calcValue , который является геттером, также вычисляет и устанавливает значение maxYears . Похоже, вы хотите maxYears быть его собственным вычисляемым свойством.

 function ValueTime(birthDate, presentValue, maxMonths = 1200){
    this.birthDate = birthDate;
    this.presentValue = presentValue;
    this.maxMonths = maxMonths;

    Object.defineProperties(this, {
        maxYears: {
            // Add `enumerable: true` if you want to make the property show
            // up when iterating the object properties. eg. `Object.keys(obj)`
            // or `for (const key in obj)`, etc.
            enumerable: true,
            get: function () { return this.maxMonths / 12 },
            set: function (maxYears) { this.maxMonths = maxYears * 12 },
        },
        calcValue: {
            get: function () { return this.presentValue * 1.25 },
            set: function (calcValue) { this.presentValue = calcValue / 1.25 },
        },
    });
}

const myVariable = new ValueTime(new Date(1985,06,23),1000);

console.log(myVariable);
console.log(myVariable.maxMonths);
console.log(myVariable.maxYears);
myVariable.maxYears = 42;
console.log(myVariable.maxMonths);
console.log(myVariable.maxYears);
console.log(myVariable.calcValue); 

Если вы ищете способ просто установить maxYears без пересчета. Вы могли бы сделать это, перейдя this.maxMonths = maxYears * 12 в конструктор, опустив для него весь getter / setter . Однако, когда вы это сделаете и maxMonths внесете изменения, maxYears они не будут автоматически обновляться.


Судя по вашему комментарию, похоже, что ваш вопрос на самом деле не тот, который вы задали, а скорее в духе «Как я могу избежать повторного вычисления возвращаемого значения getter?».

Существует несколько способов достижения этой цели.

  1. Вычислите значение при инициализации. Затем обновите его соответствующим образом при вызове методов, которые повлияют на значение.
  2. Вместо того, чтобы отслеживать вычисленное значение, вы также можете выбрать его вычисление, когда оно вам нужно. Затем при повторном вызове с теми же аргументами (зависимостями) возвращает ранее вычисленное значение. Пересчитывайте только при изменении одного из аргументов.

Вероятно, есть и другие способы избежать повторного вычисления значения, но это те два, которые приходят на ум. Ниже я разработал пример второго метода.

 const memorize = (() => {
  const eqArray = (a, b) => a.length === b.length amp;amp; a.every((_, i) => a[i] === b[i]);
  return (fn) => {
    let result, prevArgs;
    return (...args) => {
      if (!prevArgs || !eqArray(args, prevArgs)) {
        prevArgs = args;
        result   = fn(...args);
      }
      return resu<
    };
  };
})();

// demo
const add = memorize((a, b) => {
  console.log("calculate", a, " ", b);
  return a   b;
});

console.log("--- demonstration ---");
console.log("first call (calculate)");
console.log(add(1, 2));
console.log("second call with same arguments (memorized)");
console.log(add(1, 2));
console.log("third call with new arguments (re-calculate)");
console.log(add(2, 3));

// applied to question scenario
function ValueTime(birthDate, presentValue, maxMonths = 1200) {
  this.birthDate = birthDate;
  this.presentValue = presentValue;
  this.maxMonths = maxMonths;

  Object.defineProperties(this, {
    maxYears: {
      get: (() => {
        const calcMaxYears = memorize((maxMonths) => {
          console.log("calculate maxYears");
          // simulate heavy workload
          const until = Date.now()   2000;
          while (Date.now() < until);
          return maxMonths / 12;
        });
        return function () { return calcMaxYears(this.maxMonths) };
      })(),
    }
  });
}

const myVariable = new ValueTime(new Date(1985,06,23),1000);

console.log("--- applied to your scenario ---");
console.log("first maxYears call (calculate)");
console.log(myVariable.maxYears);
console.log("second maxYears call (memorized)");
console.log(myVariable.maxYears);
myVariable.maxMonths = 504;
console.log("first maxYears call re-assigning maxMonths (re-calculate)");
console.log(myVariable.maxYears);
console.log("second maxYears call after re-assigning maxMonths (memorized)");
console.log(myVariable.maxYears); 
 Check your browswer console for output (re-run snippet). The output
below only shows when the JavaScript completely finishes exectuting. 

Обратите внимание, что равенство проверяется с === помощью оператора. Это означает, что вы не должны изменять объекты, передаваемые в качестве аргументов. Поскольку ссылки сравниваются, они будут рассматриваться как одно и то же значение, если вы это сделаете.

 const add = memorize((a, b) => a.value   b.value);
const a = { value: 1 };
const b = { value: 2 };
console.log(add(a, b)); // logs 3
a.value = 5; // modify the value (object reference stays the same)
console.log(add(a, b)); // logs 3
const c = { value: 5 };
console.log(add(c, b)); // logs 7
 

Комментарии:

1. Я понимаю, к чему приводят два других ответа. Речь идет не о фактическом значении maxYears. В реальном коде средство получения возвращает массив объектов на основе тысяч вычислений. maxYears — это текущий итог, и я пытаюсь избежать необходимости выполнять вычисления дважды, чтобы установить каждый возврат.

2. @RussellTemplar Я обновил свой ответ, но в его нынешнем виде вторая часть не соответствует первоначально заданному вопросу. Даже если это решит вашу проблему.