有没有办法为Javascript数组中的惰性变量定义getter?

有没有办法为Javascript数组中的惰性变量定义getter?,javascript,arrays,lazy-evaluation,Javascript,Arrays,Lazy Evaluation,我正在尝试向数组中添加延迟计算的元素。这意味着在访问它们之前,不会计算或知道它们的值。这类似于对象的but 我最终为对象所做的是 Object.prototype.lazy = function(var_name, value_function) { this.__defineGetter__(var_name, function() { var saved_value = value_function(); this.__defineGetter__(var_name, f

我正在尝试向数组中添加延迟计算的元素。这意味着在访问它们之前,不会计算或知道它们的值。这类似于对象的but

我最终为对象所做的是

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_函数返回基本数据类型时工作

Array.prototype.lazy_push = function(value_function) {
  var a = this,
  i = this.length;
  this.push({
    toString: function() {
      return a[i] = value_function();
    }
  });
}

如果您尝试推送一个包含属性的对象,则在直接访问该对象之前,您将无法访问该属性。这不会发生在setter上,这就是为什么我想要Javascript的某种设置语法。现在我将使用伪数组,这对于我正在做的事情来说已经足够了。

没有。不幸的是,这是一件大事。

呜呜。在JavaScript中不能重载索引器操作符是一个很大的麻烦。哦,好吧。我们必须要有创意,想出一个不同的解决方案。这是一件好事(也很有趣)。:-)

勒尔,你解决问题的办法非常好。荣誉遇到真正理解JavaScript到那种程度的人是令人耳目一新的

读了这个问题后,我被一个史诗般的想法打动了,为了好玩,我写了一些代码。我对这个问题的解决方案与你的和以前做过的许多其他解决方案非常相似。但是,我觉得我想出了一些独特和非常整洁的东西,所以我想与大家分享

因此,我在CodePlex上主持了我的一个项目,在这个项目中,我使用了一种非常类似于jQuery的技术来定义属性(自包含的getter/setter函数),与您正在使用的非常类似。对于我提出的解决方案,我只是从我之前的代码中推断出来。下面是我对延迟加载数组索引的方法。从一开始

让我们考虑一个名为“PasiSeI化”的属性。以下是如何将该属性与我的技术结合使用:

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); };
property函数仅仅是对utily 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.
}
};
这样就可以处理属性。但是,为了提供一种访问数组类型的可能属性的索引值的方法,我进一步修改了以下代码:

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); };
阅读本文的每个人都可以在此处访问一组工作代码:

下面是测试代码的完整列表:

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));

好的,除非我误解了你的问题,我相信我已经找到了一个简单的解决方案,它不需要所谓的“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];
 }
希望如果我没有完全回答你的问题,你可以从这里找到答案

这不是一个真正的答案,但有几个要点

  • Javascript中没有真正的数组,数组只是一个扩展对象(JS中的一切都是这样)

  • 理想情况下,永远不要将原型函数添加到JS中的本机对象中,否则可能会意外覆盖现有属性,或者在后续过程中产生令人困惑的错误。例如,添加到对象的原型将添加到JS中的每个对象(即一切),您需要绝对确保您希望JS中的每个类型都具有该属性。这是非常危险的,因为如果您意外地覆盖了实际的Array()或Object()函数,您将破坏浏览器中的javascript,期间,刷新页面将无法修复它

  • 创建一个扩展它的新对象,而不是添加到正在修改的对象的原型中。例如,如果要扩展Array类:

    //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.....
    

  • 编写本机对象函数的所有访问器方法并不一定很有趣,但它保证了Javascript引擎的安全。

    我并不特别喜欢这个答案,但是您能将“变量”存储为表达式字符串,然后在需要时对其进行eval()吗?不理想,但它很紧凑

    var x = 10, arr = [];
    arr.push("x + 10");
    alert(eval(arr[0]));
    x = 20;
    alert(eval(arr[0]));
    
    我已经对它进行了测试,它很有效,即使它不是你想要的东西。

    在2019年,你可以使用一个

    下面是一个示例,该示例假定在访问函数时应计算函数的任何值

    函数LazyArray(){
    返回新的代理服务器([]{
    get:(对象、道具)=>{
    if(对象的类型[prop]=“功能”){
    //用结果替换函数
    obj[prop]=obj[prop]()
    }
    返回对象[prop]
    },
    })
    }
    常数a=LazyArray()
    a[0]=()=>{
    console.log('正在计算…')
    返回42
    }
    console.log(a[0])//已计算
    console.log(a[0])
    
    console.log(a.length)//访问其他属性
    是否将数组对象子类化视为创建“伪数组”?是的。即使您这样做了,一些数组语法(如for in)也不起作用。for..in不应用于迭代数组。这个问题似乎表明您想要在t中执行的操作可能不可能