Come usare una variabile per una chiave in un oggetto JavaScript letterale?


407

Perché funziona quanto segue?

<something>.stop().animate(
    { 'top' : 10 }, 10
);

Considerando che questo non funziona:

var thetop = 'top';
<something>.stop().animate(
    { thetop : 10 }, 10
);

Per renderlo ancora più chiaro: al momento non sono in grado di passare una proprietà CSS alla funzione animata come variabile.



Risposte:


668

{ thetop : 10 }è un oggetto valido letterale. Il codice creerà un oggetto con una proprietà denominata thetopche ha un valore di 10. Entrambi i seguenti sono gli stessi:

obj = { thetop : 10 };
obj = { "thetop" : 10 };

In ES5 e precedenti, non è possibile utilizzare una variabile come nome di proprietà all'interno di un oggetto letterale. L'unica opzione è effettuare le seguenti operazioni:

var thetop = "top";

// create the object literal
var aniArgs = {};

// Assign the variable property name with a value of 10
aniArgs[thetop] = 10; 

// Pass the resulting object to the animate method
<something>.stop().animate(
    aniArgs, 10  
);  

ES6 definisce ComputedPropertyName come parte della grammatica per oggetti letterali, che consente di scrivere il codice in questo modo:

var thetop = "top",
    obj = { [thetop]: 10 };

console.log(obj.top); // -> 10

È possibile utilizzare questa nuova sintassi nelle ultime versioni di ciascun browser mainstream.


Capisco! Grazie! Non dovrebbe funzionare anche con Eval? Voglio dire, al momento non funziona nel mio esempio, ma per
dirla

3
@Marcel: eval()non funzionerebbe all'interno di un oggetto letterale per nomi di proprietà dinamici, si otterrebbe solo un errore. Non solo, ma in eval()realtà non dovrebbe essere usato per queste cose. C'è un eccellente articolo sull'uso corretto e scorretto di eval: blogs.msdn.com/ericlippert/archive/2003/11/01/53329.aspx
Andy E

2
@AndyE considera l'aggiornamento di "versioni recenti" e "IE TP attuale" con tempi più specifici come "Versioni successive a XXX" o "dopo 2014-mm" (farei il cambiamento da solo, ma non so quali buoni valori sarebbe
Alexei Levenkov il

1
per ES6, c'è un modo per escludere la proprietà se la chiave è nulla / non definita? Ad esempio, {[key] : "value"}se la chiave fosse nulla, darebbe { null: "value"}, mentre mi piacerebbe che il risultato fosse{}
wasabigeek

soluzione meravigliosa, ma perché lo sai così tanto, anche leggere l'intera specifica non ha potuto ottenere il punto :(
hugemeow

126

Con ECMAScript 2015 ora puoi farlo direttamente nella dichiarazione dell'oggetto con la notazione tra parentesi: 

var obj = {
  [key]: value
}

Dove keypuò essere qualsiasi tipo di espressione (ad esempio una variabile) che restituisce un valore.

Quindi qui il tuo codice sarebbe simile a:

<something>.stop().animate({
  [thetop]: 10
}, 10)

Dove thetopverrà valutato prima di essere utilizzato come chiave.


17

Citazione ES5 che dice che non dovrebbe funzionare

Nota: le regole sono cambiate per ES6: https://stackoverflow.com/a/2274327/895245

Spec: http://www.ecma-international.org/ecma-262/5.1/#sec-11.1.5

Nome della proprietà :

  • IdentifierName
  • letterale stringa
  • NumericLiteral

[...]

Il PropertyName di produzione: IdentifierName viene valutato come segue:

  1. Restituisce il valore String contenente la stessa sequenza di caratteri di IdentifierName.

Il PropertyName di produzione: StringLiteral viene valutato come segue:

  1. Restituisce SV [valore stringa] di StringLiteral.

Il PropertyName di produzione: NumericLiteral viene valutato come segue:

  1. Lascia che nbr sia il risultato della formazione del valore di NumericLiteral.
  2. Return ToString (nbr).

Ciò significa che:

  • { theTop : 10 } è esattamente lo stesso di { 'theTop' : 10 }

    La PropertyName theTopè una IdentifierName, in modo che viene convertito nel 'theTop'valore di stringa, che è il valore di stringa 'theTop'.

  • Non è possibile scrivere inizializzatori di oggetti (valori letterali) con chiavi variabili.

    Le uniche tre opzioni sono IdentifierName(si espande in stringa letterale) StringLiteral, e NumericLiteral(si espande anche in una stringa).


