【问题标题】:Is it possible to add dynamically named properties to JavaScript object?是否可以将动态命名的属性添加到 JavaScript 对象?
【发布时间】:2010-11-14 02:57:15
【问题描述】:

在 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?

【问题讨论】:

标签: javascript


【解决方案1】:

是的。

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

data["PropertyD"] = 4;

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

【讨论】:

  • @thedz: data.PropertyD 需要知道属性名,不够动态。
  • +1 因为这对我有帮助。但我不明白为什么一个对象的属性会像数组一样处理。
  • @Bondye:这是 javascript 奇怪设计的一部分。在这种情况下,object["property"]array[4] 并不完全相同,前者不是作为真正的数组创建的。
  • 只有我一个人,还是这篇文章在获得 195 个赞时没有回答问题?我以为它是在询​​问如何定义名称未知的属性,直到它在 JavaScript 代码中生成。
  • @Qantas:假设它没有直接回答它。但是从data["PropertyD"]data[function_to_get_property_name()] 似乎微不足道。
【解决方案2】:

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
}

这利用了新的Computed Property 语法和Template Literals

【讨论】:

  • 在我希望我的代码完全正常运行的情况下,这是我所需要的(例如,没有命令式语句说 obj[propname])。相反,我可以将它与对象传播语法一起使用。
  • 对于没有看到或理解新语法的人来说,这段代码 sn-p 的作用并不是很明显。我建议进行编辑以使用 'a' const 显示输出/预期属性。
  • @JackGiffin 在某些情况下是的,但是在使用不可变结构时,这种语法非常方便,因为您展示的方法是变异 a。 (特别是在使用像 redux 这样的包时)
  • 这太好了 { ...state, [prop]: val }
  • 这是真正有助于动态构建它的正确答案,谢谢!
【解决方案3】:

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

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

【讨论】:

  • 使用eval真的很危险。
  • @GeorgSchölly True.
  • 注意(以防有人遇到和我一样的问题):对于普通对象,这工作正常。但我必须向 jQuery UI 对象添加一些属性来跟踪项目列表。在这种情况下,如果以这种方式添加该属性会丢失,因为 jQuery 总是会创建一个副本。这里需要使用jQuery.extend()
  • 我喜欢添加到我之前的评论中——即使这在我的情况下也不起作用。所以我最终使用$("#mySelector").data("propertyname", myvalue); 进行设置,并使用var myValue=$("#mySelector").data("propertyname"); 来取回值。甚至可以通过这种方式添加复杂的对象(列表、数组...)。
【解决方案4】:

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

可以使用Object.defineProperty()函数

发现于Mozilla Developer Network

例子:

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

【讨论】:

  • 这种方法的优缺点?
  • @Trevor:总体可配置性和添加 getter 和 setter 的能力;此外,能够使用defineProperties(复数)一次添加多个属性。
  • @Thielicious Object.defineProperty 并不是简单的便利工具,而是具有细粒度控制的工具。如果您不需要额外的控制,那么它不是正确选择的工具。
  • Object.defineProperty(obj, prop, valueDescriptor) 对于 V8 而言比简单地进行 obj[prop] = value 优化要慢得多,也更难优化;
  • 这是最好的解决方案。当您尝试使用 Object.getOwnPropertyNames 之类的函数解析对象时,使用 obj[prop] = value 动态创建条目会让您头疼
【解决方案5】:

ES6 引入了计算属性名称,它允许你这样做

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

