Javascript: добавить метод ко всем объектам в массиве

Представьте себе следующий код:

$.get( "ajax/getColorData.php", function( data ) {
  this.colorData = data;
});

Теперь предположим, что значение "данных":

this.colorData = [
    {
        colorName: 'Red',
        colorIsInRainbow:true
    },
    {
        colorName: 'Orange',
        colorIsInRainbow: true
    },
    {
        colorName: 'Magenta',
        colorIsInRainbow: false
    }
];

Вопрос 1

Теперь, после загрузки данных, допустим, что я хочу добавить метод colorStartsWithR к каждой записи в массиве. Я "думаю", что вместо того, чтобы определять его для каждого элемента массива, я мог бы как-то определить этот метод в прототипе. Но я не уверен, что смогу сделать это, потому что эти объекты не были созданы мной, а были возвращены $.get, поэтому мне не ясно, думаю ли я в правильном направлении.

вопрос 2

И, если пойти немного дальше, что если я захочу добавить свойство каждому члену массива, а именно:

    {
        colorName: 'Magenta',
        colorIsInRainbow: false,
        userClickedThisColor:ko.observable()
    }

Таким образом, я могу связать (через knockoutjs) список и включить флажок. В этом случае я спрашиваю, пригодится ли прототип, потому что каждый член должен получить свое собственное свойство ko.observable. Быстрый инстинкт - сделать что-то вроде:

for (var i=0;i<this.colorData.length;i++)
{
 this.colorData[i].userClickedThisColor=ko.observable()
}

и это прекрасно работает, но представьте, что существует несколько способов получения списка цветов или что пользователь может создавать новые экземпляры цветов, и что настройка, которую я сделал выше, более сложна. Теперь мне нужно продублировать вышеприведенную логику надстроек. Есть ли умнее?

Проведя большую часть своего времени на строго типизированных языках, иногда эти вещи не так очевидны для меня. Спасибо за любые предложения...

-Бен

3 ответа

Решение

Ну, у этих объектов нет специального прототипа, поэтому вы не можете добавлять к нему членов. Я думаю, что это альтернативы:

1) Добавьте участников в каждый экземпляр (я знаю, что вам это не нравится, но это вариант). Я бы пошел на этот вариант

2) Просто создайте метод и передайте каждый объект как параметр или как this с fn.call()

3) Создайте "класс" с добавленными членами, затем создайте его экземпляры, передав исходный объект в конструктор. Не идеально, но, возможно, вам придется это сделать.

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

Затем, чтобы применить его

for(var i=0; i<colorData.length; i++){
   colorData[i] = new Color(colorData[i]); //Overwrites original colorData array
}

Надеюсь это поможет. ура

Один из вариантов - создать объект оболочки и добавить новый метод в прототип оболочки:

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/

Другой вариант - просто создать функцию, которая принимает объект в качестве аргумента:

function colorStartsWithR(obj){
    return obj.colorData.colorName.substring(0,1) == "R";
}

Объекты в вашем массиве являются простыми объектами типа Object, Чтобы добавить методы к прототипу этих объектов, вам нужно добавить методы в Object типа как это:

Object.prototype.colorStartsWithR = function() {
    return this.colorName.substr(0,1) == "R";
}

Это будет работать в этом случае, но это действительно не рекомендуется. Лучшее решение состоит в том, чтобы либо изменить объекты в массиве на объект другого типа, имеющий собственный прототип, к которому можно добавить colorStartsWithR() ИЛИ просто используйте служебную функцию для проверки имени, чтобы увидеть, начинается ли с "R"`.

Вот примеры:

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)
};

Затем вы можете сделать:

this.colorData[1].colorStartsWithR();

Или просто служебная функция:

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);
Другие вопросы по тегам