如何向JavaScript对象添加键/值对?

1961

这是我的对象字面量:

var obj = {key1: value1, key2: value2};

如何向对象添加字段key3和值value3


5
好的,JavaScript中关于关联数组的整个问题很奇怪,因为你可以这样做... http://dreaminginjavascript.wordpress.com/2008/06/27/how-weird-are-javascript-arrays/ - Nosredna
2
@Nosredna - 关键是,在JavaScript中没有关联数组这样的东西。在那篇文章中,他将对象属性添加到一个数组对象中,但这些属性并不真正属于“数组”。 - UpTheCreek
2
有没有一种方法可以在对象字面量中使用未来的ES+实现有条件地设置键值对? - Con Antonakos
25个回答

2999

有两种方法可以向对象添加新的属性:

var obj = {
    key1: value1,
    key2: value2
};

使用点符号:

obj.key3 = "value3";

使用方括号表示法:

obj["key3"] = "value3";
第一种形式用于知道属性名称的情况。第二种形式用于动态确定属性名称的情况。就像这个例子中所示:
var getProperty = function (propertyName) {
    return obj[propertyName];
};

getProperty("key1");
getProperty("key2");
getProperty("key3");

你可以使用以下两种方法之一来构造一个真实的JavaScript数组:

数组字面量表示法:

var arr = [];

数组构造函数表示法:

var arr = new Array();

4
obj 是一个对象。花括号内(包括花括号)的部分是对象字面量。obj 不是一个对象字面量。 - Nosredna
44
如果关键字是一个数字,该怎么办? obj.123 = 456 是不起作用的。 但是 obj[123] = 456 是可以起作用的。 - axel freudiger
21
确实,任何在语法上不是有效变量标识符的内容都必须使用方括号表示法。 - Ionuț G. Stan
1
@KyleKhalaf Object.keys({"b": 1, "c": 3, "a": 2}).sort().forEach(console.log); 将对象中的键按字母顺序排序并依次打印输出。 - Ionuț G. Stan
2
@JohnSmith length 属性未设置,因为它不是一个数组,而是一个对象/映射/字典。 - Ionuț G. Stan
显示剩余17条评论

670

2017年的答案:Object.assign()

Object.assign(dest, src1, src2, ...)合并对象。

它用(任意多个)源对象的属性和值覆盖dest,然后返回dest

Object.assign()方法用于从一个或多个源对象中复制所有可枚举自有属性的值到目标对象。它将返回目标对象。

实时示例

var obj = {key1: "value1", key2: "value2"};
Object.assign(obj, {key3: "value3"});

document.body.innerHTML = JSON.stringify(obj);

2018年的答案:对象展开运算符{...}

obj = {...obj, ...pair, scalar};

来自MDN

它将提供的对象的可枚举属性复制到一个新对象中。

现在可以使用比Object.assign()更短的语法进行浅克隆(不包括原型)或合并对象。

请注意,Object.assign()会触发setters,而展开语法不会。

实时示例

它在当前的Chrome和Firefox中运行。 他们说它在当前的Edge中不起作用。

var obj = {key1: "value1", key2: "value2"};
var pair = {key3: "value3"};
var scalar = "value4"
obj = {...obj, ...pair, scalar};

document.body.innerHTML = JSON.stringify(obj);

2019年答案

对象赋值运算符 +=:

obj += {key3: "value3"};

糟糕...我有点过度了。从未来走私信息是非法的。已经被适当地掩盖!


111

我在编写大型项目时喜欢使用LoDash/Underscore

通过obj['key']obj.key添加内容都是纯粹的JavaScript答案。然而,LoDash和Underscore库在处理对象和数组时提供了许多额外方便的函数。

.push()是用于数组,不适用于对象

根据您所需,有两个特定的函数可能很好地实现类似于arr.push()的功能。有关更多信息,请查看文档,那里有一些很好的示例。

_.merge(仅适用于Lodash)

第二个对象将覆盖或添加到基本对象中。未定义的值不会被复制。
var obj = {key1: "value1", key2: "value2"};
var obj2 = {key2:"value4", key3: "value3", key4: undefined};
_.merge(obj, obj2);
console.log(obj);
// → {key1: "value1", key2: "value4", key3: "value3"} 

_.extend / _.assign

第二个对象将覆盖或添加到基本对象中。 undefined 将被复制。

var obj = {key1: "value1", key2: "value2"};
var obj2 = {key2:"value4", key3: "value3", key4: undefined};
_.extend(obj, obj2);
console.log(obj);
// → {key1: "value1", key2: "value4", key3: "value3", key4: undefined}

_.defaults

