开发者

Design pattern for javascript when you don't know if a property will be a variable or a function

开发者 https://www.devze.com 2023-03-22 01:36 出处:网络
A lot of properties in my objects may be either a value or a function that returns a value. Accessing a value is different to calling a function (foo; vs foo();). I currently take the following approa

A lot of properties in my objects may be either a value or a function that returns a value. Accessing a value is different to calling a function (foo; vs foo();). I currently take the following approach. Can anyone recommend anything better?

var foo = {
    bar: function(){
        return 1;
    },
    // bar: 1, // bar may also be a standard value
    GetBar: function(){
        return $.isFunction(this.bar) ? this.bar() : this.bar;
    },
    Get: function(property){
        return $.isFunction(property) ? property() : property;
    }
}
foo.GetBar();//1
foo.Get(foo.bar);//1

I'm currently using the foo.GetBar(); approach, but because I then have to write a a new function for every property than van be either a value or a function, I am th开发者_如何学编程inking that it is best to go with the foo.Get(foo.bar); approach to keep it DRY. If you cannot recommend a better solution, I would still like to hear your feedback on both of the supplied methods.

Thanks.


I generally use a global utility in this case to turn every property that might be a function into one, then always call the function instead of accessing the property:

function functor(v) {
    return typeof v == "function" ? v : function() { return v; };
};

unknown = functor(unknown);
var myVar = unknown();

It looks a little silly in that example, but it's easy enough to incorporate it into an object like yours:

var foo = {
    bar: "some value or function",
    get: function(key) {
        return functor(this[key])();
    }
};

I usually use this approach for configuration variables that might be values or functions - in that case I call functor on the way in rather than the way out:

function Foo(height, width) {
    this.height = functor(height);
    this.width = functor(width);
}

// make a new object with a fixed height and a width dependent
// on the current window size
var foo = new Foo(10, function() { return $(window).width() - 100; } );
// now always assume functions
console.log(foo.height(), foo.width());


I don't know if this is an option for you -- you may want to consider Javascript getter/setter functions. For instance:

var foo = {
    get bar() {
        return 1;
    }
};

This way you can access bar the same way as you would any property, that is, by evaluating foo.bar.

Of course this could introduce some browser compatibility problems -- Javascript getters/setters are fairly new; they were added to the 5th edition of ECMA-262 in 2009.


What some libraries do in this case is make all access be by get and set methods rather than calling anything directly.

obj.get("bar") obj.set("bar", 4);

Then, in your implementation of those methods, you can check the underlying type and either return the value directly or call it's function. In the set method, if it's a function you can pass the arguments array to allow an arbitrary number of arguments on the setter if needed.

This also allows subclasses to override these methods if they want since you essentially have setters and getters now. Obviously, there are some sacrifices in the use of these since you give up the cleanliness of direct setting and retrieving of properties. But, you asked for design patterns so this is one.

The YUI library uses this method.

The advantage of this over native setters and getters is that this will work in any browser version (with the above drawbacks).

0

精彩评论

暂无评论...
验证码 换一张
取 消