Czy można dodać dynamicznie nazwane właściwości do obiektu JavaScript?

745

W JavaScript utworzyłem taki obiekt:

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

Czy można dodać kolejne właściwości do tego obiektu po jego początkowym utworzeniu, jeśli nazwa właściwości nie zostanie określona do czasu uruchomienia? to znaczy

var propName = 'Property' + someUserInput
//imagine someUserInput was 'Z', how can I now add a 'PropertyZ' property to 
//my object?
Lee D.
źródło

Odpowiedzi:

1211

Tak.

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

data["PropertyD"] = 4;

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

Georg Schölly
źródło
142
@thedz: data.PropertyD musi znać nazwę właściwości, która nie jest wystarczająco dynamiczna.
Georg Schölly,
6
+1, ponieważ pomogło mi to. Ale nie rozumiem, dlaczego właściwości obiektu są traktowane jak tablica.
Ron van der Heijden
9
@Bondye: To część dziwnego projektu javascript. W tym przypadku object["property"]nie jest dokładnie taki sam, jak array[4]ten pierwszy nie został stworzony jako prawdziwa tablica.
Georg Schölly
5
Czy to tylko ja, czy ten post nie odpowiada na pytanie, otrzymując 195 głosów pozytywnych? Myślałem, że pyta, jak zdefiniować właściwości, w których nazwa jest nieznana, dopóki nie zostanie wygenerowana w kodzie JavaScript.
Qantas 94 Heavy
20
@Qantas: Powiedzmy, że nie odpowiada bezpośrednio. Ale przejście od data["PropertyD"]do data[function_to_get_property_name()]wydaje się trywialne.
Georg Schölly
154

ES6 na zwycięstwo!

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

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

Jeśli się zalogujesz data, otrzymasz:

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

Wykorzystuje to nową składnię właściwości obliczonych i literały szablonów .

Mauricio Soares
źródło
1
Tego właśnie potrzebowałem w przypadku, gdy chciałem, aby mój kod był w pełni funkcjonalny (jak w: nie mówi imperatywnych instrukcji obj[propname]). Zamiast tego mogłem użyć tego ze składnią rozproszenia obiektów.
intcreator
2
Nie jest od razu oczywiste, co ten fragment kodu robi komuś, kto nie widział ani nie zrozumiał nowej składni. Sugerowałbym edycję, aby wyświetlić wyjściowe / oczekiwane poprawki ze stałą „a”.
Osobiście uważam, że sposób ES5 jest o wiele czystszy i łatwiejszy do zrozumienia:var a = {}; a["dynamic-" + prop] = true;
Jack Giffin
1
@JackGiffin w niektórych przypadkach tak, ale przy pracy z niezmiennymi strukturami ta składnia może być bardzo przydatna, ponieważ pokazane podejście jest mutujące a. (Szczególnie przy użyciu pakietów takich jak redux)
Mauricio Soares,
3
To jest po prostu świetny {... stan, [prop]: val}
Xipo
87

Tak to mozliwe. Zarozumiały:

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

Zarówno:

data[propertyName] = propertyValue;

lub

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

Pierwsza metoda jest preferowana. eval () ma oczywiste obawy dotyczące bezpieczeństwa, jeśli używasz wartości dostarczonych przez użytkownika, więc nie używaj go, jeśli możesz tego uniknąć, ale warto wiedzieć, że istnieje i co może zrobić.

Możesz odwołać się do tego za pomocą:

alert(data.someProperty);

lub

data(data["someProperty"]);

lub

alert(data[propertyName]);
Cletus
źródło
40
Korzystanie z eval jest naprawdę niebezpieczne.
Georg Schölly,
10
Nie wspominając o powolności.
Eamon Nerbonne
@ GeorgSchölly True.
Obinna Nwakwue
1
Uwaga (na wypadek, gdyby ktoś napotkał ten sam problem, co ja): w przypadku normalnych obiektów działa to dobrze. Ale musiałem dodać jakąś właściwość do obiektu interfejsu użytkownika jQuery, aby śledzić listę elementów. W takim przypadku właściwość została utracona, jeśli została dodana w ten sposób, ponieważ jQuery zawsze tworzy kopię. Tutaj musisz użyć jQuery.extend () .
Matt
Lubię dodawać do mojego poprzedniego komentarza - nawet to nie działało w moim przypadku. Skończyło się więc $("#mySelector").data("propertyname", myvalue);na ustawieniu i var myValue=$("#mySelector").data("propertyname");odzyskaniu wartości. W ten sposób można dodawać nawet złożone obiekty (listy, tablice ...).
Matt
61

Wiem, że odpowiedź na to pytanie jest idealna, ale znalazłem też inny sposób na dodanie nowych właściwości i chciałem się z tobą podzielić:

Możesz użyć tej funkcji Object.defineProperty()

Znaleziono w Mozilla Developer Network

Przykład:

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
artgrohe
źródło
4
Plusy i minusy tej metody?
Trevor
6
@ Trevor: Całkowita konfigurowalność i możliwość dodawania programów pobierających i ustawiających; możliwość dodawania wielu właściwości jednocześnie za pomocą defineProperties(liczba mnoga).
rvighne,
@Thielicious Object.definePropertynie ma być łatwym narzędziem wygody, ale tym z precyzyjną kontrolą. Jeśli nie potrzebujesz tej dodatkowej kontroli, nie jest to właściwe narzędzie do wyboru.
kleinfreund
Object.defineProperty(obj, prop, valueDescriptor)jest znacznie wolniejszy i trudniejszy do zoptymalizowania dla V8 niż po prostu robienie obj[prop] = value;
Jack Giffin,
27

