¿Cómo usar una variable para una clave en un objeto literal de JavaScript?


407

¿Por qué funciona lo siguiente?

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

Mientras que esto no funciona:

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

Para hacerlo aún más claro: por el momento no puedo pasar una propiedad CSS a la función animada como variable.



Respuestas:


668

{ thetop : 10 }es un objeto literal válido El código creará un objeto con una propiedad denominada thetopque tiene un valor de 10. Los dos siguientes son lo mismo:

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

En ES5 y versiones anteriores, no puede usar una variable como nombre de propiedad dentro de un objeto literal. Su única opción es hacer lo siguiente:

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 define ComputedPropertyName como parte de la gramática de los literales de objetos, lo que le permite escribir el código de esta manera:

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

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

Puede usar esta nueva sintaxis en las últimas versiones de cada navegador convencional.


¡Entiendo! ¡Gracias! ¿No debería funcionar esto también con eval? Quiero decir que no funciona en mi ejemplo en este momento, pero creo que debería ... :-)
speendo

3
@Marcel: eval()no funcionaría dentro de un literal de objeto para nombres de propiedades dinámicas, solo obtendría un error. No solo eso, sino que eval()realmente no debería usarse para tales cosas. Hay un excelente artículo sobre el uso correcto e incorrecto de eval: blogs.msdn.com/ericlippert/archive/2003/11/01/53329.aspx
Andy E

2
@AndyE considera actualizar las "versiones recientes" y el "IE TP actual" con un tiempo más específico, como "Versiones posteriores a XXX" o "después de 2014 mm" (yo mismo haría el cambio, pero no sé qué buenos valores sería.
Alexei Levenkov

1
para ES6, ¿hay alguna forma de omitir la propiedad si la clave es nula / indefinida? Por ejemplo, en {[key] : "value"}, si la clave fuera nula, daría { null: "value"}, mientras que me gustaría que el resultado fuera{}
wasabigeek el

solución maravillosa, pero por qué sabes tanto, incluso leer toda la especificación no pudo obtener el punto :(
hugemeow

126

Con ECMAScript 2015 , ahora puede hacerlo directamente en la declaración de objeto con la notación de corchetes: 

var obj = {
  [key]: value
}

Donde keypuede haber cualquier tipo de expresión (por ejemplo, una variable) que devuelva un valor.

Así que aquí su código se vería así:

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

Donde thetopserá evaluado antes de ser utilizado como clave.


17

Cita de ES5 que dice que no debería funcionar

Nota: las reglas han cambiado para ES6: https://stackoverflow.com/a/2274327/895245

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

Nombre de la propiedad :

  • IdentifierName
  • CadenaLiteral
  • NuméricoLiteral

[...]

El PropertyName de producción: IdentifierName se evalúa de la siguiente manera:

  1. Devuelve el valor de cadena que contiene la misma secuencia de caracteres que el nombre del identificador.

El PropertyName de producción: StringLiteral se evalúa de la siguiente manera:

  1. Devuelve el SV [valor de cadena] de StringLiteral.

El PropertyName de producción: NumericLiteral se evalúa de la siguiente manera:

  1. Sea nbr el resultado de formar el valor de NumericLiteral.
  2. Regresar a Cadena (nbr).

Esto significa que:

  • { theTop : 10 } es exactamente lo mismo que { 'theTop' : 10 }

    El PropertyName theTopes un IdentifierName, por lo que se convierte al 'theTop'valor de cadena, que es el valor de cadena de 'theTop'.

  • No es posible escribir inicializadores de objetos (literales) con claves variables.

    Las únicas tres opciones son IdentifierName(se expande a cadena literal) StringLiteral, y NumericLiteral(también se expande a cadena).


3
Votantes: Fui el primero en citar cualquier estándar sobre esta pregunta. La cita de ES6 en la respuesta principal se editó después de que respondí, y ese estándar aún no se aceptaba en ese momento. Si sabe por qué más recibo votos negativos, explique, solo quiero aprender. También podría obtener la insignia de presión de grupo ...
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Supongo que el voto negativo se debió principalmente a que su respuesta no ofrece una solución, y "no es útil" en ese sentido. Votación hacia la presión de grupo :-)
Bergi

3
@ Bergi gracias por tener el coraje! :-) Pero creo que he respondido la pregunta directamente : P: "¿Por qué funciona lo siguiente?". A: porque ES5 lo dice. "¿Qué hacer al respecto?" Es implícito. ¿Desestimó la pregunta principal por decir "Es imposible" sin una cita estándar antes de que alguien edite la solución ES6?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ah bien. Por lo general, cito las preguntas específicas en una cita en bloque para que quede claro cuando las contesto directamente. Citar el estándar puede hacer una buena respuesta aún mejor, pero actualmente su publicación ni siquiera responde la pregunta de la OMI. Afirmar qué puede hacer una clave en ES5 no implica cómo funcionan. Seguramente thetopes un IdentifierName, entonces ¿ por qué no funciona ? Esa pregunta aún está abierta.
Bergi

