Можно ли добавить динамически именованные свойства в объект JavaScript?


746

В JavaScript я создал объект примерно так:

var data = {
    'PropertyA': 1,
    'PropertyB': 2,
    'PropertyC': 3
};

Можно ли добавить дополнительные свойства к этому объекту после его первоначального создания, если имя свойства не определено до времени выполнения? т.е.

var propName = 'Property' + someUserInput
//imagine someUserInput was 'Z', how can I now add a 'PropertyZ' property to 
//my object?

Ответы:


1212

Да.

var data = {
    'PropertyA': 1,
    'PropertyB': 2,
    'PropertyC': 3
};

data["PropertyD"] = 4;

// dialog box with 4 in it
alert(data.PropertyD);
alert(data["PropertyD"]);


142
@thedz: data.PropertyD должен знать имя свойства, которое недостаточно динамично.
Георг Шолли

6
+1 потому что это помогло мне. Но я не понимаю, почему свойства объекта обрабатываются как массив.
Рон ван дер Хейден

9
@Bondye: Это часть странного дизайна javascript. В этом случае object["property"]это не совсем то же самое array[4], что первый не был создан как истинный массив.
Георг Шолли

5
Это только я или этот пост не отвечает на вопрос при получении 195 голосов? Я думал, что он спрашивает, как определить свойства, где имя неизвестно, пока оно не будет сгенерировано в коде JavaScript.
Qantas 94 Heavy

20
@Qantas: допустим, он не отвечает прямо. Но переход от data["PropertyD"]к data[function_to_get_property_name()]кажется тривиальным.
Георг Шолли

154

ES6 за победу!

const b = 'b';
const c = 'c';

const data = {
    a: true,
    [b]: true, // dynamic property
    [`interpolated-${c}`]: true, // dynamic property + interpolation
    [`${b}-${c}`]: true
}

Если вы войдете в систему, dataвы получите это:

{
  a: true,
  b: true,
  interpolated-c: true,
  b-c: true
}

Это использует новый синтаксис вычисляемого свойства и литералы шаблона .


1
Это то, что мне было нужно в том случае, когда я хотел, чтобы мой код был полностью функциональным (например, без обязательных утверждений obj[propname]). Вместо этого я смог использовать это с синтаксисом распространения объекта.
Создатель

2
Не сразу очевидно, что этот фрагмент кода делает с кем-то, кто не видел и не понимал новый синтаксис. Я бы предложил редактирование для отображения выходных / ожидаемых свойств с константой 'a'.

Лично я думаю, что путь ES5 намного чище и проще для понимания:var a = {}; a["dynamic-" + prop] = true;
Джек Гиффин

1
@JackGiffin в некоторых случаях да, но при работе с неизменяемыми структурами этот синтаксис может быть очень удобен, так как подход, который вы показали, мутирует a. (Особенно при использовании таких пакетов, как редукс)
Маурисио Соареш

3
Это просто великолепно {... state, [prop]: val}
Xipo

87

Да, это возможно. Предполагая, что:

var data = {
    'PropertyA': 1,
    'PropertyB': 2,
    'PropertyC': 3
};
var propertyName = "someProperty";
var propertyValue = "someValue";

Или:

data[propertyName] = propertyValue;

или

eval("data." + propertyName + " = '" + propertyValue + "'");

Первый метод является предпочтительным. eval () имеет очевидные проблемы безопасности, если вы используете значения, предоставленные пользователем, поэтому не используйте его, если вы можете избежать этого, но стоит знать, существует ли он и что он может делать.

Вы можете сослаться на это с помощью:

alert(data.someProperty);

или

data(data["someProperty"]);

или

alert(data[propertyName]);

40
Использование eval действительно опасно.
Георг Шолли

10
Не говоря уже о медленном.
Имон Нербонн

@ GeorgSchölly Правда.
Обинна Нваквью

