Est-il possible d'ajouter des propriétés nommées dynamiquement à un objet JavaScript?

746

En JavaScript, j'ai créé un objet comme ceci:

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

Est-il possible d'ajouter d'autres propriétés à cet objet après sa création initiale si le nom des propriétés n'est pas déterminé avant l'exécution? c'est à dire

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

Réponses:

1212

Oui.

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
la source
142
@thedz: data.PropertyD a besoin de connaître le nom de la propriété, qui n'est pas assez dynamique.
Georg Schölly le
6
+1 parce que cela m'a aidé. Mais je ne comprends pas pourquoi les propriétés d'un objet sont gérées comme un tableau.
Ron van der Heijden
9
@Bondye: Cela fait partie de la conception étrange du javascript. Dans ce cas, ce object["property"]n'est pas exactement la même chose que array[4]le premier n'a pas été créé comme un vrai tableau.
Georg Schölly
5
Est-ce juste moi ou ce billet ne répond-il pas à la question tout en obtenant 195 votes positifs? Je pensais qu'il demandait comment définir des propriétés dont le nom est inconnu jusqu'à ce qu'il soit généré dans le code JavaScript.
Qantas 94 Heavy
20
@Qantas: Disons qu'il n'y répond pas directement. Mais passer de data["PropertyD"]à data[function_to_get_property_name()]semble insignifiant.
Georg Schölly
154

ES6 pour la victoire!

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

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

Si vous vous connectez data vous obtenez ceci:

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

Cela utilise la nouvelle syntaxe de propriété calculée et les nouveaux littéraux de modèle .

Mauricio Soares
la source
1
C'est ce dont j'avais besoin dans le cas où je voulais que mon code soit complètement fonctionnel (comme dans, aucune déclaration impérative ne le dit obj[propname]). Au lieu de cela, j'ai pu l'utiliser avec la syntaxe de propagation d'objet.
intcreator
2
Il n'est pas immédiatement évident ce que cet extrait de code fait à quelqu'un qui n'a pas vu ou compris la nouvelle syntaxe. Je suggérerais une modification pour afficher la sortie / les propriétés attendues avec la constante «a».
Personnellement, je pense que la méthode ES5 est beaucoup plus propre et plus facile à comprendre:var a = {}; a["dynamic-" + prop] = true;
Jack Giffin
1
@JackGiffin dans certains cas oui, mais lorsque vous travaillez avec des structures immuables, cette syntaxe peut être très pratique, car l'approche que vous avez montrée est en mutation a. (Surtout lors de l'utilisation de packages comme redux)
Mauricio Soares
3
C'est tout simplement génial {... état, [prop]: val}
Xipo
87

Oui c'est possible. En supposant:

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

Soit:

data[propertyName] = propertyValue;

ou

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

La première méthode est préférée. eval () a des problèmes de sécurité évidents si vous utilisez des valeurs fournies par l'utilisateur, alors ne l'utilisez pas si vous pouvez l'éviter mais cela vaut la peine de savoir qu'il existe et ce qu'il peut faire.

Vous pouvez référencer cela avec:

alert(data.someProperty);

ou

data(data["someProperty"]);

ou

