The prototype is only for object instances, which you don't have unfortunately. If you had a constructor like this:
function Color(r,g,b,a){
for (var i=0; i<4; i++)
this[i] = arguments[i];
}
it would work with
Object.defineProperty(Color.prototype, "r", {
get: function() { return this[0]; } // setter analogous
};
However, these Color instances are no arrays. You could give them a length property and let them inherit from Array.prototype, but they won't really be arrays. @Trouts solution is a bit like that, and I'd say it is okay because colors really are no arrays (you can't push a fifth value etc).
The alternative were to extend the array which you return from the color getter with those properties. You could do that every time someone accesses the value (like you currently do, you create a new array in the getter) but I'd suggest that you should return the same instance, propagating any changes. Your current property definition is like "setColor" and "getCurrentColor".
So, actually you want two separate things: Color objects that have more than one property per value (i.e 0 == r); and a setter for your global color variable which accepts arrays and sets the single values on the respective object.
// version with private values
function Color(r, g, b, a) {
// r, g, b and a are private-scoped variables
var desc = {
"0": {
get:function(){return r;},
set:function(val){ if(+val<256&&val>=0) r=+val;}
},
…
}
// use those property descriptors multiple times
desc.r = desc[0];
…
Object.defineProperties(this, desc);
}
// or version with public and unlimited read/write access to the properties:
function Color(r,g,b,a){
for (var i=0; i<4; i++)
this[i] = arguments[i];
}
Object.defineProperties(Color.prototype, {
r: { get:function(){return this[0];}, set:function(r){this[0]=r;} },
…
}
// and for both versions we can add array-like methods on the prototype
var cprot = Color.prototype, aprot = Array.prototype;
Object.defineProperty(cprot, "length", {value:4});
// we only need accessor functions here, nothing which changes the array [length]
cprot.map = aprot.map;
cprot.reduce = aprot.reduce;
cprot.slice = aprot.slice;
cprot.join = aprot.join;
// you might want to add other utilities like
cprot.toString = function() {
return "rgba("+this.join(",")+")"; // using array method from above
};
cprot.getHex = function() {
function hex(n) { return (n<16?"0":"") + n.toString(16); }
return "#"+this.slice(0, 3).map(hex).join("");
};
And then, your color value setter:
function defineColorProperty(obj, prop, color) {
// again, color is private-scoped
if (!color || !(color instanceof Color)) color = new Color(0, 0, 0, 0);
Object.defineProperty(obj, prop, {
get: function(){ return color; }, // a cool Color instance!
set: function(val) {
if (Object(val)!==val) return; // accept objects (including arrays)
for (var i=0; i<4 && i<val.length; i++)
color[i] = val[i];
},
enumberable: true
});
return color;
}
// usage:
> defineColorProperty(window, "color");
Object[Color]: 0, 0, 0, 0
> color = [255, 0, 120, 1];
> color.r = 42;
> color[0]
42
> color = [0, 0];
> ""+color
"rgba(0,0,120,1)"
> var x = color;
> x.b = 255;
> x.getHex()
"#0000FF"
prototypeused for defining methods given to new instances of a class, and not for spiking methods into existing objects?