我正在尝试将元素添加到延迟评估的数组中。这意味着在访问它们之前,不会计算或知道它们的值。这就像一个previous question I asked但对象。
我最终为对象做的是
Object.prototype.lazy = function(var_name, value_function) {
this.__defineGetter__(var_name, function() {
var saved_value = value_function();
this.__defineGetter__(var_name, function() {
return saved_value;
});
return saved_value;
});
}
lazy('exampleField', function() {
// the code that returns the value I want
});
但我还没有找到一种方法来为真正的阵列做这件事。数组没有这样的setter。您可以将函数推送到数组,但是您必须将其作为函数调用它来返回您真正想要的对象。我现在正在做的是创建了一个我将其视为数组的对象。
Object.prototype.lazy_push = function(value_function) {
if(!this.length)
this.length = 0;
this.lazy(this.length++, value_function);
}
所以我想知道的是,还有一种方法可以做到这一点,同时仍然在数组而不是假数组吗?
更新:以下函数仅在value_function返回基本数据类型时有效。
Array.prototype.lazy_push = function(value_function) {
var a = this,
i = this.length;
this.push({
toString: function() {
return a[i] = value_function();
}
});
}
如果您尝试推送一个包含属性的对象,则在直接访问该对象之前,您将无法访问这些属性。这不会发生在setter中,这就是为什么我想要一些Javascript的设置语法。现在我将使用假阵列,这对我正在做的事情已经足够了。
好吧,除非我误解了你的问题,我相信我找到了一个简单的解决方案,它不需要所谓的“lazy_push”功能。
按照我之前的答案中的方法,您创建一个MyArray类:
function MyArray(){
this.object = [];
}
MyArray.prototype.push = function(what){
this.object.push(what);
}
现在重要的部分是getter函数,我们将创建一个getIdx()函数来从数组中获取值。然后,该函数使用'typeof'运算符来确定返回的值是否为函数。如果是,则返回从函数返回的值,如果不返回原始值。
代码更有意义:
MyArray.prototype.getIdx = function(which){
if(typeof this.object[which] == 'function'){
alert("a function");
//NOTICE THE '()' AT THE END OF THE NEXT LINE!!!
return this.object[which]();
}
return this.object[which];
}
希望如果我没有完全回答你的问题,你可以从这里弄清楚。
<---------我的原帖------------->
不是一个真正的答案,而是一些重点。
//create the constructor,
//with an object variable that holds the object you wish to extend
function MyArray(){
this.object = [];
}
//create functions which reference the native functions of the object
MyArray.prototype.push = function(what){
this.object.push(what);
}
//Etc... Etc.... Etc.....
编写本机Object函数的所有访问器方法并不一定有趣,但它保证了Javascript引擎的安全。
那没有。不幸的是,这是一个大问题。
的Bleh。你不能在JavaScript中重载索引器操作符是一个主要的无赖。那好吧。我们必须要有创意并提出不同的解决方案。这是一件好事(也很有趣)。 :-)
LLer,你解决的解决方案是一个非常好的解决方案。荣誉。能够让那些真正了解JavaScript的人达到这个程度令人耳目一新。
在阅读完这个问题后,我被一个史诗般的想法所震撼,并为了它的乐趣而编写了一些代码。我对这个问题的解决方案非常类似于你和之前完成的许多其他问题。但是,我觉得我想出了一些独特且非常整洁的东西,所以我想分享它。
所以我在CodePlex上托管了我的一个项目,我使用一种非常jQuery-esque的技术来定义与你正在使用的属性非常类似的属性(自包含的getter / setter函数)。对于我提出的解决方案,我只是从我之前存在的代码中推断出来。这是我对延迟加载数组索引的方法。从头开始......
让我们考虑一个名为“PageSize”的属性。以下是该属性如何与我的技术一起使用:
var MyClass = function() { }; // MyClass constructor.
var instance = new MyClass();
instance.PageSize(5);
alert(instance.PageSize());
请注意,属性是单个函数,其中提供值作为第一个参数调用setter并省略参数调用getter。 “PageSize”属性将被定义为MyClass类的一部分,如下所示:
MyClass.prototype.PageSize = function(v) { return this.GetSetProperty("PageSize", v, myFunctionThatDoesLazyLoading); };
属性函数只是一个包含对utitily GetSetProperty方法的调用的包装器,该方法执行实际的获取和设置。这是GetSetProperty函数的片段:
Object.prototype.GetSetProperty = function(name, value, loadFunction) {
if (!this.Properties)
{
this.Properties = {};
}
if (value)
{
this.Properties[name] = value;
}
else
{
if (!this.Properties[name] && loadFunction)
{
this.Properties[name] = loadFunction();
}
return this.Properties[name]; // This will return "undefined" if property doesn't exist or the loadFunction wasn't provided.
}
};
这样处理属性。但是,为了提供一种访问Array类型的可能属性的索引值的方法,我将进一步修改此代码:
Object.prototype.GetSetProperty = function(name, value, loadFunction, index) {
if (!this.Properties)
{
this.Properties = {};
}
if (typeof index === "number" && this.Properties[name] && this.Properties[name].constructor == Array)
{
return this.GetSetArrayProperty(name, index, value, loadFunction);
}
else
{
value = index;
}
if (value)
{
this.Properties[name] = value;
}
else
{
if (!this.Properties[name] && loadFunction)
{
this.Properties[name] = loadFunction();
}
return this.Properties[name]; // This will return "undefined" if property doesn't exist or the loadFunction wasn't provided.
}
};
Object.prototype.GetSetArrayProperty = function(name, index, value, loadFunction) {
if (value)
{
this.Properties[name][index] = value;
}
else
{
if (!this.Properties[name][index] && loadFunction)
{
this.Properties[name][index] = loadFunction();
}
return this.Properties[name][index];
}
};
原型声明需要像这样修改:
MyClass.prototype.PageSize = function(i, v) { return this.GetSetProperty("PageSize", v, myFunctionThatDoesLazyLoading, i); };
读这篇文章的每个人都可以在这里访问一组工作代码:http://jsbin.com/ajawe/edit
以下是包含测试的代码的完整列表:
Object.prototype.GetSetProperty = function(name, value, loadFunction, index) {
if (!this.Properties)
{
this.Properties = {};
}
if (typeof index === "number" && this.Properties[name] && this.Properties[name].constructor == Array)
{
return this.GetSetArrayProperty(name, index, value, loadFunction);
}
else
{
value = index;
}
if (value)
{
this.Properties[name] = value;
}
else
{
if (!this.Properties[name] && loadFunction)
{
this.Properties[name] = loadFunction();
}
return this.Properties[name]; // This will return "undefined" if property doesn't exist or the loadFunction wasn't provided.
}
};
Object.prototype.GetSetArrayProperty = function(name, index, value, loadFunction) {
if (value)
{
this.Properties[name][index] = value;
}
else
{
if (!this.Properties[name][index] && loadFunction)
{
this.Properties[name][index] = loadFunction();
}
return this.Properties[name][index];
}
};
// Here's a nifty function that declares the properties for you.
Function.prototype.CreateProperty = function(propertyName, loadFunction) {
eval("this.prototype['" + propertyName.toString() + "'] = function(i, v) { return this.GetSetProperty('" + propertyName.toString() + "', v, " + eval(loadFunction) + ", i); };");
};
var myFunctionThatDoesLazyLoading = function() {
return "Ahoy!";
};
var MyClass = function() { }; // MyClass constructor.
MyClass.prototype.PageSize = function(i, v) { return this.GetSetProperty("PageSize", v, myFunctionThatDoesLazyLoading, i); };
var instance = new MyClass();
alert(instance.PageSize()); // PageSize is lazy loaded.
instance.PageSize(5); // PageSize is re-assigned.
alert(instance.PageSize()); // Returns the new value.
instance.PageSize([1, 2, 3]); // PageSize is re-assigned to have an Array value.
alert(instance.PageSize(2)); // Returns the value at index 2 of the Array value.
instance.PageSize(2, "foo"); // Re-assigns the value at index 2.
alert(instance.PageSize(2)); // Returns the new value at index 2.
MyClass.CreateProperty("NewProp", function() { return ["a", "b", "c"]; }); // Demo of the CreateProperty function.
alert(instance.NewProp());
alert(instance.NewProp(1));
我不是特别喜欢这个答案,但是你可以将你的“变量”存储为表达式字符串,然后在需要时将它存储为eval()吗?不理想,但它很紧凑......
var x = 10, arr = [];
arr.push("x + 10");
alert(eval(arr[0]));
x = 20;
alert(eval(arr[0]));
我已经对它进行了测试,但它确实有效,即使它并不是你想要的。
在2019年,你可以使用Proxy。
下面是一个示例,假设在访问时应评估任何函数值。
function LazyArray() {
return new Proxy([], {
get: (obj, prop) => {
if (typeof obj[prop] === 'function') {
// replace the function with the result
obj[prop] = obj[prop]()
}
return obj[prop]
},
})
}
const a = LazyArray()
a[0] = () => {
console.log('calculating...')
return 42
}
console.log(a[0]) // lazy evaluated
console.log(a[0])
console.log(a.length) // accessing other properties