alert(data[propertyName]);
cletus
la source
40
Utiliser eval est vraiment dangereux.
Georg Schölly
10
Sans parler de lenteur.
Eamon Nerbonne
@ GeorgSchölly True.
Obinna Nwakwue
1
Remarque (au cas où quelqu'un rencontrerait le même problème que moi): Pour les objets normaux, cela fonctionne bien. Mais j'ai dû ajouter une propriété à un objet jQuery UI pour garder une trace d'une liste d'éléments. Dans ce cas, la propriété était perdue si elle était ajoutée de cette façon car jQuery crée toujours une copie. Ici, vous devez utiliser jQuery.extend () .
Matt
J'aime ajouter à mon commentaire précédent - même cela n'a pas fonctionné dans mon cas. J'ai donc fini par utiliser le $("#mySelector").data("propertyname", myvalue);pour définir et var myValue=$("#mySelector").data("propertyname");récupérer la valeur. Même des objets complexes (listes, tableaux ...) peuvent être ajoutés de cette façon.
Matt
61

Je sais que la question est parfaitement répondue, mais j'ai également trouvé une autre façon d'ajouter de nouvelles propriétés et je voulais la partager avec vous:

Vous pouvez utiliser la fonction Object.defineProperty()

Trouvé sur Mozilla Developer Network

Exemple:

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
la source
4
Avantages et inconvénients de cette méthode?
Trevor
6
@Trevor: configurabilité totale et possibilité d'ajouter des getters et setters; aussi, possibilité d'ajouter plusieurs propriétés à la fois avec defineProperties(pluriel).
rvighne
@Thielicious Object.definePropertyn'est pas censé être l'outil de commodité facile, mais celui avec le contrôle à grain fin. Si vous n'avez pas besoin de ce contrôle supplémentaire, ce n'est pas le bon outil à choisir.
kleinfreund du
Object.defineProperty(obj, prop, valueDescriptor)est beaucoup plus lent et plus difficile à optimiser que V8 pour le V8 obj[prop] = value;
Jack Giffin
27

ES6 introduit des noms de propriété calculés, qui vous permettent de faire

let a = 'key'
let myObj = {[a]: 10};
// output will be {key:10}
Rouillé
la source
23

Ici, en utilisant votre notation:

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
la source
18

Vous pouvez ajouter autant de propriétés que vous le souhaitez simplement en utilisant la notation par points:

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

ou :

data[newattribute] = somevalue

pour les touches dynamiques.

Gabriel Hurley
la source
4
si le nom des propriétés n'est pas déterminé avant l'exécution "- donc cela ne fonctionnera pas à moins que vous n'utilisiez eval, ce qui n'est pas une bonne option
Marc Gravell
1
ou utilisez la syntaxe [] ... data [somevar] = somevalue
Gabriel Hurley
17

en plus de toutes les réponses précédentes, et au cas où vous vous demandez comment nous allons écrire des noms de propriétés dynamiques dans le futur en utilisant des noms de propriétés calculés (ECMAScript 6), voici comment:

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

personIndex[ personId ]; // "John Doe"

référence: Comprendre ECMAScript 6 - Nickolas Zakas

Anas Nakawa
la source
11

Juste un ajout à la réponse d'Aeing ci-dessus. Vous pouvez définir une fonction pour encapsuler la complexité de defineProperty comme mentionné ci-dessous.

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

référence: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript

Sarvesh
la source
9

Vous pouvez ajouter des propriétés dynamiquement en utilisant certaines des options ci-dessous:

Dans votre exemple:

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

Vous pouvez définir une propriété avec une valeur dynamique des deux manières suivantes:

data.key = value;

ou

data['key'] = value;

Encore plus ... si votre clé est également dynamique, vous pouvez définir en utilisant la classe Object avec:

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

où les données sont votre objet, la clé est la variable pour stocker le nom et la valeur de la clé est la variable pour stocker la valeur.

J'espère que ça aide!

Fabricio
la source
8

Je sais qu'il y a déjà plusieurs réponses à ce message, mais je n'en ai pas vu une dans laquelle il existe plusieurs propriétés et elles se trouvent dans un tableau. Et cette solution est d'ailleurs pour ES6.

Par exemple, disons que nous avons un tableau nommé personne avec des objets à l'intérieur:

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

Vous pouvez donc ajouter une propriété avec la valeur correspondante. Disons que nous voulons ajouter une langue avec une valeur par défaut EN .

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

le tableau Person deviendrait maintenant comme ceci:

Person = [{id:1, Name: "John", Language:"EN"}, 
{id:2, Name: "Susan", Language:"EN"}, {id:3, Name: "Jet", Language:"EN"}]
Edper
la source
Vous n'ajoutez pas réellement de propriétés à un objet, vous créez un nouvel objet avec les propriétés de l'ancien objet (via l'opérateur d'étalement) et les nouveaux accessoires également.
Ed Orsi
3
Vous avez raison sur le fait que cela aurait dû être Person = Person.map(code here). Mais le fait est que vous pouvez facilement ajouter des propriétés à un objet existant ES6.
Edper
5

La manière la plus simple et la plus portable est.

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

Basé sur la réponse #abeing!

Sydwell
la source
3

Soyez prudent lorsque vous ajoutez une propriété à l'objet existant à l'aide de la méthode . (Point) .

(.dot) La méthode d'ajout d'une propriété à l'objet ne doit être utilisée que si vous connaissez la «clé» au préalable, sinon utilisez la méthode [bracket] .

Exemple:

   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 }

Notez le problème dans le journal de fin de console - «clé: 1999» au lieu de Property6: 6, Property7: 7, ........., Property1999: 1999 . Ainsi, la meilleure façon d'ajouter une propriété créée dynamiquement est la méthode [bracket].

SridharKritha
la source
1

Un bon moyen d'accéder à partir de noms de chaînes dynamiques qui contiennent des objets (par exemple 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;
}

Exemple:

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

eval fonctionne pour la valeur de lecture, mais la valeur d'écriture est un peu plus difficile.

Une version plus avancée (créer des sous-classes si elles n'existent pas et autoriser les objets au lieu des variables globales)

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

Exemple:

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

C'est la même chose que o.object.subobject.property

hamboy75
la source
1
exactement ce que je cherchais, cela est utile pour réagir this.setState ({propriété dynamique: valeur}) merci!
Kevin Danikowski
0

Voici comment j'ai résolu le problème.

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

Génère l'objet suivant:

{
  "someouter": {
    "someinner": {
      "someValue": 123
    }
  },
  "simpleString": "string"
}
lewma
la source
0

Cela peut être utile si une nouvelle propriété mixte est ajoutée au moment de l'exécution:

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

Cependant, l'opérateur réparti utilise une copie superficielle, mais ici, nous nous assignons des données, donc ne perdons rien

Mohsen
la source
-2

Un moyen facile et parfait

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

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

console.log(data);

Si vous souhaitez l'appliquer sur un tableau de données (version 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
la source
-14

Absolument. Considérez-le comme un dictionnaire ou un tableau associatif. Vous pouvez y ajouter à tout moment.

thedz
la source
1
Le simple fait de dire que cela n'aidera pas.
Obinna Nwakwue