BLEH. Это большая проблема, вы не можете перегрузить оператор индексатора в JavaScript. Ну что ж. Нам просто нужно проявить творческий подход и придумать другое решение. Это хорошо (и весело). : -)
LLer, решение, с которым вы согласились, чертовски хорошее. Престижность. Приятно встретить людей, которые действительно понимают JavaScript до такой степени.
После прочтения этого вопроса я был поражен эпической идеей и написал какой-то код для удовольствия. Мое решение проблемы очень похоже на ваше и многие другие, которые были сделаны ранее. Но я чувствую, что придумал что-то уникальное и очень аккуратное, поэтому я хочу поделиться этим.
Поэтому я размещаю свой проект на CodePlex, где я использую очень jQuery-esque технику для определения свойств (автономных функций получения / установки), очень похожих на тот, который вы используете. Для решения, которое я придумала, я просто экстраполировал из этого моего ранее существующего кода. Вот мой подход к ленивой загрузке индексов массива. Начиная с самого начала ...
Давайте рассмотрим свойство с именем «PageSize». Вот как это свойство будет использоваться с моей техникой:
var MyClass = function() { }; // MyClass constructor.
var instance = new MyClass();
instance.PageSize(5);
alert(instance.PageSize());
Обратите внимание, что свойство является единственной функцией, где предоставление значения в качестве первого параметра вызывает установщик, а пропущенный параметр вызывает получатель. Свойство PageSize будет определено как часть класса MyClass следующим образом:
MyClass.prototype.PageSize = function(v) { return this.GetSetProperty("PageSize", v, myFunctionThatDoesLazyLoading); };
Функция свойства - это просто оболочка для вызова метода 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));