Reputation: 2616
I am wondering if it is possible to do something like the following:
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
...
Is there a way to get a field from an object like this such that it re-evaluates a function each time it is accessed?
Upvotes: 3
Views: 4506
Reputation: 43
const obj = {
counter: function* () {
let val = 1;
while (true) yield val++;
}
};
const foo = obj.counter()
const bar = obj.counter()
console.log(foo.next().value); // 1
console.log(foo.next().value); // 2
console.log(foo.next().value); // 3
console.log(bar.next().value); // 1
console.log(bar.next().value); // 2
console.log(bar.next().value); // 3
You can also initialize the generator as an IIFE to prevent multiple counters per instance of obj
.
const obj = {
counter: (function* () {
let val = 1;
while (true) yield val++;
})(),
};
console.log(obj.counter.next().value); // 1
console.log(obj.counter.next().value); // 2
console.log(obj.counter.next().value); // 3
Upvotes: 0
Reputation: 2566
Using Proxy
class as well as use case for evaluate requrements for property evaluation
/* 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 && 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 && 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 && 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);
Upvotes: 0
Reputation: 215029
This is possible with proxies if your target platform supports them:
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
Another option would be a getter:
obj = Object.create({}, {
counter: {
get: function() {
return this.val = (this.val || 0) + 1;
}
}
})
or a valueOf
object (this doesn't work with console.log
, but does with arithmetic):
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
Upvotes: 4
Reputation: 324780
You can use a getter:
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
Upvotes: 9