9

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
  • There's two problems with your use of this.foo: 1.) this refers to the top-level object (window) if called as foo(), and 2.) If you assign foo to a variable and call that variable many times, foo will be updated on every call because that variable isn't getting changed. Commented Apr 24, 2010 at 5:36
  • This was only an example, most of these are defined inside objects so this refers to the object. And I checked with a simple alert() function to see if it was getting called many times or just once and it's the value is only computed once. Commented Apr 24, 2010 at 5:46

9 Answers 9

7

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(); 
                    } 
                  } 
                })()
Sign up to request clarification or add additional context in comments.

6 Comments

Wow this works because toString is only called if the variable is used. Thanks.
If foo is accessed twice, then wouldn't the complicated calculation be done twice?
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.
This is what I ended up doing and it works. :D var 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.
@LLer: see the edited answer, but maybe you mean something else?
|
4

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.

1 Comment

Apparently I discover that they are partially supported in IE8, but using different syntax: robertnyman.com/2009/05/28/…. IE8 is using the Object.defineProperty method, which should become the ECMAScript standard.
3

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.

3 Comments

Yes, it's the ES5 standard syntax, from the TC39 committee
I know, I just think the syntax is absolutely horrible.
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...
2

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

4 Comments

I want to be able to use myProperty variable without having to worry about calling getMyProperty().
That's effectively what he has now (although I like yours better).
@LLer - Unfortunately, JavaScript does not support this. Why don't you try Haskell? :)
After experimenting with an edit of this, I've found something that I think works.
2

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;

2 Comments

Fortunately this is for a Chrome/Firefox extension, so IE does not matter. :)
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.
1

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.

1 Comment

Now wouldn't it be nice if you could just do lazy myProperty = builder();?
0

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.

Comments

0

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
}

1 Comment

The usage looks promising. Thanks. This is interesting, I'll try to understand it in a bit.
0

You can use the javascript Proxy class for creating such functionality.

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

Now you can use it like this:

'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'

This example is to demonstrate the working. You can change after your needs.

Here is a fiddle with a demonstration

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.