第二个对象包含默认值,如果它们不存在,则将添加到基本对象中。如果键已经存在,则会复制undefined值。
var obj = {key3: "value3", key5: "value5"};
var obj2 = {key1: "value1", key2:"value2", key3: "valueDefault", key4: "valueDefault", key5: undefined};
_.defaults(obj, obj2);
console.log(obj);
// → {key3: "value3", key5: "value5", key1: "value1", key2: "value2", key4: "valueDefault"}

$.extend

此外,值得一提的是jQuery.extend函数,它与_.merge类似,如果您已经在使用jQuery,可能是更好的选择。
第二个对象将覆盖或添加到基本对象中。未定义的值不会被复制。
var obj = {key1: "value1", key2: "value2"};
var obj2 = {key2:"value4", key3: "value3", key4: undefined};
$.extend(obj, obj2); 
console.log(obj);
// → {key1: "value1", key2: "value4", key3: "value3"}

Object.assign()

值得一提的是ES6/ ES2015中的Object.assign方法,它与_.merge函数类似,并且如果您已经使用ES6/ ES2015的polyfill(如Babel),则可能是最佳选择,如果您想要自己进行填充。第二个对象将覆盖或添加到基本对象中。undefined将被复制。
var obj = {key1: "value1", key2: "value2"};
var obj2 = {key2:"value4", key3: "value3", key4: undefined};
Object.assign(obj, obj2); 
console.log(obj);
// → {key1: "value1", key2: "value4", key3: "value3", key4: undefined}

我相信现在的 _.merge 已经变成了 _.extend(destination, others)。 - A.D
啊,你说得对,_.extend 是一个更通用的别名,因为 underscore 库仍在使用 extend 而不是 merge。我会更新我的答案。 - Sir.Nathan Stassen

88
你可以使用以下任意一种方式(前提是key3是你要使用的实际键名)。
arr[ 'key3' ] = value3;
或者
arr.key3 = value3;
如果key3是一个变量,那么你应该这样做:
var key3 = 'a_key';
var value3 = 3;
arr[ key3 ] = value3;

在此之后,请求 arr.a_key 将返回字面值为 3value3的值。


9
这不是一个数组,而是一个对象。JS 数组只能用整数作为索引。尝试执行 arr.length,它会返回 0。更多阅读资料:http://www.less-broken.com/blog/2010/12/lightweight-javascript-dictionaries.html - DevAntoine
1
@DevAntoine的链接无法访问。 如果您想获取此数组类型的“长度”,请使用: Object.keys(your_array).length更多关于这个问题的阅读,请参见: https://dev59.com/vGEi5IYBdhLWcg3wWLPc - The Anh Nguyen
1
一个简单的方法是覆盖正在创建的数组的长度属性。对于我来说,设置var myarray ["length"] = numArrayFields解决了这个问题。假设你以某种方式跟踪你正在添加到数组中的字段数量。 - Giga Chad Coding

75

性能

2020.01.14我在MacOS HighSierra 10.13.6上使用Chrome v78.0.0,Safari v13.0.4和Firefox v71.0.0进行了一些选定解决方案的测试。我将解决方案分为可变(首字母为M)和不可变(首字母为I)。我还提供了一些不可变解决方案(IB、IC、ID/IE),这些方案还没有在回答中发布。

结论

  • 最快的可变解决方案比最快的不可变解决方案快得多(>10倍)
  • 经典的可变方法,如 obj.key3 = "abc" (MA,MB) 是最快的
  • 对于不可变解决方案,{...obj, key3:'abc'}Object.assign (IA,IB) 是最快的
  • 令人惊讶的是,在Chrome(MC-IA)和Safari(MD-IB)中,有些不可变解决方案比一些可变解决方案更快

enter image description here

细节

In snippet below there are presended tested solution, you can prefrom test on your machine HERE (update 2022: I send Big thanks to Josh DeLong who rewrite tests from jspref.com which stops working to jsbench.me)

var o = {
    key1: true,
    key2: 3,
};

var log= (s,f)=> console.log(`${s} --> ${JSON.stringify(f({...o}))}`);



function MA(obj) {
  obj.key3 = "abc";
  return obj;
}

function MB(obj) {
  obj['key3'] = "abc";
  return obj;
}

function MC(obj) {
  Object.assign(obj, {key3:'abc'});
  return obj;
}

function MD(obj) {
  Object.defineProperty(obj, 'key3', {
    value: "abc",       // undefined by default
    enumerable: true,      // false by default
    configurable: true,    // false by default
    writable: true         // false by default
  });
  return obj;
}

function IA(obj) {
  return {...obj, key3:'abc'};
}

function IB(obj) {
  return Object.assign({key3:'abc'}, obj);
}

function IC(obj) {
  let ob= JSON.parse(JSON.stringify(obj))
  ob.key3 = 'abc';
  return ob;
}


function ID(obj) {
    let ob= Object.fromEntries(Object.entries(obj));
  ob.key3 = 'abc';
  return ob;
}

function IE(obj) {
    return Object.fromEntries(Object.entries(obj).concat([['key3','abc']]))
}



