#javascript #lazy-evaluation
#javascript #ленивая оценка
Вопрос:
Мне интересно, возможно ли сделать что-то вроде следующего:
var obj = {
counter: (function(){
if(!this.val){ this.val = 0; }
this.val = 1;
return this.val;
})();
};
console.log(obj.counter); //should output 1
console.log(obj.counter); //should output 2
console.log(obj.counter); //should output 3
...
Есть ли способ получить поле из такого объекта, чтобы оно переоценивало функцию при каждом обращении к ней?
Комментарии:
1. Почему бы вам не использовать функцию вместо поля
2. Счетчик определяется как IIFE, поэтому ваша ссылка
this
теряется для самого IIFE. Таким образом, каждый раз, когда вы вызываетеobj.counter
,this.val
будет неопределенным и, следовательно, будет установлено значение 0, затем 1. @ksven опередил меня с кодом ответа, поэтому отправляю в качестве комментария.3. хорошо, могу ли я поместить фактическую переменную подсчета в другое место и при этом получить автоматическое увеличение?
Ответ №1:
Вы можете использовать геттер:
var obj = {};
Object.defineProperty(obj,"counter",{
get: function() {
this.val = this.val || 0;
this.val ;
return this.val;
}
});
console.log(obj.counter); // 1
console.log(obj.counter); // 2
console.log(obj.counter); // 3
Комментарии:
1. Это здорово, я не знал, что он существует. Спасибо!
Ответ №2:
Это возможно с прокси-серверами, если ваша целевая платформа их поддерживает:
var obj = Proxy.create({
get: function(target, value) {
if(value == 'counter')
return this.val = (this.val || 0) 1;
}
});
console.log(obj.counter); //should output 1
console.log(obj.counter); //should output 2
console.log(obj.counter); //should output 3
Другим вариантом может быть геттер:
obj = Object.create({}, {
counter: {
get: function() {
return this.val = (this.val || 0) 1;
}
}
})
или valueOf
объект (это не работает с console.log
, но работает с арифметикой):
var obj = {
counter: {
valueOf: function() {
return this.val = (this.val || 0) 1;
}
}
};
console.log(obj.counter 5); // 6
console.log(obj.counter 5); // 7
console.log(obj.counter 5); // 8
Ответ №3:
Использование Proxy
класса, а также варианта использования для оценки требований для оценки свойств
/* https://github.com/hack2root/lazyeval */
let lazy = (eval) => ((data) => new Proxy(data, {
set(obj, key, val) {
obj[key] = val;
eval(obj);
}
}))({});
// 1. ARRANGE
let a = 1;
let b = 2;
let c;
// 2. ACT
let func = lazy((f) => {
if (f.a amp;amp; f.b) {
c = f.a f.b
}
});
func.a = a;
func.b = b;
// 3. ASSERT
console.log("c is", c);
let lazy_require = (requre) => (eval) => ((data) => new Proxy(data, {
set(obj, key, val) {
obj[key] = val;
if (requre(obj)) {
eval(obj);
}
}
}))({});
// 1. ARRANGE
let a_f = 1;
let b_f = 2;
let c_f;
// 2. ACT
let func_reqire = lazy_require((f) => f.a amp;amp; f.b);
let lazy_func = func_reqire((f) => {
c_f = f.a f.b
});
lazy_func.a = a_f;
lazy_func.b = b_f;
// 3. ASSERT
console.log('c_f is', c_f);
let lazy_require_data = (requre) => (eval) => (data) => new Proxy(data, {
set(obj, key, val) {
obj[key] = val;
if (requre(obj)) {
eval(obj);
}
}
});
// 1. ARRANGE
let a_data = 1;
let b_data = 2;
let c_data;
// 2. ACT
let func_require_data = lazy_require_data((f) => f.a amp;amp; f.b);
let func_data = func_require_data((f) => {
c_data = f.a f.b
});
let func_json = func_data({
a: a_data,
b: b_data
});
func_json.a = a;
func_json.b = b;
// 3. ASSERT
console.log('c_data is', c_data);