Como usar uma variável para uma chave em um literal de objeto JavaScript?

407

Por que o seguinte funciona?

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

Considerando que isso não funciona:

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

Para deixar ainda mais claro: No momento, não sou capaz de passar uma propriedade CSS para a função animar como uma variável.

speendo
fonte

Respostas:

668

{ thetop : 10 }é um literal de objeto válido. O código criará um objeto com uma propriedade nomeada thetopque possui um valor de 10. Ambos são os mesmos:

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

No ES5 e versões anteriores, você não pode usar uma variável como um nome de propriedade dentro de um literal de objeto. Sua única opção é fazer o seguinte:

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

O ES6 define ComputedPropertyName como parte da gramática dos literais de objetos, o que permite escrever o código assim:

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

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

Você pode usar essa nova sintaxe nas versões mais recentes de cada navegador principal.

Andy E
fonte
Compreendo! Obrigado! Isso também não funciona com eval? Quero dizer que não funciona no meu exemplo no momento, mas eu acho que deveria ... :-)
speendo
3
@ Marcel: eval()não funcionaria dentro de um literal de objeto para nomes de propriedades dinâmicas, você apenas receberia um erro. Não apenas isso, mas eval()realmente não deve ser usado para essas coisas. Existe um excelente artigo sobre o uso correto e incorreto de eval: blogs.msdn.com/ericlippert/archive/2003/11/01/53329.aspx
Andy E
2
@AndyE considere atualizar "versões recentes" e "TP atual do IE" com um tempo mais específico, como "Versões posteriores a XXX" ou "após 2014 mm" (eu mesmo faria alterações, mas não sei quais são os bons valores seria #
Alexei Levenkov 26/02
11
para o ES6, existe uma maneira de deixar de fora a propriedade se a chave for nula / indefinida? Por exemplo, em {[key] : "value"}, se a chave fosse nula, daria { null: "value"}, enquanto eu gostaria que o resultado fosse{}
wasabigeek
solução maravilhosa, mas por que você sabe tanto, mesmo lendo toda a especificação não poderia obter o ponto :(
hugemeow
126

Com o ECMAScript 2015, agora você pode fazê-lo diretamente na declaração de objeto com a notação de colchetes: 

var obj = {
  [key]: value
}

Onde keypode haver qualquer tipo de expressão (por exemplo, uma variável) retornando um valor.

Portanto, aqui seu código seria semelhante a:

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

Onde thetopserá avaliado antes de ser usado como chave.

kube
fonte
17

Citação ES5 que diz que não deve funcionar

Nota: as regras foram alteradas para o ES6: https://stackoverflow.com/a/2274327/895245

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

Nome da propriedade :

  • IdentifierName
  • StringLiteral
  • NumericLiteral

[...]

A produção PropertyName: IdentifierName é avaliada da seguinte maneira:

  1. Retorne o valor String contendo a mesma sequência de caracteres que o IdentifierName.

A produção PropertyName: StringLiteral é avaliada da seguinte maneira:

  1. Retorne o valor SV [String] do StringLiteral.

A produção PropertyName: NumericLiteral é avaliada da seguinte maneira:

  1. Seja nbr o resultado da formação do valor do NumericLiteral.
  2. Retornar ToString (nbr).

Isso significa que:

  • { theTop : 10 } é exatamente o mesmo que { 'theTop' : 10 }

    Como PropertyName theTopé um IdentifierName, ele é convertido no 'theTop'valor da sequência, que é o valor da sequência 'theTop'.

  • Não é possível escrever inicializadores de objetos (literais) com chaves variáveis.

    As únicas três opções são IdentifierName(expande para literal da cadeia) StringLiterale NumericLiteral(também se expande para uma cadeia).

Ciro Santilli adicionou uma nova foto
fonte
3
Downvoters: Eu fui o primeiro a citar qualquer padrão sobre esta questão. A citação do ES6 na resposta superior foi editada depois que eu respondi, e esse padrão ainda não era aceito na época. Se você souber por que mais estou recebendo votos negativos, explique, eu só quero aprender. Eu poderia muito bem pegar o crachá pressão dos pares ...
Ciro Santilli郝海东冠状病六四事件法轮功
Eu acho que o voto negativo foi principalmente porque sua resposta não oferece uma solução e "não é útil" nesse sentido. Votação contra a pressão dos colegas :-)
Bergi
3
@ Bergi obrigado por ter coragem! :-) Mas acho que respondi diretamente à pergunta : P: "Por que o seguinte funciona?". A: porque o ES5 diz isso. "O que fazer sobre isso?" está implícito. Você votou negativamente na principal pergunta por dizer "É impossível" sem uma cotação padrão antes de alguém ser editado na solução ES6?
Ciro Santilli foi lançado em 03/08/15
Ah, certo. Normalmente, cito as perguntas específicas em uma citação em bloco para deixar claro quando as respondo diretamente. Citar o padrão pode fazer uma boa resposta ainda melhor, mas atualmente sua postagem nem responde à pergunta imo. Declarar o que pode fazer uma chave no ES5 não significa como eles funcionam. Certamente thetopé um IdentifierName, então por que não funciona ? Essa questão ainda está em aberto.
Bergi 3/08/15
11
@ Bergi obrigado novamente por me explicar! Eu o atualizei para torná-lo mais explícito. Eu não tinha feito isso antes porque achei óbvio, porque podemos escrever {a:1}.a, portanto, aclaramente não expanda o valor da variável no caso identificador. Mas sim, explicar melhor é uma melhoria neste caso.
Ciro Santilli foi lançado em 03/08/15
5

