Imagine the following code:
$.get( "ajax/getColorData.php", function( data ) {
this.colorData = data;
});
now envision that the value of 'data' is:
this.colorData = [
{
colorName: 'Red',
colorIsInRainbow:true
},
{
colorName: 'Orange',
colorIsInRainbow: true
},
{
colorName: 'Magenta',
colorIsInRainbow: false
}
];
Question 1
Now, after I download the data, let's say that I want to add a method, "colorStartsWithR" to every entry in the array. I "think" that rather than define it on every single member of the array, I could somehow define this method on the prototype. But, I'm not sure that I can do that because these objects were not created by me, but were returned by $.get, so it's not clear to me if I'm thinking in the right direction.
Question 2
And to take it a bit further, what if I wanted to add a property to every member of the array, specifically:
{
colorName: 'Magenta',
colorIsInRainbow: false,
userClickedThisColor:ko.observable()
}
This way, I can bind (via knockoutjs) the list and include a checkbox. In this case, I question whether or not the prototype would come in handy because each member should get its own ko.observable property. A quick instinct is to do something like:
for (var i=0;i<this.colorData.length;i++)
{
this.colorData[i].userClickedThisColor=ko.observable()
}
and that works fine, but imagine that there is more than one way to retrieve a list of colors, or that a user can create new color instances, and that the customization I did above is more complicated. I would now need to duplicate the above "add-on" logic. Is there a smarter way?
Having spent most of my time in strongly-typed languages, sometimes these things aren't as obvious to me. Thanks for any suggestions...
-Ben
Well, those objects don't have a special prototype, so you can't add members to it. I think these are the alternatives:
1) Add the members to each instance (I know you don't like it, but it's an option). I'd go for this option
2) Just create a method, and pass each object as a parameter or as this
with fn.call()
3) Create a "class" with the added members, then create instances of it passing the original object in the constructor. Not ideal, but maybe you have to do it.
function Color(plainObj){
this.colorName: plainObj.colorName;
this.colorIsInRainbow: plainObj.colorIsInRainbow;
//if you want all properties to be copied dynamically, uncomment the following:
//for(var key in plainObj) this[key] = plainObj[key];
this.userClickedThisColor = ko.observable()
...
}
Color.prototype.colorStartsWithR = function() {
return this.colorName.charAt(0) == "R";
};
//etc
Then, to apply it
for(var i=0; i<colorData.length; i++){
colorData[i] = new Color(colorData[i]); //Overwrites original colorData array
}
Hope this helps. Cheers
One option is to create a wrapper object and add the new method to the wrappers prototype:
var colorData = [
{
colorName: 'Red',
colorIsInRainbow:true
},
{
colorName: 'Orange',
colorIsInRainbow: true
},
{
colorName: 'Magenta',
colorIsInRainbow: false
}
];
function ColorDataWrapper(colorData){
this.colorData = colorData;
}
ColorDataWrapper.prototype.colorStartsWithR = function(){
return this.colorData.colorName.substring(0,1) == "R";
};
for(var i = 0; i < colorData.length; i++){
colorData[i] = new ColorDataWrapper(colorData[i]);
}
alert(colorData[0].colorStartsWithR());
alert(colorData[1].colorStartsWithR());
JS Fiddle: http://jsfiddle.net/ywrK6/1/
Another option is to just create a function that accepts the object as an argument:
function colorStartsWithR(obj){
return obj.colorData.colorName.substring(0,1) == "R";
}
The objects in your array are plain objects of the type Object
. To add methods to the prototype of those objects, you'd have to add the methods to the Object
type like this:
Object.prototype.colorStartsWithR = function() {
return this.colorName.substr(0,1) == "R";
}
That would work in this case, but it is really not recommended. The better solutions are to either change the objects in the array to be a different type of object that has it's own prototype to which you can add colorStartsWithR()
to OR just use a utility function to test a name to see if starts with "R"`.
Here are examples:
function colorObj(color, inRainbow) {
this.colorName = color;
this.colorIsInRainbow = inRainbow;
}
colorObj.prototype.colorStartsWithR = function() {
return this.colorName.substr(0,1) == "R";
}
this.colorData = [
new colorObj('Red', true),
new colorObj('Orange, true),
new colorObj('Magenta, false)
};
Then, you could do:
this.colorData[1].colorStartsWithR();
Or, just a utility function:
var colorData = [
{
colorName: 'Red',
colorIsInRainbow:true
},
{
colorName: 'Orange',
colorIsInRainbow: true
},
{
colorName: 'Magenta',
colorIsInRainbow: false
}
];
function startsWithR(str) {
return str.substr(0,1) == "R";
}
startsWithR(colorData[1].colorName);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With