ES6 wprowadza obliczone nazwy właściwości, które pozwalają ci to zrobić

let a = 'key'
let myObj = {[a]: 10};
// output will be {key:10}
Zardzewiały
źródło
23

Tutaj, używając notacji:

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'
Maxim Sloyko
źródło
18

Możesz dodać dowolną liczbę dodatkowych właściwości, korzystając z notacji kropkowej:

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

lub :

data[newattribute] = somevalue

dla kluczy dynamicznych.

Gabriel Hurley
źródło
4
jeśli nazwa właściwości nie zostanie określona do czasu uruchomienia ”- aby nie działało, chyba że użyjesz eval, co nie jest dobrą opcją
Marc Gravell
1
lub użyj składni [] ... data [somevar] = somevalue
Gabriel Hurley
17

oprócz wszystkich poprzednich odpowiedzi, a jeśli zastanawiasz się, w jaki sposób będziemy pisać dynamiczne nazwy właściwości w przyszłości za pomocą obliczonych nazw właściwości (ECMAScript 6), oto jak to zrobić :

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

personIndex[ personId ]; // "John Doe"

odniesienie: Zrozumienie ECMAScript 6 - Nickolas Zakas

Anas Nakawa
źródło
11

Tylko dodatek do powyższej odpowiedzi Abeinga. Możesz zdefiniować funkcję, aby zawrzeć złożoność definicji właściwości, jak wspomniano poniżej.

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

odniesienie: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript

Sarvesh
źródło
9

Możesz dynamicznie dodawać właściwości za pomocą niektórych z poniższych opcji:

W twoim przykładzie:

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

Możesz zdefiniować właściwość o wartości dynamicznej na dwa następujące sposoby:

data.key = value;

lub

data['key'] = value;

Co więcej ... jeśli twój klucz jest również dynamiczny, możesz zdefiniować za pomocą klasy Object:

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

gdzie dane są twoim obiektem, klucz jest zmienną do przechowywania nazwy klucza, a wartość jest zmienną do przechowywania wartości.

Mam nadzieję, że to pomoże!

Fabricio
źródło
8

Wiem, że istnieje już kilka odpowiedzi na ten post, ale nie widziałem żadnej, w której istnieje wiele właściwości i są one w tablicy. Nawiasem mówiąc, to rozwiązanie jest dla ES6.

Na przykład załóżmy, że mamy tablicę o nazwie osoba z obiektami w środku:

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

Możesz więc dodać właściwość o odpowiedniej wartości. Powiedzmy, że chcemy dodać język z domyślną wartością EN .

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

Osoba tablica teraz stanie się to tak:

Person = [{id:1, Name: "John", Language:"EN"}, 
{id:2, Name: "Susan", Language:"EN"}, {id:3, Name: "Jet", Language:"EN"}]
Edper
źródło
W rzeczywistości nie dodajesz właściwości do obiektu, tworzysz nowy obiekt z właściwościami starego obiektu (poprzez operator rozkładania) i nowymi rekwizytami.
Ed Orsi,
3
Masz rację, że powinno być Person = Person.map(code here). Ale chodzi o to, że możesz łatwo dodać właściwość do istniejącego obiektu za pomocą ES6.
Edper,
5

Najprostszym i najbardziej przenośnym sposobem jest.

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

Na podstawie odpowiedzi #abeing!

Sydwell
źródło
3

Zachowaj ostrożność podczas dodawania właściwości do istniejącego obiektu za pomocą metody . (Kropka) .

(.dot) metoda dodawania właściwości do obiektu powinna być używana tylko wtedy, gdy znasz wcześniej „klucz”, w przeciwnym razie użyj metody [bracket] .

Przykład:

   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 }

Zwróć uwagę na problem na końcu dziennika konsoli - „klucz: 1999” zamiast właściwości 6: 6, właściwości 7: 7, ........., właściwości 1999: 1999 . Zatem najlepszym sposobem dodawania dynamicznie tworzonej właściwości jest metoda [bracket].

SridharKritha
źródło
1

Dobry sposób na dostęp z dynamicznych nazw ciągów zawierających obiekty (na przykład 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;
}

Przykład:

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

eval działa na wartość odczytu, ale wartość zapisu jest nieco trudniejsza.

Bardziej zaawansowana wersja (Utwórz podklasy, jeśli nie istnieją i zezwala na obiekty zamiast zmiennych globalnych)

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

Przykład:

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

Jest to to samo, co o.object.subobject.property

hamboy75
źródło
1
dokładnie to, czego szukałem, jest to przydatne do zareagowania this.setState ({dynamic property: value}) thankyou!
Kevin Danikowski,
0

Oto jak rozwiązałem problem.

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

Generuje następujący obiekt:

{
  "someouter": {
    "someinner": {
      "someValue": 123
    }
  },
  "simpleString": "string"
}
lewma
źródło
0

Może być przydatny, jeśli w środowisku wykonawczym dodano nowe mieszane właściwości:

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

Jednak operator rozprzestrzeniania używa płytkiej kopii, ale tutaj przypisujemy sobie dane, więc nic nie powinno stracić

Mohsen
źródło
-2

Idealny łatwy sposób

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

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

console.log(data);

Jeśli chcesz zastosować go do tablicy danych (wersja 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);
Umesh
źródło
-14

Zdecydowanie. Pomyśl o tym jak o słowniku lub tablicy asocjacyjnej. Możesz dodać do niego w dowolnym momencie.

thedz
źródło
1
Samo powiedzenie, że to nie pomoże.
Obinna Nwakwue