1
@ Bergi gracias de nuevo por explicarme! Lo he actualizado para hacerlo más explícito. No lo había hecho antes porque pensaba que era obvio, porque podemos escribir {a:1}.a, por lo que aclaramente no expandimos el valor de la variable en el caso del identificador. Pero sí, explicar más es una mejora en este caso.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

5

He usado lo siguiente para agregar una propiedad con un nombre "dinámico" a un objeto:

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

key es el nombre de la nueva propiedad.

El objeto de propiedades pasado a animateserá{left: 20, width: 100, top: 10}

Esto solo está usando la []notación requerida según lo recomendado por las otras respuestas, ¡pero con menos líneas de código!


5

Agregar corchetes alrededor de la variable funciona bien para mí. Prueba esto

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

Esto no funcionará en versiones anteriores de EcmaScript, pero hoy en día es un enfoque muy limpio.
Oliver

3

No pude encontrar un ejemplo simple sobre las diferencias entre ES6 y ES5, así que hice uno. Ambas muestras de código crean exactamente el mismo objeto. Pero el ejemplo ES5 también funciona en navegadores más antiguos (como IE11), mientras que el ejemplo ES6 no lo hace.

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

Actualización 2020 / ejemplo ...

Un ejemplo más complejo, usar corchetes y literales ... algo que puede tener que hacer, por ejemplo, con vue / axios. Envuelva el literal entre paréntesis, entonces

[`...`]

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

1

Código dado:

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

Traducción:

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

Como puede ver, la { thetop : 10 }declaración no hace uso de la variable thetop. En su lugar, crea un objeto con una clave llamada thetop. Si desea que la clave sea el valor de la variable thetop, deberá usar corchetes alrededor de thetop:

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

La sintaxis de corchetes se ha introducido con ES6. En versiones anteriores de JavaScript, tendría que hacer lo siguiente:

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

1

No puedo creer esto no se haya publicado todavía: ¡solo use la notación de flecha con una evaluación anónima!

Completamente no invasivo, no se mete con el espacio de nombres, y solo se necesita una línea:

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

manifestación:

útil en entornos que aún no admiten la nueva {[myKey]: myValue}sintaxis, como, aparentemente; Lo acabo de verificar en mi Consola de desarrollador web: Firefox 72.0.1, lanzado el 2020-01-08.

(Estoy seguro de que potencialmente podría hacer algunas soluciones más potentes / extensibles o lo que sea que implique un uso inteligente de reduce, pero en ese punto, probablemente sería mejor servir simplemente dividiendo la creación de objetos en su propia función en lugar de bloquearla compulsivamente todo en línea)


no es que importe, ya que OP preguntó esto hace diez años, pero por el bien de la integridad y para demostrar cómo es exactamente la respuesta a la pregunta como se indicó, lo mostraré en el contexto original:

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

0

De esta manera también puede lograr la salida deseada

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

La implementación de ES5 para asignar claves está a continuación:

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

Object.defineProperties(obj, objArgs);

He adjuntado un fragmento que solía convertir en objeto desnudo.

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

Puede hacer lo siguiente para ES5:

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

O extraer a una función:

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

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


0

Puedes hacerlo de esta manera:

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

0

También puedes intentarlo así:

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.