views:

185

answers:

8

This is what I'm doing right now.

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

It works but only if foo is called as a function like so

foo();

But what if I want to call it as a normal variable with a value? I could modify the code to be

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

That would allow me to only call it once as a function and after that as a regular variable. But it's still not what I want. Plus it gets complicated if it accidentally gets called as a function again, returning an error.

Is this even possible in JavaScript?

BTW, this is for a Chrome/Firefox extension, so IE compatibility does not matter.

Ended up using toString because getters don't allow me to redefine the whole attribute, a function must be associated with it. And toString has cleaner syntax.

+2  A: 

What I think you want is a lazily instantiated variable, which can be implemented like this.

var myProperty = null;
function getMyProperty() {
    return (myProperty = myProperty ||  builder());
}
ChaosPandion
I want to be able to use myProperty variable without having to worry about calling getMyProperty().
this is a dead end
That's effectively what he has now (although I like yours better).
Gabe
@LLer - Unfortunately, JavaScript does not support this. Why don't you try Haskell? :)
ChaosPandion
After experimenting with an edit of this, I've found something that I think works.
this is a dead end
Nevermind lol..
this is a dead end
+4  A: 

If only Internet Explorer didn't exist, you could use getters and setters as described by John Resig in this blog article:

... They allow you to bind special functions to an object that look like normal object properties, but actually execute hidden functions instead.

Daniel Vassallo
That is pretty cool. IE just loves to please huh?
ChaosPandion
Apparently I discover that they are partially supported in IE8, but using different syntax: http://robertnyman.com/2009/05/28/getters-and-setters-with-javascript-code-samples-and-demos/. IE8 is using the `Object.defineProperty` method, which should become the ECMAScript standard.
Daniel Vassallo
+2  A: 

This is not practical on the web because IE does not support it, but you can look at https://developer.mozilla.org/en/defineGetter for examples how to do this.

There are a couple ways to do it, here is one example:

var data = {};
data.__defineGetter__("prop",
                      (function () {
                           var value = null;
                           return function () {
                             if (null == value) {
                               value = getYourValueHere();
                             }
                             return value;
                           };
                        })());

and now you can use it like:

var a = data.prop;
var b = data.prop;
Joey Mazzarelli
Fortunately this is for a Chrome/Firefox extension, so IE does not matter. :)
this is a dead end
In that case it will work perfectly, I have used it in a Firefox extension too. Check out the docs, there is a friendlier syntax than the one I showed.
Joey Mazzarelli
+3  A: 

Using a function is your best option for now, however the new JavaScript standard (ECMAScript 5th Ed.) which is being implemented now by all major browser vendors, gives you a method to create accessor properties, where you can define a property with a get and set functions that will be internally called, without worrying to treat this properties as functions, e.g.:

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

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

This new standard will take some time to be implemented for all browsers, (the IE9 preview version really disappointed me), and I wouldn't recommend you to use it for production, unless you have total control on the environment where your application will be used.

CMS
The syntax looks like it was designed by a committee... Oh wait..
ChaosPandion
Yes, it's the ES5 standard syntax, from the [TC39 committee](http://www.ecma-international.org/memento/TC39.htm)
CMS
I know, I just think the syntax is absolutely horrible.
ChaosPandion
Yes it's kinda ugly, but this syntax was selected very carefully, as you can see no new grammar has been defined, the method receives a plain old object, introducing new grammar would have made the adoption of the new standard slower, giving more compatibility issues...
CMS
A: 

You could define a JavaScript getter. From the Apple JavaScript Coding Guidelines:

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

See John Resig's post, JavaScript Getters and Setters, and the Defining Getters and Setters page at the Mozilla Developer Centre for more information.

Steve Harrison
+1  A: 

I would recommend a variation on ChaosPandion's answer, but with a closure.

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

and then use myProperty() every time you need to access the variable.

cobbal
Now wouldn't it be nice if you could just do `lazy myProperty = builder();`?
ChaosPandion
+7  A: 

How about using toString?

var foo = function() {
  function someComplicatedComputationThatMayTakeMoreTime() {
        //your calculations
  }
  return {
      toString: function() { 
           return someComplicatedComputationThatMayTakeMoreTime(); 
      }
  }
}

More about Object-to-Primitive Conversions in JavaScript

EDIT based on comment. Use a singleton (I think it's called):

myObject.prop = (function(){ 
                  function someComplicatedComputationThatMayTakeMoreTime() {
                   //your calculations
                  }
                  return { 
                    toString: function() { 
                     return someComplicatedComputationThatMayTakeMoreTime(); 
                    } 
                  } 
                })()
KooiInc
Wow this works because toString is only called if the variable is used. Thanks.
this is a dead end
Excellent link. Bookmarked.
BradBrening
If `foo` is accessed twice, then wouldn't the complicated calculation be done twice?
Casey Hope
It would. If you don't want that, you should add an extra property (say: result) in the returned Object. Now add a check to toString: if the result property has no value, do the calculation (in toString), assign its result to the property and return it. If it has a value, return that value.
KooiInc
This is what I ended up doing and it works. :Dvar foo = { toString: function() { return foo = someComplicatedComputationThatMayTakeMoreTime(); ; }}The only problem with this right now is if I want to do this with an object's property, I can't access other properties without knowing the name of the object, I was previously using "this.otherproperty". Not that big of a deal, but I'm always looking to make things flexible so I'm using __defineGetter__() for those.
this is a dead end
@LLer: see the edited answer, but maybe you mean something else?
KooiInc
IMO, this is a very hairy way of going about things. This will only work in cases where the object is implicitly convereted into a string.
trinithis
A: 

I would use explicit lazy evaluation. Here's my implementation of it based on Scheme's take:

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

}) ();

Example Syntax:

var x = lazy (function () { return expression; });
var y = force (x);

var z = delay (function () { return expression; });
var w = force (z);

Note values are stored once evaluated, so repeated forcing will not do extra computations.

Example usage:

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
}
trinithis
The usage looks promising. Thanks. This is interesting, I'll try to understand it in a bit.
this is a dead end