1
Примечание (в случае, если кто-то сталкивается с той же проблемой, что и я): для обычных объектов это работает нормально. Но мне пришлось добавить какое-то свойство в объект пользовательского интерфейса jQuery, чтобы отслеживать список элементов. В этом случае свойство было потеряно при добавлении таким способом, потому что jQuery всегда создает копию. Здесь вам нужно использовать jQuery.extend () .
Мэтт

Я хотел бы добавить к моему предыдущему комментарию - даже это не сработало в моем случае. Поэтому я в конечном итоге использовал $("#mySelector").data("propertyname", myvalue);для установки и var myValue=$("#mySelector").data("propertyname");для возврата значения. Даже сложные объекты (списки, массивы ...) могут быть добавлены таким образом.
Мэтт

61

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

Вы можете использовать функцию Object.defineProperty()

Найдено в сети разработчиков Mozilla

Пример:

var o = {}; // Creates a new object

// Example of an object property added with defineProperty with a data property descriptor
Object.defineProperty(o, "a", {value : 37,
                               writable : true,
                               enumerable : true,
                               configurable : true});
// 'a' property exists in the o object and its value is 37

// Example of an object property added with defineProperty with an accessor property descriptor
var bValue;
Object.defineProperty(o, "b", {get : function(){ return bValue; },
                               set : function(newValue){ bValue = newValue; },
                               enumerable : true,
                               configurable : true});
o.b = 38;
// 'b' property exists in the o object and its value is 38
// The value of o.b is now always identical to bValue, unless o.b is redefined

// You cannot try to mix both :
Object.defineProperty(o, "conflict", { value: 0x9f91102, 
                                       get: function() { return 0xdeadbeef; } });
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors

4
Плюсы и минусы этого метода?
Тревор

6
@Trevor: полная конфигурируемость и возможность добавлять геттеры и сеттеры; также, возможность добавить несколько свойств одновременно defineProperties(множественное число).
rvighne

@Thielicious Object.definePropertyне предназначен для простого удобного инструмента, но с тонким контролем. Если вам не нужен этот дополнительный контроль, это не правильный инструмент для выбора.
kleinfreund

Object.defineProperty(obj, prop, valueDescriptor)гораздо медленнее и сложнее оптимизировать V8, чем просто делать obj[prop] = value;
Джек Гиффин

27

ES6 представляет вычисленные имена свойств, которые позволяют вам

let a = 'key'
let myObj = {[a]: 10};
// output will be {key:10}

23

Здесь, используя вашу запись:

var data = {
    'PropertyA': 1,
    'PropertyB': 2,
    'PropertyC': 3
};
var propName = 'Property' + someUserInput
//imagine someUserInput was 'Z', how can I now add a 'PropertyZ' property to 
//my object?
data[propName] = 'Some New Property value'

18

Вы можете добавить столько свойств, сколько захотите, просто используя точечную запись:

var data = {
    var1:'somevalue'
}
data.newAttribute = 'newvalue'

или :

data[newattribute] = somevalue

для динамических клавиш.


4
если имя свойства не определено до времени выполнения "- так что это не сработает, если вы не используете eval, что не является хорошим вариантом
Марк Гравелл

1
или используйте синтаксис [] ... data [somevar] = somevalue
Габриэль Херли

17

в дополнение ко всем предыдущим ответам, и в случае, если вам интересно, как мы будем писать динамические имена свойств в будущем с использованием вычисляемых имен свойств (ECMAScript 6), вот как:

var person = "John Doe";
var personId = "person_" + new Date().getTime();
var personIndex = {
    [ personId ]: person
//  ^ computed property name
};

personIndex[ personId ]; // "John Doe"

ссылка: Понимание ECMAScript 6 - Николас Закас


11

Просто дополнение к ответу Abeing выше. Вы можете определить функцию для инкапсуляции сложности defineProperty, как указано ниже.

var defineProp = function ( obj, key, value ){
  var config = {
    value: value,
    writable: true,
    enumerable: true,
    configurable: true
  };
  Object.defineProperty( obj, key, config );
};

