- Provide easy transparent lazy evaluation pattern with memorization for values itself(not object properties)
` var _fn = function(a, b, c) { var d = new Evaluator( function() { console.log('hard calc...');
return 1;
}
);
var e = new Evaluator(
function()
{
console.log('hard calc...');
return 1;
}
);
// here complex logic of evaluation result where d or e can be accessed one or more times or never accessed
};
// example of function which defer final result evaluation(may be result never evaluated)
var _fn = function(a, b)
{
return new Evaluator(
function()
{
return a + b;
}
);
};
(function($G)
{
String.prototype._split = function(str)
{
var self = this;
var ev = Evaluator(
function()
{
return self.split(str);
}
);
// better than real String#split and Array#forEach because no overhead of array allocation
ev._each = function(_fn)
{
var p = 0, q;
for( ;; )
{
q = p;
p = self.indexOf(str, p);
if(p < 0)
break;
_fn(self.slice(q, p));
p += str.length;
}
if(q < self.length)
_fn(self.split(q));
};
return ev;
};
// demo
var parts = '1 2 3'._split(' ');
// no real split here
parts._each(
function(part){ /* do something */ }
);
// real split here because engine does not find property '0' in Evaluator instance
var firstPart = parts[0];
})(this);
`
- result is evaluated on access using any operators except
=ie (any arithmetic, logic,typeof,instanceof,in, dot(propertis access), any casts(ToObject,ToPrimitive,ToBoolean...),[[Class]]query,===,!==, square bracket operator, [[Call]]) =just makes reference to Evaluator instance- if value is evaluated once - it is stored and never will be evaluated again (memorization)
- Evaluator is fully transparent ie no build-in ways or possible hacks to detect some variable is Evaluator or not
- Evaluator instance can contains properties and if user code access to it - property getting or property function calling with this = Evaluator instance occurs instead evaluation. But
Object.prototype.hasOwnProperty.call(ev, propertyName)must evaluates instead returnsev.[[HasPropety]](propertyName). It keep transparency of Evaluator. Same rules for otherObject.prototype.*methods.
Emulation using power of Object#valueOf, Object#toString and __noSuchMethod__(if possible) for ES5- engines
This implementation calls evaluation when Object#valueOf normally is called ie all arithmetic operators, Number cast and when Object#toString is called ie square bracket operator and String cast. Object#__noSuchMethod__ also allows catch a._b() case(SpiderMonkey only).
` if ('noSuchMethod' in {}) { this.Evaluator = function(_fn) { var value;
var _restore = function()
{
this.valueOf = this.toString = function()
{
return value;
};
delete(this.__noSuchMethod__);
};
this.valueOf = this.toString = function()
{
value = _fn();
_restore.call(this);
return value;
};
this.__noSuchMethod__ = function(id, args)
{
value = _fn();
_restore.call(this);
if(typeof(value[id]) == 'function')
return value[id].apply(value, args);
else
throw new TypeError();
};
};
}
else
{
this.Evaluator = function(_fn)
{
this.valueOf = this.toString = function()
{
var value = _fn();
this.valueOf = this.toString = function()
{
return value;
};
return value;
};
};
}
`
Lazy evaluations of object's property (easy case for ES5- engines), but it does not relative for current proposal
` (function($G) { var Class = function() {
};
Object.defineProperty(
Class.prototype,
'a',
{
get: function()
{
console.log('hard evalutions...');
var value = 1;
Object.getPrototypeOf(this).a = value;
}
}
);
var a = new Class();
var b = a.a + 1;
// ...
var c = 2*a.a;
})(this);
`
Also it possible to implement Evaluator using Proxy api, but it currently does not give ability to evaluate value on instanceof, typeof, in, get [[Class]], ===, !== operators.
When new instance of Evaluator is created it just function with special mark [[Evaluator]] or [[Class]] == 'Evaluator'. It is passed as ordinary function - by reference. But when some user code try to use some operator or [[Call]] or cast(see above) it becomes evaluated value.