views:

291

answers:

5
var obj = {}
obj.__setitem__ = function(key, value){
  this[key] = value * value
}
obj.x = 2  // 4
obj.y = 3  // 9

JavaScript doesn't have __setitem__ and this example obviously doesn't work.

In python __setitem__ works like:

class CustomDict(dict):
  def __setitem__(self, key, value):
    super(CustomDict, self).__setitem__(key, value * value)

d = CustomDict()
d['x'] = 2  # 4
d['y'] = 3  # 9

Is it possible to implement __setitem__ behavior in JavaScript? All tricky workarounds would be helpful.

+9  A: 

Is it possible to implement __setitem__ behavior in JavaScript?

No. There is no getter/setter for arbitrary properties in JavaScript.

In Firefox you can use JavaScript 1.5+'s getters and setters to define x and y properties that square their values on assignment, eg.:

var obj= {
    _x: 0,
    get x() { return this._x; },
    set x(v) { this._x=v*v; }
};
obj.x= 4;
alert(obj.x);

but you would have to declare a setter for each named property you wanted to use in advance. And it won't work cross-browser.

bobince
+1  A: 

I don't think you can override the [] operator in the current version of Javascript. In current Javascript, objects are largely just associative arrays, so the [] operator just adds a key/value pair to array that is the object.

You could write methods that set specific values or even squared a number and added the value as a key/value pair, but not by overloading the [] operator.

Javascript2 has some specifications for operator overloading, but that spec is MIA.

archetypal
+5  A: 

you can do this (as objects in javascript are also associative arrays):

var obj = {};
obj._ = function(key, value){
  this[key] = value * value;
}
obj._('x', 2);  // 4
obj._('y', 3);  // 9

alert(obj.x + "," + obj.y); //--> 4,9
najmeddine
Yes, I can. But Python's __setitem__ much more prettier.
NV
ok, I renamed it to `_`, may be more python-like ...
najmeddine
Oh, I see the irony. How can you implement obj.x+=2 or something like this?
NV
`obj._('x', obj.x + 2)`
najmeddine
+5  A: 

No, but there are plans for supporting a similar feature in JavaScript 2. The following object literal syntax has been suggested on Mozilla bug 312116 and it seems that it might be how it will be done for object literals:

({
  get * (property) {
    // handle property gets here
  }
})

I'm assuming set would also be supported (as set * (property, value) {...}).

Eli Grey
Looks nice! Could you post a link to the page there it was suggested?
NV
@NV: It's in some bug on bugzilla.mozilla.org. I couldn't find it with some simple searches so it may take you a while to find it.
Eli Grey
@NV: Found it: https://bugzilla.mozilla.org/show_bug.cgi?id=312116
Eli Grey
+3  A: 

There are no true setters and getters in the commonly implemented Javascript versions, so if you want to emulate the effect you have to use some different syntax. For a property obj.x, using obj.x() to access the value of the property and obj.x(123) to set the value seems like a rather convenient syntax.

It can be implemented like this:

// Basic property class
function Property(value) {
   this.setter(value);
}

Property.prototype.setter = function(value) {
   this.value = value * value;
}

Property.prototype.getter = function() {
   return this.value;
}

Property.prototype.access = function(value) {
   if (value !== undefined)
      this.setter(value);
   return this.getter();
}


// generator function to add convenient access syntax
function make_property(value) {
   var prop = new Property(value);
   function propaccess(value) {
      return prop.access(value);
   }
   return propaccess;
}

Now properties generated by make_property support the desired syntax and square values they are assigned:

var obj = {
   x: make_property(2)
};

alert(obj.x()); // 4
obj.x(3);       // set value
alert(obj.x()); // 9
sth