log('MA',MA);
log('MB',MB);
log('MC',MC);
log('MD',MD);
log('IA',IA);
log('IB',IB);
log('IC',IC);
log('ID',ID);
log('IE',IE);
This snippet only presents code - it not perform tests itself!

enter image description here


34
展开运算符是一种方便快捷的语法,可用于向数组中添加项目、组合数组或对象,并将数组展开为函数参数。现在,ES2018带有将展开属性复制到对象文字的功能。它从提供的对象上复制其自身可枚举属性到一个新对象。
展开语法对于将对象上的属性和方法组合到一个新对象中非常有用:
您可以像这样在对象中添加属性。

const obj1 = {hello: ""};
const obj2 = {...obj1, laugh: "" };
console.log('obj1', obj1)
console.log('obj2', obj2)

你也可以像这样组合对象

const objectOne = {hello: ""}
const objectTwo = {world: ""}
const objectThree = {...objectOne, ...objectTwo, laugh: ""}
console.log(objectThree) // Object { hello: "", world: "", laugh: "" }
const objectFour = {...objectOne, ...objectTwo, laugh: () => {console.log("".repeat(5))}}
objectFour.laugh() // 


32
arr.key3 = value3;

因为你的arr不是真正的数组... 它是一个原型对象。真正的数组应该是:

var arr = [{key1: value1}, {key2: value2}];

但是它还不正确。实际上应该是:

var arr = [{key: key1, value: value1}, {key: key2, value: value2}];

17

简单添加属性:

我们想要向这个对象添加 prop2 : 2,这是最方便的方法:

  1. 点运算符:object.prop2 = 2;
  2. 方括号:object['prop2'] = 2;

那么我们该使用哪种呢?

点运算符具有更清晰的语法,并且应作为默认选项(我个人认为)。但是,点运算符无法添加动态键到一个对象中,在某些情况下可能非常有用。以下是一个例子:

const obj = {
  prop1: 1
}

const key = Math.random() > 0.5 ? 'key1' : 'key2';

obj[key] = 'this value has a dynamic key';

console.log(obj);

合并对象:

当我们想要合并两个对象的属性时,以下是最方便的选项:

  1. Object.assign(),将目标对象作为参数,一个或多个源对象,并将它们合并在一起。例如:

const object1 = {
  a: 1,
  b: 2,
};

const object2 = Object.assign({
  c: 3,
  d: 4
}, object1);

console.log(object2);

  1. 对象展开运算符...

const obj = {
  prop1: 1,
  prop2: 2
}

const newObj = {
  ...obj,
  prop3: 3,
  prop4: 4
}

console.log(newObj);

我们该使用哪种语法?

  • 我认为,展开语法不仅更简洁,而且应该作为默认选项。别忘了将这个语法转换成所有浏览器都支持的语法,因为它相对较新。
  • Object.assign() 更加灵活,因为我们可以访问传入的所有对象并在分配给新对象之前操纵它们。

1
在您的帖子顶部:那些不是花括号,而是方括号。 - Bram Vanroy

14

我知道这个问题已经有了一个被接受的答案,但是我想在某个地方记录下我的想法。请各位[人]随意挑毛病,因为我不确定这是否是最好的解决方案......但我几分钟前才想到这个:

Object.prototype.push = function( key, value ){
   this[ key ] = value;
   return this;
}

你可以这样使用它:

var obj = {key1: value1, key2: value2};
obj.push( "key3", "value3" );

由于原型函数返回了 this,你可以继续在 obj 变量末尾链接多个 .push 方法:obj.push(...).push(...).push(...);

另一个特性是,在 push 函数的参数中,你可以传递一个数组或另一个对象作为值。请参见我的 fiddle 以获取一个可工作的示例:http://jsfiddle.net/7tEme/


1
也许这不是一个好的解决方案,我在jquery1.9中似乎遇到了错误:TypeError: 'undefined' is not a function (evaluating 'U[a].exec(s)')。这很奇怪,因为即使在jsfiddle上使用jquery1.9也能正常工作。 - sadmicrowave
5
不应扩展Object.prototype,这会破坏JavaScript的“对象作为哈希表”的特性(以及许多库,如Google Maps JS API)。参见讨论:https://dev59.com/d3I-5IYBdhLWcg3wfoa1。 - Justin R.

12

大多数答案中已经提到了两种最常用的方法

obj.key3 = "value3";

obj["key3"] = "value3";

使用Object.defineProperty()是定义属性的另一种方式。

Object.defineProperty(obj, 'key3', {
  value: "value3",       // undefined by default
  enumerable: true,      // false by default
  configurable: true,    // false by default
  writable: true         // false by default
});

当您想要在定义属性时拥有更多控制时,此方法非常有用。 用户可以将定义的属性设置为可枚举、可配置和可写。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接