JavaScript对象是否可以动态添加命名属性?

948
在JavaScript中,我创建了如下的对象:
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?

使用用户输入来操作数据结构中的键是最好情况下的反模式,最坏情况下是安全隐患。你真正想要通过这样做实现什么目标? - ggorlen
22个回答

1501
可以的。

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

data["PropertyD"] = 4;

// dialog box with 4 in it
alert(data.PropertyD);
alert(data["PropertyD"]);


169
"data.PropertyD需要知道属性名称,但这种方式不够灵活。" - Georg Schölly
11
因为这篇文章帮助了我,所以我给它点了个赞。但我不明白为什么对象属性要像数组一样处理。 - Ron van der Heijden
9
这是 JavaScript 的奇怪设计的一部分。在这种情况下,object["property"]array[4]不完全相同,前者并不是作为真正的数组创建的。 - Georg Schölly
11
这篇帖子是不是只有我觉得它在获得195个赞的同时并没有回答问题?我认为问题是如何定义属性,其中名称在JavaScript代码生成之前是未知的。请问是否正确? - Qantas 94 Heavy
40
假设直接回答这个问题是行不通的。但是从data["PropertyD"]data[function_to_get_property_name()]的转换似乎很简单。 - Georg Schölly
显示剩余17条评论

297

ES6赢了!

const b = 'B';
const c = 'C';

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

如果你记录 data,你会得到这个:

{
  a: true,
  B: true,
  interpolated-C: true,
  B-C: true
}

这利用了 ECMAScript 2015 中的新 计算属性 语法和 模板字符串


2
这正是我需要的情况,当我想让我的代码完全功能化时(即没有命令式语句说 obj[propname])。相反,我能够使用对象扩展语法来实现。 - intcreator
3
对于那些没有看过或理解新语法的人来说,这段代码片段的作用并不是立即显而易见的。我建议编辑一下,使用 'a' 常量来显示输出/预期属性。 - user1630889
1
在某些情况下是的,但是当使用不可变结构时,这种语法非常方便,因为你展示的方法会改变 a。(特别是在使用像 redux 这样的包时) - Mauricio Soares
6
这太棒了 { ...state, [prop]: val } - Xipo
1
这是正确的答案,真正有助于动态构建它,谢谢! - Maxime Culea
显示剩余2条评论

96

是的,这是可能的。假设:

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

两种可能的情况:

data[propertyName] = propertyValue;
或者
eval("data." + propertyName + " = '" + propertyValue + "'");

首选第一种方法。如果您使用用户提供的值,则eval()具有明显的安全问题,因此如果可以避免使用,请不要使用它,但了解其存在及其功能是值得的。

您可以使用以下方式引用此内容:

alert(data.someProperty);
或者
data(data["someProperty"]);
或者
alert(data[propertyName]);

63
使用 eval 很危险。 - Georg Schölly
@GeorgSchölly 确实。 - Obinna Nwakwue
1
注意(如果其他人遇到和我一样的问题):对于普通对象,这个方法很有效。但是我不得不向 jQuery UI 对象添加一些属性来跟踪项目列表。在这种情况下,使用这种方式添加属性时,该属性会丢失,因为jQuery总是会创建一个副本。这时需要使用 jQuery.extend() - Matt
我想在我的上一个评论中补充一点 - 即使在我的情况下也不起作用。所以我最终使用 $("#mySelector").data("propertyname", myvalue); 来设置,var myValue=$("#mySelector").data("propertyname"); 来获取值。甚至可以通过这种方式添加复杂对象(列表、数组等)。 - Matt

79

ES6引入了计算属性名,可以让您执行

let a = 'key'
let myObj = {[a]: 10};
// output will be {key:10}

最佳答案。这是唯一一个对我有用的。谢谢。 - S.Alvi

70

我知道这个问题已经得到了完美的回答,但我也发现了另一种添加新属性的方法,并想与你分享:

你可以使用Object.defineProperty()函数

Mozilla开发者网络上找到

例如:

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

5
这种方法的利弊如何? - Trevor
6
@Trevor:完全可配置性,能够添加getter和setter;另外,使用defineProperties(复数形式)一次添加多个属性的能力。 - rvighne
@Thielicious Object.defineProperty 不是旨在成为易用的便利工具,而是具有细粒度控制的工具。如果您不需要额外的控制,那么它就不是正确的选择。 - kleinfreund
"Object.defineProperty(obj, prop, valueDescriptor)" 比起简单地执行 "obj[prop] = value",对于 V8 来说速度更慢且更难优化。 - Jack G
这是最佳解决方案。使用obj[prop] = value动态创建条目时,当您尝试使用Object.getOwnPropertyNames等函数解析对象时,会让您头痛不已。 - Grumbunks

23

在这里,使用您的符号表示:

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'

21

您可以使用点符号来添加任意数量的属性:

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

或者:

data[newattribute] = somevalue

用于动态键的情况。


5
如果属性名称直到运行时才确定,那么除非使用eval(这不是一个好的选择),否则它将无法工作。 - Marc Gravell
1
数据[somevar] = somevalue - Gabriel Hurley

19

除了之前的回答外,如果你想知道我们将如何使用计算属性名称(ECMAScript 6)编写未来中的动态属性名称,这里是方法:

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

personIndex[ personId ]; // "John Doe"
reference: Understanding ECMAScript 6 - Nickolas Zakas

15

仅对上面abing的答案进行补充。您可以定义一个函数来封装如下所述的defineProperty的复杂性。

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

参考资料: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript


13
你可以使用以下选项之一动态添加属性:
在你的例子中:
var data = {
    'PropertyA': 1,
    'PropertyB': 2,
    'PropertyC': 3
};

您可以用以下两种方式定义一个具有动态值的属性:

data.key = value;
或者
data['key'] = value;

更进一步……如果你的键也是动态的,你可以使用 Object 类进行定义:

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

其中data是你的对象,key是用于存储键名的变量,value是用于存储值的变量。

希望这可以帮到你!


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