เป็นไปได้หรือไม่ที่จะเพิ่มคุณสมบัติที่ตั้งชื่อแบบไดนามิกไปยังวัตถุ JavaScript


745

ใน 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?

คำตอบ:


1211

ใช่.

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

data["PropertyD"] = 4;

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


142
@thedz: data.PropertyD จำเป็นต้องรู้ชื่อคุณสมบัติซึ่งไม่ได้เป็นแบบไดนามิกเพียงพอ
Georg Schölly

6
+1 เพราะสิ่งนี้ช่วยฉัน แต่ฉันไม่เข้าใจว่าทำไมคุณสมบัติของวัตถุจึงถูกจัดการเหมือนอาเรย์
Ron van der Heijden

9
@Bondye: นั่นเป็นส่วนหนึ่งของการออกแบบจาวาสคริปต์ที่แปลก ในกรณีนี้object["property"]จะไม่เหมือนกับarray[4]ในอดีตไม่ได้สร้างเป็นอาร์เรย์จริง
Georg Schölly

5
มันเป็นเพียงฉันหรือโพสต์นี้ไม่ตอบคำถามในขณะที่รับ upvotes 195? ฉันคิดว่ามันถูกถามถึงวิธีการกำหนดคุณสมบัติที่ไม่รู้จักชื่อจนกว่าจะถูกสร้างขึ้นในรหัส JavaScript
แควนตัส 94 หนัก

20
@ แควนตัส: สมมติว่ามันไม่ตอบโดยตรง แต่ไปจากdata["PropertyD"]ที่จะdata[function_to_get_property_name()]ดูเหมือนเล็กน้อย
Georg Schölly

154

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 ทรัพย์สินไวยากรณ์และแม่แบบตัวอักษร


1
นี่คือสิ่งที่ฉันจำเป็นต้องใช้ในกรณีที่ผมอยากรหัสของฉันสามารถทำงานได้อย่างสมบูรณ์ (ในขณะที่ไม่มีความจำเป็นงบพูดobj[propname]) แต่ฉันสามารถใช้สิ่งนี้กับไวยากรณ์การกระจายวัตถุ
ประสานงาน

2
ไม่ชัดเจนในทันทีว่าข้อมูลโค้ดนี้ทำอะไรกับคนที่ไม่เห็นหรือเข้าใจไวยากรณ์ใหม่ ฉันอยากจะแนะนำให้แก้ไขเพื่อแสดงเอาท์พุท / ทรัพย์สินที่คาดหวังด้วย 'a' const

โดยส่วนตัวฉันคิดว่าวิธี ES5 นั้นสะอาดและง่ายต่อการเข้าใจมากขึ้น:var a = {}; a["dynamic-" + prop] = true;
Jack Giffin

1
@JackGiffin ในบางกรณีใช่ แต่เมื่อทำงานกับโครงสร้างที่ไม่เปลี่ยนรูป, aรูปแบบนี้จะมีประโยชน์มากเนื่องจากวิธีการที่คุณแสดงให้เห็นว่าเป็นกรรมวิธี (พิเศษเมื่อใช้แพคเกจเช่น redux)
เมาริซิโอ Soares

3
นี่เป็นเพียงยอดเยี่ยม {... รัฐ [prop]: val}
Xipo

87

ใช่มันเป็นไปได้ สมมติว่า:

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

40
การใช้ eval นั้นอันตรายจริงๆ
Georg Schölly

10
ไม่พูดถึงช้า
Eamon Nerbonne

@ GeorgSchölly True
Obinna Nwakwue

1
หมายเหตุ (ในกรณีที่มีคนพบปัญหาเดียวกันกับที่ฉันทำ): สำหรับวัตถุปกติใช้งานได้ดี แต่ฉันต้องเพิ่มคุณสมบัติบางอย่างในวัตถุ jQuery UI เพื่อติดตามรายการ ในกรณีนั้นคุณสมบัติจะหายไปหากเพิ่มวิธีนี้เนื่องจาก jQuery สร้างสำเนาเสมอ ที่นี่คุณต้องใช้jQuery.extend ()
Matt

ฉันชอบที่จะเพิ่มความคิดเห็นก่อนหน้าของฉัน - แม้จะไม่ได้ทำงานในกรณีของฉัน ดังนั้นฉันจึงใช้การ$("#mySelector").data("propertyname", myvalue);ตั้งค่าและvar myValue=$("#mySelector").data("propertyname");รับค่ากลับมา แม้แต่วัตถุที่ซับซ้อน (รายการ, อาร์เรย์ ... ) สามารถเพิ่มด้วยวิธีนี้
Matt

61

ฉันรู้ว่าคำถามตอบได้อย่างสมบูรณ์แบบ แต่ฉันก็พบวิธีอื่นในการเพิ่มคุณสมบัติใหม่และต้องการแบ่งปันกับคุณ:

คุณสามารถใช้ฟังก์ชั่น 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

4
ข้อดีข้อเสียของวิธีนี้?
เทรเวอร์

6
@Trevor: การกำหนดค่าทั้งหมดและความสามารถในการเพิ่มตัวเลือกและตัวตั้งค่า; นอกจากนี้ความสามารถในการเพิ่มคุณสมบัติหลายรายการพร้อมกันด้วยdefineProperties(พหูพจน์)
rvighne

@ Thielicious Object.definePropertyไม่ได้หมายถึงเป็นเครื่องมืออำนวยความสะดวกง่าย ๆ แต่เป็นเครื่องมือที่มีการควบคุมที่ละเอียด หากคุณไม่ต้องการการควบคุมเพิ่มเติมนั่นไม่ใช่เครื่องมือที่เหมาะสมในการเลือก
kleinfreund

