Это - то, что я делаю прямо сейчас.
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = function() { return x; };
return x;
}
Это работает, но только если нечто называют как функция как так
foo();
Но что, если я хочу назвать его как нормальную переменную со значением? Я мог изменить код, чтобы быть
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = x;
return x;
}
Это позволило бы мне только называть его однажды как функцию и после этого как регулярная переменная. Но это все еще не, что я хочу. Плюс он является сложным, если это случайно называют как функция снова, возвращая ошибку.
Это даже возможно в JavaScript?
BTW, это для расширения Chrome/Firefox, таким образом, совместимость IE не имеет значения.
Законченное использование toString, потому что методы считывания не позволяют мне переопределять целый атрибут, функция, должно быть связано с ним. И toString имеет более чистый синтаксис.
Как насчет использования toString?
var foo = function() {
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
}
Подробнее о преобразованиях объектов в примитивы в JavaScript
РЕДАКТИРОВАТЬ на основе комментариев. Используйте синглтон (я думаю, он называется):
myObject.prop = (function(){
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
})()
Если бы не существовало только Internet Explorer, вы могли бы использовать геттеры и сеттеры, как описано Джоном Ресигом в этой статье блога:
... Они позволяют вам связывать специальные функции с объектом, которые выглядят как обычные свойства объекта, но вместо этого выполняют скрытые функции.
Я бы порекомендовал вариант ответа ChaosPandion , но с закрытием.
var myProperty = (function () {
var innerProperty = null;
return function() {
return (innerProperty = innerProperty || someComplicatedComputationThatMayTakeMoreTime());
};
})();
, а затем используйте myProperty ()
каждый раз, когда вам нужно получить доступ к переменной.
Это непрактично в Интернете, потому что IE не поддерживает его, но вы можете посмотреть https: // developer .mozilla.org / en / defineGetter , где приведены примеры того, как это сделать.
Есть несколько способов сделать это, вот один пример:
var data = {};
data.__defineGetter__("prop",
(function () {
var value = null;
return function () {
if (null == value) {
value = getYourValueHere();
}
return value;
};
})());
и теперь вы можете использовать это как:
var a = data.prop;
var b = data.prop;
Я думаю, вам нужна переменная с отложенным созданием экземпляра, которую можно реализовать следующим образом.
var myProperty = null;
function getMyProperty() {
return (myProperty = myProperty || builder());
}
Использование функции - ваш лучший вариант на данный момент , однако новый стандарт JavaScript (ECMAScript 5th Ed.), Который сейчас , реализованный сейчас всеми основными поставщиками браузеров, дает вам метод для создания свойств доступа , где вы можете определить свойство с помощью get
и набора
функции, которые будут вызываться изнутри, не беспокоясь о том, чтобы рассматривать эти свойства как функции, например:
var obj = {};
Object.defineProperty(obj, 'foo', {
get: function () { // getter logic
return 'foo!';
},
set: function (value) {
// setter logic
}
});
obj.foo; // "foo!", no function call
Реализация этого нового стандарта для всех браузеров займет некоторое время (предварительная версия IE9 действительно меня разочаровала) , и я бы не рекомендовал вам использовать его в производственной среде, если у вас нет полного контроля над средой, в которой будет использоваться ваше приложение.
Вы можете определить геттер JavaScript. Из Apple JavaScript Coding Guidelines :
myObject.__defineGetter__( "myGetter", function() { return this.myVariable; } );
var someVariable = myObject.myGetter;
См. Сообщение Джона Ресига, Геттеры и сеттеры JavaScript , а также страницу Определение геттеров и сеттеров на сайте Центр разработчиков Mozilla для получения дополнительной информации.
Я бы использовал явное ленивое вычисление. Вот моя реализация, основанная на дубле Scheme:
var delay, lazy, force, promise, promiseForced, promiseRunning;
(function () {
var getValue = function () {
return this.value;
};
var RUNNING = {};
var DelayThunk = function (nullaryFunc) {
this.value = nullaryFunc;
};
DelayThunk.prototype.toString = function () {
return "[object Promise]";
};
DelayThunk.prototype.force = function () {
if (promiseRunning (this)) {
throw new Error ("Circular forcing of a promise.");
}
var nullaryFunc = this.value;
this.value = RUNNING;
this.value = nullaryFunc ();
this.force = getValue;
return this.value;
};
var LazyThunk = function (nullaryFunc) {
DelayThunk.call (this, nullaryFunc);
};
LazyThunk.prototype = new DelayThunk (null);
LazyThunk.prototype.constructor = LazyThunk;
LazyThunk.prototype.force = function () {
var result = DelayThunk.prototype.force.call (this);
while (result instanceof LazyThunk) {
result = DelayThunk.prototype.force.call (result);
}
return force (result);
};
delay = function (nullaryFunc) {
return new DelayThunk (nullaryFunc);
};
lazy = function (nullaryFunc) {
return new LazyThunk (nullaryFunc);
};
force = function (expr) {
if (promise (expr)) {
return expr.force ();
}
return expr;
};
promise = function (expr) {
return expr instanceof DelayThunk;
};
promiseForced = function (expr) {
return expr.force === getValue || !promise (expr);
};
promiseRunning = function (expr) {
return expr.value === RUNNING || !promise (expr);
};
}) ();
Пример синтаксиса:
var x = lazy (function () { return expression; });
var y = force (x);
var z = delay (function () { return expression; });
var w = force (z);
Значения примечания сохраняются после оценки, поэтому повторное форсирование не приведет к дополнительным вычислениям.
Пример использования:
function makeThunk (x, y, z) {
return lazy (function () {
// lots of work done here
});
}
var thunk = makeThunk (arg1, arg2, arg3);
if (condition) {
output (force (thunk));
output (force (thunk)); // no extra work done; no extra side effects either
}