#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?».
Существует несколько способов достижения этой цели.
- Вычислите значение при инициализации. Затем обновите его соответствующим образом при вызове методов, которые повлияют на значение.
- Вместо того, чтобы отслеживать вычисленное значение, вы также можете выбрать его вычисление, когда оно вам нужно. Затем при повторном вызове с теми же аргументами (зависимостями) возвращает ранее вычисленное значение. Пересчитывайте только при изменении одного из аргументов.
Вероятно, есть и другие способы избежать повторного вычисления значения, но это те два, которые приходят на ум. Ниже я разработал пример второго метода.
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 Я обновил свой ответ, но в его нынешнем виде вторая часть не соответствует первоначально заданному вопросу. Даже если это решит вашу проблему.