11

Getters and setters are a beauty in VB.Net:

Get
    Return width
End Get
Set(ByVal value As Integer)
    width = value
End Set

In Javascript, this is probably what we would do:

function Test() {
    var width = 100;
    this.__defineGetter__("Width", function() {
        return width;
    });
    this.__defineSetter__("Width", function(value){
        width = value;
    });
}

It looks like a plate of spaghetti ransacked by a kuri. What are some neater alternatives we have?

Note: The new code should access the value using new Test().Width and not new Test().Width().

Pacerier
  • 86,231
  • 106
  • 366
  • 634

3 Answers3

11

With ES5 you'll be able to do:

function Test() {
  var a = 1;

  return {
    get A() { return a; },
    set A(v) { a = v; }
  };
}

The getter/setter functions can of course do anything you want them to.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    This hogs up memory as the getter and setter is "unique" per each object. Also, you can't really do anything with the prototype when returning an expression in the constructor. – Tyler Crompton Jan 03 '12 at 10:19
  • @TylerCrompton yes that's definitely true; I was just following the outline of the original question. It'd be preferable to have the getters/setters on the prototype when possible. – Pointy Jan 03 '12 at 19:50
  • 3
    A nice overview of the different getters & setters (including a live benchmark) can be found here: http://jsperf.com/object-defineproperty-vs-definegetter-vs-normal – Bob Fanger Apr 05 '12 at 20:45
  • I'm pretty sure that what you describe is a firefox only extension. ecmascript5 syntax is a bit different and uses Object methods. – kybernetikos Aug 02 '12 at 08:20
  • 1
    @kybernetikos I'm not so sure about that, this syntax works in Node.JS using V8. – Cory Gross Jul 05 '13 at 20:05
  • You're right, I had confused it with a different getter/setter syntax. – kybernetikos Jul 10 '13 at 23:44
9

Here's a clean(er) alternative (also for older script engines):

function Test() {
  var a=1;
  return { A: { toString: function(){return a;} } };
}

alert(Test().A); //=> 1

Mind you, you can't use it to define private/static complex structures. You can only 'get' strings or numbers (so immutable variables) with this pattern. Maybe the pattern can be enhanced using json.

[edit] Using json, you can also create a getter this way for objects:

function Test() {
  var a=1,
  b = {foo:50, bar:100};

  return { 
          A: { toString: function(){return a;} } 
          foobar: { toString: function(){return JSON.stringify(b);}  } 
         };
}
var foobar = JSON.parse(Test().foobar);
alert(foobar.foo); //=> 50
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • Why did you use `A: { toString: function(){return a;} }` and ` foobar: { toString: function(){return JSON.stringify(b);} } ` instead of simply `A : return a;` and `foobar : return JSON.stringify(b)` ? – Pacerier Jun 05 '14 at 20:54
  • @Pacerier he's relying on the default behaviour of a few functions (such as `alert`) that call `.toString()` automatically on any argument that isn't already a string. – Alnitak Jun 06 '14 at 08:34
  • @Alnitak, Yes, why not simply return `a`, instead of returning an object that returns `a` via toString? I mean what's the difference... Why not just simply return `a`? – Pacerier Jun 06 '14 at 16:32
6

In Ecmascript5, the 'clean' (and standards compliant) way of doing this is with defineProperty.

function Test() {
    var a = 1;
    Object.defineProperty(this, "A", {get : function() { 
            return a;
        },  
        enumerable : true});
}

This assumes that you just want to see how to define a getter. If all you want to do is make instances of Test immutable (a good thing to do where you can), you should use freeze for that:

function Test() {
    this.a = 1;
    Object.freeze(this);
}
kybernetikos
  • 8,281
  • 1
  • 46
  • 54
  • 1
    Doesn't this (just like the accepted answer) hog up memory? I'm quite new to js, but it seems to me that these getters/setters belong to the prototype. [this page](http://msdn.microsoft.com/en-us/library/dd548687(v=vs.94).aspx) suggests that saying `Object.defineProperty(Test.prototype, "A", {get : function() {...}})` after the `Test` function should be possible. Wouldn't that be "cleaner"? – Hans Feb 03 '14 at 08:53
  • Yes, you're absolutely right. I did it this way to be more like his example code in the question, but usually I would expect to put things on the prototype where possible, and I probably should have made that point. Of course, the other factor is that if you do this, you have to put the actual value on 'this', which means that you lose the information hiding aspect which is always a shame. – kybernetikos Feb 03 '14 at 10:39
  • @kybernetikos, So do you mean that Pointy's answer is not standards compliant? – Pacerier Jun 05 '14 at 20:53
  • I could be wrong, but as far as I know having the property on the prototype won't prevent an _instance_ of the object from acquiring its own property with the same name that will then obscure the property on the prototype. – Alnitak Jun 06 '14 at 08:36
  • If you have a property with a setter on the prototype, doing `child.propertyname = someValue` will result in the setter being called, not overwriting the propertyname. You are right to say however that you could still override the property with an Object.defineProperty on the child. – kybernetikos Jun 06 '14 at 22:42