Как вычислить переменную в JavaScript, если и только если она используется? - PullRequest
9 голосов
/ 24 апреля 2010

Это то, чем я сейчас занимаюсь.

var foo = function() {
  var x = someComplicatedComputationThatMayTakeMoreTime();
  this.foo = function() { return x; };
  return x;
}

Это работает, но только если foo вызывается как функция, подобная

foo();

Но что, если я захочу вызвать его как нормальную переменную со значением? Я мог бы изменить код, чтобы он был

var foo = function() {
  var x = someComplicatedComputationThatMayTakeMoreTime();
  this.foo = x;
  return x;
}

Это позволило бы мне вызывать его только один раз как функцию, а затем как обычную переменную. Но это все еще не то, что я хочу. Кроме того, это усложняется, если его случайно снова вызывают как функцию, возвращающую ошибку.

Возможно ли это даже в JavaScript?

Кстати, это расширение Chrome / Firefox, поэтому совместимость с IE не имеет значения.

Завершено использованием toString, потому что получатели не позволяют мне переопределить весь атрибут, с ним должна быть связана функция. И toString имеет более чистый синтаксис.

Ответы [ 9 ]

7 голосов
/ 24 апреля 2010

Как насчет использования 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(); 
                    } 
                  } 
                })()
4 голосов
/ 24 апреля 2010

Если бы не существовал только Internet Explorer, вы могли бы использовать методы получения и установки, как описано Джоном Резигом в этой статье блога:

... Они позволяют вам привязывать специальные функции к объекту, которые выглядят как обычные свойства объекта, но вместо этого выполняют скрытые функции.

3 голосов
/ 24 апреля 2010

Использование функции - ваш лучший выбор на данный момент , однако новый стандарт JavaScript (ECMAScript 5th Ed.), Который внедряется сейчас всеми основными поставщиками браузеров, дает вам метод для создания свойств доступа , где вы можете определить свойство с помощью get и set функций, которые будут вызываться изнутри, не беспокоясь о том, что эти свойства следует рассматривать как функции, например:

var obj = {};
Object.defineProperty(obj, 'foo', {
  get: function () { // getter logic
    return 'foo!';
  },
  set: function (value) {
    // setter logic
  }
});

obj.foo; // "foo!", no function call

Этот новый стандарт будет реализован во всех браузерах (предварительная версия IE9 действительно разочаровала меня), и я бы не рекомендовал использовать его для производства, если у вас нет общего количества управление средой, в которой будет использоваться ваше приложение.

2 голосов
/ 24 апреля 2010

Это не практично в Интернете, потому что 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;
2 голосов
/ 24 апреля 2010

Я думаю, что вам нужна переменная с отложенной реализацией, которая может быть реализована следующим образом.

var myProperty = null;
function getMyProperty() {
    return (myProperty = myProperty ||  builder());
}
1 голос
/ 24 апреля 2010

Я бы порекомендовал вариант ответа ChaosPandion , но с закрытием.

var myProperty = (function () {
  var innerProperty = null;
  return function() {
    return (innerProperty = innerProperty ||  someComplicatedComputationThatMayTakeMoreTime());
  };
})();

и затем используйте myProperty() каждый раз, когда вам нужно получить доступ к переменной.

0 голосов
/ 16 ноября 2016

Вы можете использовать javascript Proxy class для создания такой функциональности.

var object = {};
var handler = {

    resolvers: {},

    get ( target, property, proxy ) {

        if ( ! target.hasOwnProperty( property ) && this.resolvers.hasOwnProperty( property ) ) {
            // execute the getter for the property;
            target[ property ] = this.resolvers[ property ]();
        }
        return target[ property ];

    },

    set ( target, property, value, receiver ) {

        // if the value is function set as a resolver
        if ( typeof value === 'function' ) {
            this.resolvers[property] = value;
        // otherwise set value to target
        } else {
            target.property = value;
        }
    },

    has ( target, property, receiver ) {
        //true when proxy handler has either a resolver or target has a value;
        return this.resolvers.hasOwnProperty( property ) || target.hasOwnProperty( property );
    }

};

var lazyObject = new Proxy( object, handler );

Теперь вы можете использовать его так:

'exampleField' in lazyObject; //returns false
lazyObject.exampleField = function(){ return 'my value' }; // add a resolver function
'exampleField' in lazyObject; //returns true
lazyObject.exampleField; //executes your resolver function and returns 'my value'

Этот пример демонстрирует работу. Вы можете изменить после ваших потребностей.

Вот скрипка с демонстрацией

0 голосов
/ 26 апреля 2010

Я бы использовал явную ленивую оценку. Вот моя реализация этого на основе взятия Схемы:

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
}
0 голосов
/ 24 апреля 2010

Вы можете определить получатель JavaScript. Из Руководства Apple по кодированию JavaScript :

myObject.__defineGetter__( "myGetter", function() { return this.myVariable; } );
var someVariable = myObject.myGetter;

См. Сообщение Джона Ресига, Методы получения и установки JavaScript и страницу Определение методов получения и установки в Центре разработчиков Mozilla для получения дополнительной информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...