3
Downvoter: sono stato il primo a citare qualsiasi standard su questa domanda. La citazione ES6 sulla risposta principale è stata modificata dopo che ho risposto e tale standard non era ancora stato accettato al momento. Se ti capita di sapere perché altrimenti ricevo voti negativi, ti prego di spiegare, voglio solo imparare. Potrei anche ottenere il badge della pressione dei pari ...
Ciro Santilli 14 冠状 病 六四 事件 法轮功

Immagino che il downvote sia stato principalmente perché la tua risposta non offre una soluzione, ed è "non utile" a questo proposito. Votando verso la pressione dei coetanei :-)
Bergi

3
@Bergi grazie per il coraggio! :-) Ma penso di aver risposto direttamente alla domanda : D: "Perché funziona quanto segue?". A: perché ES5 lo dice. "Cosa fare al riguardo?" è implicito. Hai sottovalutato la domanda principale per dire "È impossibile" senza una citazione standard prima che qualcuno abbia modificato la soluzione ES6?
Ciro Santilli 3 冠状 病 六四 事件 法轮功

Ah giusto. In genere cito le domande specifiche in un blockquote per chiarire quando rispondo direttamente. Citare lo standard può rendere una buona risposta ancora migliore, ma al momento il tuo post non risponde nemmeno alla domanda imo. Dichiarare cosa può fare una chiave in ES5 non implica come funzionano. Sicuramente thetopè un IdentifierName, quindi perché non funziona ? Questa domanda è ancora aperta.
Bergi,

1
@Bergi grazie ancora per avermi spiegato! L'ho aggiornato per renderlo più esplicito. Non l'avevo mai fatto prima perché pensavo fosse ovvio, perché possiamo scrivere {a:1}.a, quindi achiaramente non espandere il valore della variabile nel caso dell'identificatore. Ma sì, spiegare ulteriormente è un miglioramento in questo caso.
Ciro Santilli 3 冠状 病 六四 事件 法轮功

5

Ho usato quanto segue per aggiungere una proprietà con un nome "dinamico" a un oggetto:

var key = 'top';
$('#myElement').animate(
   (function(o) { o[key]=10; return o;})({left: 20, width: 100}),
   10
);

key è il nome della nuova proprietà.

L'oggetto delle proprietà passate animatesarà{left: 20, width: 100, top: 10}

Questo sta solo usando la []notazione richiesta come raccomandato dalle altre risposte, ma con meno righe di codice!


5

L'aggiunta di parentesi quadre attorno alla variabile funziona bene per me. Prova questo

var thetop = 'top';
<something>.stop().animate(
    { [thetop] : 10 }, 10
);

Questo non funzionerà nelle versioni precedenti di EcmaScript, ma al giorno d'oggi questo è un approccio molto pulito.
Oliver,

3

Non sono riuscito a trovare un semplice esempio delle differenze tra ES6 ed ES5, quindi ne ho creato uno. Entrambi gli esempi di codice creano esattamente lo stesso oggetto. Ma l'esempio ES5 funziona anche con i browser più vecchi (come IE11), mentre l'esempio ES6 no.

ES6

var matrix = {};
var a = 'one';
var b = 'two';
var c = 'three';
var d = 'four';

matrix[a] = {[b]: {[c]: d}};

ES5

var matrix = {};
var a = 'one';
var b = 'two';
var c = 'three';
var d = 'four';

function addObj(obj, key, value) {
  obj[key] = value;
  return obj;
}

matrix[a] = addObj({}, b, addObj({}, c, d));

2

Aggiornamento / esempio 2020 ...

Un esempio più complesso, usando parentesi e letterali ... qualcosa che potresti dover fare ad esempio con vue / axios. Avvolgi il letterale tra parentesi, quindi

[`...`]

{
    [`filter[${query.key}]`]: query.value,  // 'filter[foo]' : 'bar'
}

1

Codice dato:

var thetop = 'top';
<something>.stop().animate(
    { thetop : 10 }, 10
);

Traduzione:

var thetop = 'top';
var config = { thetop : 10 }; // config.thetop = 10
<something>.stop().animate(config, 10);

Come puoi vedere, la { thetop : 10 }dichiarazione non utilizza la variabile thetop. Invece crea un oggetto con una chiave denominata thetop. Se vuoi che la chiave sia il valore della variabile thetop, dovrai usare parentesi quadre intorno thetop:

