Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript: Add method to all objects in array

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

like image 224
BenjiFB Avatar asked Jan 08 '14 01:01

BenjiFB


3 Answers

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

like image 193
Edgar Villegas Alvarado Avatar answered Nov 03 '22 00:11

Edgar Villegas Alvarado


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";
}
like image 40
Kevin Bowersox Avatar answered Nov 03 '22 01:11

Kevin Bowersox


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);
like image 1
jfriend00 Avatar answered Nov 03 '22 00:11

jfriend00