//Call the method to add properties to any object
defineProp( data, "PropertyA",  1 );
defineProp( data, "PropertyB",  2 );
defineProp( data, "PropertyC",  3 );

ссылка: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript


9

Вы можете добавлять свойства динамически, используя некоторые параметры ниже:

В вашем примере:

var data = {
    'PropertyA': 1,
    'PropertyB': 2,
    'PropertyC': 3
};

Вы можете определить свойство с динамическим значением следующими двумя способами:

data.key = value;

или

data['key'] = value;

Более того, если ваш ключ также динамический, вы можете определить его с помощью класса Object с помощью:

Object.defineProperty(data, key, withValue(value));

где data - ваш объект, key - это переменная для хранения имени ключа, а value - это переменная для хранения значения.

Надеюсь, это поможет!


8

Я знаю, что на этот пост уже есть несколько ответов, но я не видел ни одного, в котором есть несколько свойств, и они находятся в массиве. И это решение, кстати, для ES6.

Для иллюстрации, скажем, у нас есть массив с именем person с объектами внутри:

 let Person = [{id:1, Name: "John"}, {id:2, Name: "Susan"}, {id:3, Name: "Jet"}]

Таким образом, вы можете добавить свойство с соответствующим значением. Допустим, мы хотим добавить язык со значением по умолчанию EN .

Person.map((obj)=>({...obj,['Language']:"EN"}))

Человек массив Теперь стал бы так:

Person = [{id:1, Name: "John", Language:"EN"}, 
{id:2, Name: "Susan", Language:"EN"}, {id:3, Name: "Jet", Language:"EN"}]

Вы на самом деле не добавляете свойства к объекту, вы создаете новый объект со свойствами старого объекта (с помощью оператора распространения) и новыми реквизитами.
Эд Орси

3
Вы правы в том, что так и должно было быть Person = Person.map(code here). Но дело в том, что вы можете легко добавить свойство к существующему объекту ES6.
Edper

5

Самый простой и самый портативный способ.

var varFieldName = "good";
var ob = {};
Object.defineProperty(ob, varFieldName , { value: "Fresh Value" });

На основании ответа #abeing!


3

Будьте осторожны при добавлении свойства к существующему объекту с помощью метода . (Точка) .

(.dot) метод добавления свойства к объекту следует использовать только , если вы знаете о «ключе» заранее в противном случае используйте [кронштейн] метод.

Пример:

   var data = {
        'Property1': 1
    };
    
    // Two methods of adding a new property [ key (Property4), value (4) ] to the
    // existing object (data)
    data['Property2'] = 2; // bracket method
    data.Property3 = 3;    // dot method
    console.log(data);     // { Property1: 1, Property2: 2, Property3: 3 }
    
    // But if 'key' of a property is unknown and will be found / calculated
    // dynamically then use only [bracket] method not a dot method    
    var key;
    for(var i = 4; i < 6; ++i) {
    	key = 'Property' + i;     // Key - dynamically calculated
    	data[key] = i; // CORRECT !!!!
    }
    console.log(data); 
    // { Property1: 1, Property2: 2, Property3: 3, Property4: 4, Property5: 5 }
    
    for(var i = 6; i < 2000; ++i) {
    	key = 'Property' + i; // Key - dynamically calculated
    	data.key = i;         // WRONG !!!!!
    }
    console.log(data); 
    // { Property1: 1, Property2: 2, Property3: 3, 
    //   Property4: 4, Property5: 5, key: 1999 }

Обратите внимание на проблему в конце журнала консоли: «ключ: 1999» вместо свойства 6: 6, свойства 7: 7, ........., свойство 1999: 1999 . Поэтому лучшим способом добавления динамически создаваемого свойства является метод [скобка].


1

Хороший способ получить доступ из динамических строковых имен, которые содержат объекты (например, object.subobject.property)