var thetop = 'top';
var config = { [thetop] : 10 }; // config.top = 10
<something>.stop().animate(config, 10);

La sintassi della parentesi quadra è stata introdotta con ES6. Nelle versioni precedenti di JavaScript, dovresti fare quanto segue:

var thetop = 'top';
var config = (
  obj = {},
  obj['' + thetop] = 10,
  obj
); // config.top = 10
<something>.stop().animate(config, 10);

1

Non riesco a credere che questo non sia stato ancora pubblicato: basta usare la notazione a freccia con valutazione anonima!

Completamente non invasivo, non scherza con lo spazio dei nomi e richiede solo una riga:

myNewObj = ((k,v)=>{o={};o[k]=v;return o;})(myKey,myValue);

demo:

utile in ambienti che non supportano ancora la nuova {[myKey]: myValue}sintassi, come - apparentemente; L'ho appena verificato sulla mia Console per gli sviluppatori Web: Firefox 72.0.1, rilasciato il 2020-01-08.

(Sono sicuro che potresti potenzialmente creare alcune soluzioni più potenti / estensibili o qualsiasi cosa implichi un uso intelligente reduce, ma a quel punto probabilmente saresti meglio servito semplicemente rompendo la creazione di oggetti nella sua funzione invece di bloccarla compulsivamente tutto in linea)


non è importante da quando OP lo ha chiesto dieci anni fa, ma per completezza e per dimostrare come sia esattamente la risposta alla domanda come affermato, lo mostrerò nel contesto originale:

var thetop = 'top';
<something>.stop().animate(
    ((k,v)=>{o={};o[k]=v;return o;})(thetop,10), 10
);

0

In questo modo è anche possibile ottenere l'output desiderato

var jsonobj={};
var count=0;
$(document).on('click','#btnadd', function() {
    jsonobj[count]=new Array({ "1"  : $("#txtone").val()},{ "2"  : $("#txttwo").val()});
    count++;
    console.clear();
    console.log(jsonobj);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span>value 1</span><input id="txtone" type="text"/>
<span>value 2</span><input id="txttwo" type="text"/>
<button id="btnadd">Add</button>


0

L'implementazione ES5 per assegnare le chiavi è di seguito:

var obj = Object.create(null),
    objArgs = (
      (objArgs = {}),
      (objArgs.someKey = {
        value: 'someValue'
      }), objArgs);

Object.defineProperties(obj, objArgs);

Ho allegato uno snippet che ho usato per convertire in oggetto nudo.

var obj = {
  'key1': 'value1',
  'key2': 'value2',
  'key3': [
    'value3',
    'value4',
  ],
  'key4': {
    'key5': 'value5'
  }
}

var bareObj = function(obj) {

  var objArgs,
    bareObj = Object.create(null);

  Object.entries(obj).forEach(function([key, value]) {

    var objArgs = (
      (objArgs = {}),
      (objArgs[key] = {
        value: value
      }), objArgs);

    Object.defineProperties(bareObj, objArgs);

  });

  return {
    input: obj,
    output: bareObj
  };

}(obj);

if (!Object.entries) {
  Object.entries = function(obj){
    var arr = [];
    Object.keys(obj).forEach(function(key){
      arr.push([key, obj[key]]);
    });
    return arr;
  }
}

console(bareObj);


0

È possibile effettuare le seguenti operazioni per ES5:

var theTop = 'top'
<something>.stop().animate(
  JSON.parse('{"' + theTop + '":' + JSON.stringify(10) + '}'), 10
)

Oppure estrai in una funzione:

function newObj (key, value) {
  return JSON.parse('{"' + key + '":' + JSON.stringify(value) + '}')
}

var theTop = 'top'
<something>.stop().animate(
  newObj(theTop, 10), 10
)


0

Puoi farlo in questo modo:

var thetop = 'top';
<something>.stop().animate(
    new function() {this[thetop] = 10;}, 10
);

0

Puoi anche provare in questo modo:

let array1 = [{
    "description": "THURSDAY",
    "count": "1",
    "date": "2019-12-05"
},
{
    "description": "WEDNESDAY",
    "count": "0",
    "date": "2019-12-04"
}]
let res = array1.map((value, index) => {
    return { [value.description]: { count: value.count, date: value.date } }
})
console.log(res);

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.