Eu usei o seguinte para adicionar uma propriedade com um nome "dinâmico" a um objeto:

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

key é o nome da nova propriedade.

O objeto das propriedades transmitidas para animateserá{left: 20, width: 100, top: 10}

Isso é apenas usando a []notação necessária , conforme recomendado pelas outras respostas, mas com menos linhas de código!

Phil M
fonte
5

Adicionar colchete em torno da variável funciona bem para mim. Tente isto

var thetop = 'top';
<something>.stop().animate(
    { [thetop] : 10 }, 10
);
Vaishali Venkatesan
fonte
Isso não funcionará nas versões mais antigas do EcmaScript, mas hoje em dia essa é uma abordagem muito limpa.
Oliver
3

Não consegui encontrar um exemplo simples sobre as diferenças entre o ES6 e o ​​ES5, então fiz um. Os dois exemplos de código criam exatamente o mesmo objeto. Mas o exemplo do ES5 também funciona em navegadores mais antigos (como o IE11), enquanto o exemplo do ES6 não.

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));
Dieter Schmitt
fonte
2

Atualização 2020 / exemplo ...

Um exemplo mais complexo, usando colchetes e literais ... algo que você pode ter que fazer, por exemplo, com vue / axios. Coloque o literal entre colchetes, para

[`...`]

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

Código fornecido:

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

Tradução:

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

Como você pode ver, a { thetop : 10 }declaração não faz uso da variável thetop. Em vez disso, cria um objeto com uma chave chamada thetop. Se você deseja que a chave seja o valor da variável thetop, precisará usar colchetes ao redor thetop:

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

A sintaxe entre colchetes foi introduzida no ES6. Nas versões anteriores do JavaScript, você teria que fazer o seguinte:

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

Eu não posso acreditar que isso ainda não foi publicado: basta usar a seta-notação com a avaliação anônima!

Completamente não invasivo, não mexe com o espaço para nome e leva apenas uma linha:

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

demo:

útil em ambientes que ainda não suportam a nova {[myKey]: myValue}sintaxe, como - aparentemente; Acabei de verificar no meu console do desenvolvedor da Web - Firefox 72.0.1, lançado em 20-01-2008.

(Tenho certeza de que você poderia potencialmente criar soluções mais poderosas / extensíveis ou o que quer que envolva o uso inteligente de reduce, mas nesse momento você provavelmente seria melhor atendido apenas quebrando a criação de objetos em sua própria função, em vez de ativá-la compulsivamente tudo em linha)


não que isso importe, já que o OP pediu isso há dez anos, mas por uma questão de exaustividade e para demonstrar como é exatamente a resposta para a pergunta conforme declarada, mostrarei isso no contexto original:

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

Dessa forma, você também pode obter a saída desejada

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>

Deepu Reghunath
fonte
0

A implementação do ES5 para atribuir chaves está abaixo:

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

Object.defineProperties(obj, objArgs);

Anexei um trecho que usei para converter em objeto nu.

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

querido
fonte
0

Você pode fazer o seguinte no ES5:

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

Ou extraia para uma função:

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

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

Se você deseja que a chave do objeto seja igual ao nome da variável, existe uma abreviação no ES 2015. Novas anotações no ECMAScript 2015

var thetop = 10;
var obj = { thetop };
console.log(obj.thetop); // print 10
ROROROOROROR
fonte
0

Você pode fazer assim:

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

Você também pode tentar assim:

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

Saurabh Agrawal
fonte