function ReadValue(varname)
{
    var v=varname.split(".");
    var o=window;
    if(!v.length)
        return undefined;
    for(var i=0;i<v.length-1;i++)
        o=o[v[i]];
    return o[v[v.length-1]];
}

function AssignValue(varname,value)
{
    var v=varname.split(".");
    var o=window;
    if(!v.length)
        return;
    for(var i=0;i<v.length-1;i++)
        o=o[v[i]];
    o[v[v.length-1]]=value;
}

Пример:

ReadValue("object.subobject.property");
WriteValue("object.subobject.property",5);

eval работает для чтения значения, но записать значение немного сложнее.

Более продвинутая версия (создайте подклассы, если они не существуют, и разрешите объекты вместо глобальных переменных)

function ReadValue(varname,o=window)
{
    if(typeof(varname)==="undefined" || typeof(o)==="undefined" || o===null)
        return undefined;
    var v=varname.split(".");
    if(!v.length)
        return undefined;
    for(var i=0;i<v.length-1;i++)
    {
        if(o[v[i]]===null || typeof(o[v[i]])==="undefined") 
            o[v[i]]={};
        o=o[v[i]];
    }
    if(typeof(o[v[v.length-1]])==="undefined")    
        return undefined;
    else    
        return o[v[v.length-1]];
}

function AssignValue(varname,value,o=window)
{
    if(typeof(varname)==="undefined" || typeof(o)==="undefined" || o===null)
        return;
    var v=varname.split(".");
    if(!v.length)
        return;
    for(var i=0;i<v.length-1;i++)
    {
        if(o[v[i]]===null || typeof(o[v[i]])==="undefined")
            o[v[i]]={};
        o=o[v[i]];
    }
    o[v[v.length-1]]=value;
}

Пример:

ReadValue("object.subobject.property",o);
WriteValue("object.subobject.property",5,o);

Это то же самое, что o.object.subobject.property


1
именно то, что я искал, это полезно для реагирования this.setState ({dynamic property: value}) спасибо!
Кевин Даниковски,

0

Вот как я решил проблему.

var obj = {

};
var field = "someouter.someinner.someValue";
var value = 123;

function _addField( obj, field, value )
{
    // split the field into tokens
    var tokens = field.split( '.' );

    // if there's more than one token, this field is an object
    if( tokens.length > 1 )
    {
        var subObj = tokens[0];

        // define the object
        if( obj[ subObj ] !== undefined ) obj[ subObj ] = {};

        // call addfield again on the embedded object
        var firstDot = field.indexOf( '.' );
        _addField( obj[ subObj ], field.substr( firstDot + 1 ), value );

    }
    else
    {
        // no embedded objects, just field assignment
        obj[ field ] = value;
    }
}

_addField( obj, field, value );
_addField(obj, 'simpleString', 'string');

console.log( JSON.stringify( obj, null, 2 ) );

Создает следующий объект:

{
  "someouter": {
    "someinner": {
      "someValue": 123
    }
  },
  "simpleString": "string"
}

0

Это может быть полезно, если смешанное новое свойство добавляется во время выполнения:

data = { ...data, newPropery: value}

Тем не менее, оператор распространения использует поверхностное копирование, но здесь мы присваиваем данные себе, поэтому ничего не должны терять


-2

Идеальный простой способ

var data = {
    'PropertyA': 1,
    'PropertyB': 2,
    'PropertyC': 3
};

var newProperty = 'getThisFromUser';
data[newProperty] = 4;

console.log(data);

Если вы хотите применить его к массиву данных (версия ES6 / TS)

const data = [
  { 'PropertyA': 1, 'PropertyB': 2, 'PropertyC': 3 },
  { 'PropertyA': 11, 'PropertyB': 22, 'PropertyC': 33 }
];

const newProperty = 'getThisFromUser';
data.map( (d) => d[newProperty] = 4 );

console.log(data);

-14

Определенно. Думайте об этом как словарь или ассоциативный массив. Вы можете добавить к нему в любой момент.


1
Просто сказать, что это не поможет.
Обинна Нваквью
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.