【讨论】:

    【解决方案6】:

    在这里,使用您的符号:

    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'
    

    【讨论】:

      【解决方案7】:

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

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

      data[newattribute] = somevalue
      

      用于动态键。

      【讨论】:

      • 如果在运行时才确定属性名称" - 除非您使用 eval,否则这将不起作用,这不是一个好的选择
      • 或者使用 [] 语法... data[somevar] = somevalue
      【解决方案8】:

      除了之前的所有答案,如果您想知道我们将如何使用计算属性名称 (ECMAScript 6) 在 Future 中编写动态属性名称,请参考以下方法:

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

      参考:Understanding ECMAScript 6 - Nickolas Zakas

      【讨论】:

        【解决方案9】:

        只是对上面abeing答案的补充。您可以定义一个函数来封装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

        【讨论】:

          【解决方案10】:

          您可以使用以下一些选项动态添加属性:

          在你的例子中:

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

          您可以通过以下两种方式定义具有动态值的属性:

          data.key = value;
          

          data['key'] = value;
          

          更多..如果您的密钥也是动态的,您可以使用 Object 类定义:

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

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

          我希望这会有所帮助!

          【讨论】:

            【解决方案11】:

            我知道这篇文章已经有几个答案,但我还没有看到一个有多个属性并且它们在一个数组中的答案。顺便说一下,这个解决方案适用于 ES6。

            为了说明,假设我们有一个名为 person 的数组,其中包含对象:

             let Person = [{id:1, Name: "John"}, {id:2, Name: "Susan"}, {id:3, Name: "Jet"}]
            

            因此,您可以添加具有相应值的属性。假设我们要添加默认值为 ENLanguage

            Person.map((obj)=>({...obj,['Language']:"EN"}))
            

            Person 数组现在会变成这样:

            Person = [{id:1, Name: "John", Language:"EN"}, 
            {id:2, Name: "Susan", Language:"EN"}, {id:3, Name: "Jet", Language:"EN"}]
            

            【讨论】:

            • 您实际上并没有向对象添加属性,而是使用旧对象的属性(通过扩展运算符)和新道具创建一个新对象。
            • 你说得对,它应该是Person = Person.map(code here)。但重点是您可以使用ES6 轻松地将属性添加到现有对象。
            【解决方案12】:

            我正在寻找一种解决方案,可以在对象声明中使用动态键名(不使用 ...[key]: value 等 ES6 功能)

            这是我想出的:

            var obj = (obj = {}, obj[field] = 123, obj)
            

            一开始看起来有点复杂,但其实很简单。我们使用逗号运算符连续运行三个命令:

            1. obj = {}:创建一个新对象并将其分配给变量obj
            2. obj[field] = 123:将computed property name 添加到obj
            3. obj:使用obj 变量作为括号/逗号列表的结果

            此语法可以在函数参数中使用,无需显式声明obj 变量:

            // The test function to see the result.
            function showObject(obj) {
                console.log(obj);
            }
            
            // My dynamic field name.
            var field = "myDynamicField";
            
            // Call the function with our dynamic object.
            showObject( (obj = {}, obj[field] = 123, obj) );
            
            /*
            Output:
            
            {
              "myDynamicField": true
            }
            */

            一些变化

            “严格模式”解决方法:

            上面的代码在strict mode中不起作用,因为变量“obj”没有被声明。

            // This gives the same result, but declares the global variable `this.obj`!
            showObject( (this.obj = {}, obj[field] = 123, obj) );
            

            ES2015 在初始化程序中使用计算属性名称的代码:

            // Works in most browsers, same result as the other functions.
            showObject( {[field] = 123} );
            

            此解决方案适用于 all modern browsers(但不适用于 IE,如果我需要提及的话)

            使用JSON.parse()的超级hacky方式:

            // Create a JSON string that is parsed instantly. Not recommended in most cases.
            showObject( JSON.parse( '{"' + field +'":123}') );
            // read: showObject( JSON.parse( '{"myDynamicfield":123}') );
            

            允许键中的特殊字符

            请注意,您还可以在计算属性名称中(以及 JSON.parse 中)使用空格和其他特殊字符。

            var field = 'my dynamic field :)';
            showObject( {[field] = 123} );
            // result: { "my dynamic field :)": 123 }
            

            这些字段不能使用点来访问(obj.my dynamic field :) 显然在语法上是无效的),而只能通过括号表示法,即obj['my dynamic field :)'] 返回123

            【讨论】:

              【解决方案13】:

              如果在运行时添加混合新属性会很有用:

              data = { ...data, newPropery: value}
              

              然而,spread 运算符使用浅拷贝,但这里我们将数据分配给自身,因此不会丢失任何内容

              【讨论】:

                【解决方案14】:

                最简单和最便携的方式是。

                var varFieldName = "good";
                var ob = {};
                Object.defineProperty(ob, varFieldName , { value: "Fresh Value" });
                

                基于#abeing 答案!

                【讨论】:

                  【解决方案15】:

                  使用 .(dot) 方法向现有对象添加属性时要小心

                  (.dot) 仅当您知道 'key' 时才应使用向对象添加属性的方法 提前,否则使用 [bracket] 方法。

                  示例:

                     var data = {
                          'Property1': 1
                      };
                      
                      // Two methods of adding a new property [ key (Property4), value (4) ] to the
                      // existing object (data)
                      data['Property2'] = 2; // bracket method
                      data.Property3 = 3;    // dot method
                      console.log(data);     // { Property1: 1, Property2: 2, Property3: 3 }
                      
                      // But if 'key' of a property is unknown and will be found / calculated
                      // dynamically then use only [bracket] method not a dot method    
                      var key;
                      for(var i = 4; i < 6; ++i) {
                      	key = 'Property' + i;     // Key - dynamically calculated
                      	data[key] = i; // CORRECT !!!!
                      }
                      console.log(data); 
                      // { Property1: 1, Property2: 2, Property3: 3, Property4: 4, Property5: 5 }
                      
                      for(var i = 6; i < 2000; ++i) {
                      	key = 'Property' + i; // Key - dynamically calculated
                      	data.key = i;         // WRONG !!!!!
                      }
                      console.log(data); 
                      // { Property1: 1, Property2: 2, Property3: 3, 
                      //   Property4: 4, Property5: 5, key: 1999 }

                  注意控制台日志末尾的问题 - 'key: 1999' 而不是 Property6: 6, Property7: 7,.........,Property1999: 1999。所以添加动态创建的属性最好的方法是 [bracket] 方法。

                  【讨论】:

                    【解决方案16】:

                    从包含对象的动态字符串名称访问的好方法(例如 object.subobject.property)

                    function ReadValue(varname)
                    {
                        var v=varname.split(".");
                        var o=window;
                        if(!v.length)
                            return undefined;
                        for(var i=0;i<v.length-1;i++)
                            o=o[v[i]];
                        return o[v[v.length-1]];
                    }
                    
                    function AssignValue(varname,value)
                    {
                        var v=varname.split(".");
                        var o=window;
                        if(!v.length)
                            return;
                        for(var i=0;i<v.length-1;i++)
                            o=o[v[i]];
                        o[v[v.length-1]]=value;
                    }
                    

                    例子:

                    ReadValue("object.subobject.property");
                    WriteValue("object.subobject.property",5);
                    

                    eval 适用于读取值,但写入值有点困难。

                    更高级的版本(如果子类不存在则创建子类,并允许使用对象而不是全局变量)

                    function ReadValue(varname,o=window)
                    {
                        if(typeof(varname)==="undefined" || typeof(o)==="undefined" || o===null)
                            return undefined;
                        var v=varname.split(".");
                        if(!v.length)
                            return undefined;
                        for(var i=0;i<v.length-1;i++)
                        {
                            if(o[v[i]]===null || typeof(o[v[i]])==="undefined") 
                                o[v[i]]={};
                            o=o[v[i]];
                        }
                        if(typeof(o[v[v.length-1]])==="undefined")    
                            return undefined;
                        else    
                            return o[v[v.length-1]];
                    }
                    
                    function AssignValue(varname,value,o=window)
                    {
                        if(typeof(varname)==="undefined" || typeof(o)==="undefined" || o===null)
                            return;
                        var v=varname.split(".");
                        if(!v.length)
                            return;
                        for(var i=0;i<v.length-1;i++)
                        {
                            if(o[v[i]]===null || typeof(o[v[i]])==="undefined")
                                o[v[i]]={};
                            o=o[v[i]];
                        }
                        o[v[v.length-1]]=value;
                    }
                    

                    例子:

                    ReadValue("object.subobject.property",o);
                    WriteValue("object.subobject.property",5,o);
                    

                    这个和o.object.subobject.property一样

                    【讨论】:

                    • 正是我要找的,这对于反应 this.setState({dynamic property: value }) 谢谢!
                    【解决方案17】:

                    是的,这是可能的。我已经使用下面的实现实现了。为此,我得到了一个数组作为响应,我希望在一个对象中作为属性列表。

                    response = {
                      "equityMonths": [
                        {
                          "id": 1,
                          "month": "JANUARY",
                          "isEligible": false
                        },
                        {
                          "id": 2,
                          "month": "FEBRUARY",
                          "isEligible": true
                        },
                        {
                          "id": 3,
                          "month": "MARCH",
                          "isEligible": false
                        },
                        {
                          "id": 4,
                          "month": "APRIL",
                          "isEligible": true
                        },
                        {
                          "id": 5,
                          "month": "MAY",
                          "isEligible": false
                        },
                        {
                          "id": 6,
                          "month": "JUNE",
                          "isEligible": true
                        },
                        {
                          "id": 7,
                          "month": "JULY",
                          "isEligible": true
                        },
                        {
                          "id": 8,
                          "month": "AUGUST",
                          "isEligible": false
                        },
                        {
                          "id": 9,
                          "month": "SEPTEMBER",
                          "isEligible": true
                        },
                        {
                          "id": 10,
                          "month": "OCTOBER",
                          "isEligible": false
                        },
                        {
                          "id": 11,
                          "month": "NOVEMBER",
                          "isEligible": true
                        },
                        {
                          "id": 12,
                          "month": "DECEMBER",
                          "isEligible": false
                        }
                      ]
                    }
                    

                    在这里,我希望 equityMonths 作为对象,Jan 到 Dec 它是关键,isEligible 作为值。为此,我们必须使用 Object 类的 defineProperty() 方法,该方法允许将动态属性添加到对象中。

                    为对象动态添加属性的代码。

                    let equityMonth = new Object();
                    
                    response.equityMonths.forEach(element => {
                        Object.defineProperty(equityMonth, element['month'], {
                           value: element['isEligible'],
                           writable: true,
                           enumerable: true,
                           configurable: true
                        });
                    });
                    console.log("DATA : " + JSON.stringify(equityMonth));
                    

                    在上面的代码中,我们有 equityMonths 的数组,我们已将其作为属性转换为对象。

                    输出:

                    DATA : {"JANUARY":false,"FEBRUARY":true,"MARCH":false,"APRIL":true,"MAY":false,"JUNE":true,"JULY":true,"AUGUST":false,"SEPTEMBER":true,"OCTOBER":false,"NOVEMBER":true,"DECEMBER":false}
                    

                    【讨论】:

                      【解决方案18】:

                      这是我解决问题的方法。

                      var obj = {
                      
                      };
                      var field = "someouter.someinner.someValue";
                      var value = 123;
                      
                      function _addField( obj, field, value )
                      {
                          // split the field into tokens
                          var tokens = field.split( '.' );
                      
                          // if there's more than one token, this field is an object
                          if( tokens.length > 1 )
                          {
                              var subObj = tokens[0];
                      
                              // define the object
                              if( obj[ subObj ] !== undefined ) obj[ subObj ] = {};
                      
                              // call addfield again on the embedded object
                              var firstDot = field.indexOf( '.' );
                              _addField( obj[ subObj ], field.substr( firstDot + 1 ), value );
                      
                          }
                          else
                          {
                              // no embedded objects, just field assignment
                              obj[ field ] = value;
                          }
                      }
                      
                      _addField( obj, field, value );
                      _addField(obj, 'simpleString', 'string');
                      
                      console.log( JSON.stringify( obj, null, 2 ) );
                      

                      生成以下对象:

                      {
                        "someouter": {
                          "someinner": {
                            "someValue": 123
                          }
                        },
                        "simpleString": "string"
                      }
                      

                      【讨论】:

                        【解决方案19】:

                        一个完美的简单方法

                        var data = {
                            'PropertyA': 1,
                            'PropertyB': 2,
                            'PropertyC': 3
                        };
                        
                        var newProperty = 'getThisFromUser';
                        data[newProperty] = 4;
                        
                        console.log(data);
                        

                        如果你想将其应用于数据数组(ES6/TS 版本)

                        const data = [
                          { 'PropertyA': 1, 'PropertyB': 2, 'PropertyC': 3 },
                          { 'PropertyA': 11, 'PropertyB': 22, 'PropertyC': 33 }
                        ];
                        
                        const newProperty = 'getThisFromUser';
                        data.map( (d) => d[newProperty] = 4 );
                        
                        console.log(data);
                        

                        【讨论】:

                          【解决方案20】:

                          当然。将其视为字典或关联数组。您可以随时添加。

                          【讨论】:

                          • 光说没用。
                          猜你喜欢
                          • 2020-11-05
                          • 2020-01-18
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2012-12-09
                          • 2012-01-12
                          • 2011-10-05
                          相关资源
                          最近更新 更多