Object.defineProperty(obj, prop, valueDescriptor)เป็นจำนวนมากช้าและยากสำหรับ V8 เพื่อเพิ่มประสิทธิภาพมากกว่าเพียงแค่การทำobj[prop] = value;
Jack Giffin


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'

18

คุณสามารถเพิ่มคุณสมบัติได้มากเท่าที่คุณต้องการโดยใช้เครื่องหมายจุด:

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

หรือ :

data[newattribute] = somevalue

สำหรับปุ่มแบบไดนามิก


4
หากชื่อคุณสมบัติไม่ได้ถูกกำหนดจนกว่าจะถึงเวลาใช้งาน "- ดังนั้นจะไม่ทำงานจนกว่าคุณจะใช้ eval ซึ่งไม่ใช่ตัวเลือกที่ดี
Marc Gravell

1
หรือใช้ไวยากรณ์ [] ... data [somevar] = somevalue
Gabriel Hurley

17

นอกเหนือจากคำตอบก่อนหน้าทั้งหมดและในกรณีที่คุณสงสัยว่าเราจะเขียนชื่อคุณสมบัติแบบไดนามิกในอนาคตโดยใช้ชื่อคุณสมบัติที่คำนวณ (ECMAScript 6) ได้อย่างไร:

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

personIndex[ personId ]; // "John Doe"

อ้างอิง: การทำความเข้าใจ ECMAScript 6 - Nickolas Zakas


11

เป็นเพียงการเพิ่มคำตอบของ 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


9

คุณสามารถเพิ่มคุณสมบัติแบบไดนามิกโดยใช้ตัวเลือกบางตัวด้านล่าง:

ในตัวอย่างของคุณ:

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

คุณสามารถกำหนดคุณสมบัติด้วยค่าแบบไดนามิกในอีกสองวิธี:

data.key = value;

หรือ

data['key'] = value;

เพิ่มเติม .. ถ้ากุญแจของคุณเป็นแบบไดนามิกคุณสามารถกำหนดโดยใช้คลาส Object ด้วย:

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

โดยที่dataคือวัตถุของคุณkeyคือตัวแปรที่ใช้เก็บชื่อคีย์และค่าเป็นตัวแปรในการเก็บค่า

ฉันหวังว่านี่จะช่วยได้!


8

ฉันรู้ว่ามีคำตอบหลายข้อสำหรับโพสต์นี้แล้ว แต่ฉันไม่เห็นคำตอบที่มีคุณสมบัติหลายอย่างและอยู่ในอาร์เรย์ และวิธีแก้ปัญหานี้สำหรับ ES6

สำหรับภาพประกอบสมมติว่าเรามีอาร์เรย์ชื่อบุคคลที่มีวัตถุอยู่ภายใน:

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

ดังนั้นคุณสามารถเพิ่มคุณสมบัติที่มีค่าที่สอดคล้องกัน สมมติว่าเราต้องการที่จะเพิ่มภาษามีค่าเริ่มต้นของEN

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

คนอาร์เรย์ตอนนี้จะกลายเป็นเช่นนี้

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

คุณไม่ได้เพิ่มคุณสมบัติให้กับวัตถุจริงๆคุณกำลังสร้างวัตถุใหม่ด้วยคุณสมบัติของวัตถุเก่า (ผ่านตัวดำเนินการแพร่กระจาย) และอุปกรณ์ประกอบฉากใหม่เช่นกัน
Ed Orsi

3
Person = Person.map(code here)คุณขวาบนว่ามันควรจะได้รับ ES6แต่ประเด็นก็คือคุณสามารถเพิ่มคุณสมบัติการวัตถุที่มีอยู่ได้อย่างง่ายดายด้วย
Edper

5

วิธีที่ง่ายและสะดวกที่สุดคือ

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

ตามคำตอบ #abeing!


3

โปรดใช้ความระมัดระวังขณะที่การเพิ่มคุณสมบัติที่จะวัตถุที่มีอยู่โดยใช้. (จุด)วิธีการ

(.dot)วิธีการเพิ่มสถานที่ให้บริการไปยังวัตถุที่ควรจะใช้เฉพาะในกรณีที่คุณรู้'key'ก่อนมิฉะนั้นใช้[วงเล็บ]วิธี

ตัวอย่าง:

   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 }

หมายเหตุปัญหาในตอนท้ายของบันทึกคอนโซล - 'สำคัญ: 1999'แทนProperty6: 6, Property7: 7 ......... , Property1999: 1999 วิธีที่ดีที่สุดในการเพิ่มคุณสมบัติที่สร้างขึ้นแบบไดนามิกคือวิธี [วงเล็บ]


1

วิธีที่ดีในการเข้าถึงจากชื่อสตริงแบบไดนามิกที่มีวัตถุ (เช่น 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


1
สิ่งที่ฉันกำลังมองหามันมีประโยชน์สำหรับการตอบสนอง this.setState ({คุณสมบัติแบบไดนามิก: ค่า}) ขอบคุณ!
เควิน Danikowski

0

นี่คือวิธีที่ฉันแก้ปัญหา

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"
}

0

มันจะมีประโยชน์ถ้าเพิ่มคุณสมบัติใหม่แบบผสมใน runtime:

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

อย่างไรก็ตามผู้ประกอบการแพร่กระจายใช้คัดลอกตื้น แต่ที่นี่เรากำหนดข้อมูลให้กับตัวเองดังนั้นควรจะสูญเสียอะไร


-2

วิธีง่ายๆที่สมบูรณ์แบบ

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

-14

อย่างแน่นอน. คิดว่ามันเป็นพจนานุกรมหรืออาเรย์ คุณสามารถเพิ่มเข้าไปได้ทุกที่


1
เพียงแค่บอกว่าจะไม่ช่วย
Obinna Nwakwue
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.