เข้าถึงคุณสมบัติวัตถุแบบไดนามิกโดยใช้ตัวแปร


721

ฉันกำลังพยายามเข้าถึงคุณสมบัติของวัตถุโดยใช้ชื่อแบบไดนามิก เป็นไปได้ไหม

const something = { bar: "Foobar!" };
const foo = 'bar';
something.foo; // The idea is to access something.bar, getting "Foobar!"

คำตอบ:


958

มีสองวิธีในการเข้าถึงคุณสมบัติของวัตถุ:

  • เครื่องหมายจุด: something.bar
  • เครื่องหมายวงเล็บ: something['bar']

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

var foo = 'bar';
something[foo];
// both x = something[foo] and something[foo] = x work as expected

34
ระวังด้วยสิ่งนี้: คอมไพเลอร์ javascript จะเกิดข้อผิดพลาดที่นี่เนื่องจากพวกเขาไม่เปลี่ยนชื่อสตริง แต่พวกเขาเปลี่ยนชื่อคุณสมบัติของวัตถุ
chacham15

6
ข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่เป็นไปได้: วัตถุ JS เป็นอาร์เรย์เชื่อมโยงนั่นคือเหตุผล อ่านเพิ่มเติม: quirksmode.org/js/associative.html stackoverflow.com/questions/14031368/ …
Sudhanshu Mishra

@dnetnet ไม่พวกเขาไม่ใช่ อาร์เรย์เป็นวัตถุที่สืบทอดมาจากต้นแบบวัตถุ JS ธรรมดาและดังนั้นคุณสามารถเพิ่มคุณสมบัติแบบ go-go เหมือนวัตถุธรรมดาใด ๆ พฤติกรรม 'เชื่อมโยง' นั้นมีลักษณะคล้ายวัตถุมากกว่าอาเรย์ คุณไม่สามารถทำซ้ำรุ่น 'เชื่อมโยง' ด้วยดัชนีอย่างง่ายดังนั้นมันจึงไม่แสดงพฤติกรรมแบบอาเรย์ คุณสามารถกำหนดอาเรย์ 'เชื่อมโยง' ของคุณเป็น {} หรือ [] และปฏิบัติเหมือนกันในกรณีใดก็ตามที่เกี่ยวข้องกับการเข้าถึงคุณสมบัติแบบสุ่ม
วอมแบต Wombat

3
@VanquishedWombat ไม่แน่ใจว่าการคัดค้านของคุณเกี่ยวข้องกับอะไร? ฉันไม่ได้บอกว่า JS Objects เป็นอาร์เรย์?
Sudhanshu Mishra

1
คำตอบนี้เป็นวิธีการลงคะแนนมากเกินไปมันไม่มีความชัดเจน ...
Aft3rL1f3

104

นี่คือทางออกของฉัน:

function resolve(path, obj) {
    return path.split('.').reduce(function(prev, curr) {
        return prev ? prev[curr] : null
    }, obj || self)
}

ตัวอย่างการใช้งาน:

resolve("document.body.style.width")
// or
resolve("style.width", document.body)
// or even use array indexes
// (someObject has been defined in the question)
resolve("part.0.size", someObject) 
// returns null when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})

คำตอบที่ยอดเยี่ยมดูเพิ่มเติมได้ที่: stackoverflow.com/questions/37510640/ …
Julian Knight

2
คุณเป็นแรงบันดาลใจให้ฉันสร้างเวอร์ชันขั้นสูงที่อนุญาตให้มีเครื่องหมายวงเล็บและชื่อคุณสมบัติพร้อมช่องว่างและตรวจสอบอินพุต: it.knightnet.org.uk/kb/node-js/get-properties
Julian Knight

ฉันรักทางออกนี้ อย่างไรก็ตามฉันพยายามแก้ไขค่าในวัตถุต้นฉบับดูเหมือนว่าฟังก์ชั่นของคุณจะส่งคืนสำเนาย่อยของวัตถุ เป็นไปได้หรือไม่ที่จะเปลี่ยนแปลงเพื่อให้การดัดแปลงออบเจกต์ที่ส่งคืนแก้ไขต้นฉบับ?
Eagle1

40

ในจาวาสคริปต์เราสามารถเข้าถึงด้วย:

  • เครื่องหมายจุด - foo.bar
  • วงเล็บเหลี่ยม - foo[someVar]หรือfoo["string"]

แต่เฉพาะกรณีที่สองเท่านั้นที่อนุญาตให้เข้าถึงคุณสมบัติแบบไดนามิก:

var foo = { pName1 : 1, pName2 : [1, {foo : bar }, 3] , ...}

var name = "pName"
var num  = 1;

foo[name + num]; // 1

// -- 

var a = 2;
var b = 1;
var c = "foo";

foo[name + a][b][c]; // bar

4
ฉันกำลังดูข้อความสั่ง 2,000 บรรทัดเพราะ dev ก่อนหน้านี้ไม่ได้ใช้วงเล็บเหลี่ยมและคุณสมบัติวัตถุที่เข้าถึงแบบคงที่โดยใช้เครื่องหมายจุด สำหรับแอปกระบวนการอนุมัติที่มีผู้อนุมัติ 7 คนที่แตกต่างกันและขั้นตอนเหมือนกันทั้งหมด / ฉีก
ชาด

26

ต่อไปนี้เป็นตัวอย่าง ES6 ของวิธีที่คุณสามารถเข้าถึงคุณสมบัติของวัตถุโดยใช้ชื่อคุณสมบัติที่สร้างขึ้นแบบไดนามิกโดยเชื่อมสองสายเข้าด้วยกัน

var suffix = " name";

var person = {
    ["first" + suffix]: "Nicholas",
    ["last" + suffix]: "Zakas"
};

console.log(person["first name"]);      // "Nicholas"
console.log(person["last name"]);       // "Zakas"

สิ่งนี้เรียกว่าชื่อคุณสมบัติที่คำนวณได้


16

คุณสามารถทำสิ่งนี้ได้หลายวิธี

let foo = {
    bar: 'Hello World'
};

foo.bar;
foo['bar'];

เครื่องหมายวงเล็บมีประสิทธิภาพเป็นพิเศษเพราะช่วยให้คุณเข้าถึงคุณสมบัติตามตัวแปร:

let foo = {
    bar: 'Hello World'
};

let prop = 'bar';

foo[prop];

สิ่งนี้สามารถขยายเพื่อวนลูปมากกว่าทุกคุณสมบัติของวัตถุ สิ่งนี้อาจดูเหมือนซ้ำซ้อนเนื่องจากโครงสร้าง JavaScript ที่ใหม่กว่าเช่น ... ของ ... แต่ช่วยอธิบายกรณีการใช้งาน:

let foo = {
    bar: 'Hello World',
    baz: 'How are you doing?',
    last: 'Quite alright'
};

for (let prop in foo.getOwnPropertyNames()) {
    console.log(foo[prop]);
}

ทั้งจุดและเครื่องหมายวงเล็บยังทำงานตามที่คาดไว้สำหรับวัตถุที่ซ้อนกัน:

let foo = {
    bar: {
        baz: 'Hello World'
    }
};

foo.bar.baz;
foo['bar']['baz'];
foo.bar['baz'];
foo['bar'].baz;

การทำลายวัตถุ

นอกจากนี้เรายังสามารถพิจารณาการทำลายวัตถุเป็นวิธีการเข้าถึงคุณสมบัติในวัตถุ แต่เป็นดังนี้:

let foo = {
    bar: 'Hello World',
    baz: 'How are you doing?',
    last: 'Quite alright'
};

let prop = 'last';
let { bar, baz, [prop]: customName } = foo;

// bar = 'Hello World'
// baz = 'How are you doing?'
// customName = 'Quite alright'

11

คุณสามารถทำได้เช่นนี้โดยใช้ Lodash

_.get(object, 'a[0].b.c');

มีหลายสถานการณ์เช่นการค้นหาวัตถุที่ซ้อนกันลึกซึ่งนี่เป็นตัวเลือกเดียว
Jonathan Kempf

8

ปรับปรุง

ฉันได้นำความคิดเห็นด้านล่างมาพิจารณาและตกลงกัน หลีกเลี่ยงการใช้ Eval

การเข้าถึงคุณสมบัติรูทในวัตถุสามารถทำได้อย่างง่ายดายobj[variable]แต่การซ้อนสิ่งที่ซับซ้อน lodash.getไม่ต้องเขียนโค้ดเขียนแล้วฉันขอแนะนำให้ใช้

ตัวอย่าง

// Accessing root property
var rootProp = 'rootPropert';
_.get(object, rootProp, defaultValue);

// Accessing nested property
var listOfNestedProperties = [var1, var2];
_.get(object, listOfNestedProperties);

สามารถใช้งาน Lodash ได้หลายวิธีต่อไปนี้คือลิงก์ไปยังเอกสารคู่มือ lodash.get


4
ทางที่ดีควรหลีกเลี่ยงการใช้งานevalทุกครั้งที่ทำได้ stackoverflow.com/questions/86513/…
Luke

8
การใช้evalบางอย่างที่ไม่สำคัญเท่ากับการเข้าถึงคุณสมบัตินั้นเป็นเรื่องธรรมดาและไม่แนะนำให้ใช้ในทุกสถานการณ์ "ปัญหา" คืออะไร obj['nested']['test']ทำงานได้ดีมากและไม่ต้องการให้คุณฝังโค้ดในสตริง
Kyll

3
eval ช้ากว่าสามเท่าหรือมากกว่าฉันจะไม่แนะนำสิ่งนี้ให้กับมือใหม่เพราะมันอาจสอนนิสัยที่ไม่ดีให้กับพวกเขาได้ ฉันใช้obj['nested']['value']- จำเด็ก ๆ , eval เป็นความชั่วร้าย!
jaggedsoft

1
@ ลุคตอนนี้เขาเป็นเพียงคนเดียวที่ต้องการนำ Lodash มา_.getไว้บนโต๊ะ ฉันคิดว่าคำตอบนี้สมควรได้รับ upvotes ตอนนี้แทนที่จะ downvotes อาจจะเกินกำลัง แต่ก็ดีที่จะรู้ว่ามันมีอยู่จริง
Emile Bergeron

1
ขอบคุณสำหรับการแนะนำ lodash สำหรับสิ่งนี้ ฉันมาที่นี่โดย google มองหาวิธีการตั้งค่าลึกลงไปในวัตถุและใช้วิธี _.set ของพวกเขา (ซึ่งเหมือนกันกับข้างต้น แต่ด้วยการโต้แย้งพิเศษสำหรับค่าที่จะตั้งค่า)
TPHughes

5

เมื่อใดก็ตามที่คุณต้องการเข้าถึงคุณสมบัติแบบไดนามิกคุณจะต้องใช้ตัวยึดสี่เหลี่ยมเพื่อเข้าถึงคุณสมบัติที่ไม่ใช่ "" ไวยากรณ์ของโอเปอเรเตอร์
: object [propery}

const something = { bar: "Foobar!" };
const foo = 'bar';
// something.foo; -- not correct way at it is expecting foo as proprty in  something={ foo: "value"};
// correct way is  something[foo]
alert( something[foo])


4

ฉันเจอกรณีที่ฉันคิดฉันต้องการส่ง "ที่อยู่" ของคุณสมบัติวัตถุเป็นข้อมูลไปยังฟังก์ชันอื่นและเติมวัตถุ (ด้วย AJAX) ทำการค้นหาจากอาร์เรย์ที่อยู่และแสดงในฟังก์ชันอื่นนั้น ฉันไม่สามารถใช้เครื่องหมายจุดได้โดยไม่ต้องใช้สายกายกรรมดังนั้นฉันจึงคิดว่าอาร์เรย์น่าจะผ่านได้ดี ฉันลงเอยด้วยการทำสิ่งที่แตกต่างกันอยู่ดี

นี่คือตัวอย่างของวัตถุไฟล์ภาษาเช่นเดียวกับที่ฉันต้องการข้อมูลจาก:

const locs = {
  "audioPlayer": {
    "controls": {
      "start": "start",
      "stop": "stop"
    },
    "heading": "Use controls to start and stop audio."
  }
}

ฉันต้องการผ่านอาร์เรย์เช่น: ["audioPlayer", "controls", "stop"] เพื่อเข้าถึงข้อความภาษา "หยุด" ในกรณีนี้

ฉันสร้างฟังก์ชั่นเล็ก ๆ นี้ที่ค้นหาพารามิเตอร์ที่อยู่ "น้อยที่สุด" (ครั้งแรก) และมอบหมายวัตถุที่ส่งคืนให้กับตัวเองอีกครั้ง จากนั้นพร้อมที่จะค้นหาพารามิเตอร์ที่อยู่เฉพาะถัดไปหากมีอยู่

function getText(selectionArray, obj) {
  selectionArray.forEach(key => {
    obj = obj[key];
  });
  return obj;
}

การใช้งาน:

/* returns 'stop' */
console.log(getText(["audioPlayer", "controls", "stop"], locs)); 

/* returns 'use controls to start and stop audio.' */
console.log(getText(["audioPlayer", "heading"], locs)); 

2

มันน่าสนใจเมื่อคุณต้องส่งพารามิเตอร์ไปยังฟังก์ชั่นนี้เช่นกัน

รหัส jsfiddle

var obj = {method:function(p1,p2,p3){console.log("method:",arguments)}}

var str = "method('p1', 'p2', 'p3');"

var match = str.match(/^\s*(\S+)\((.*)\);\s*$/);

var func = match[1]
var parameters = match[2].split(',');
for(var i = 0; i < parameters.length; ++i) {
  // clean up param begninning
    parameters[i] = parameters[i].replace(/^\s*['"]?/,'');
  // clean up param end
  parameters[i] = parameters[i].replace(/['"]?\s*$/,'');
}

obj[func](parameters); // sends parameters as array
obj[func].apply(this, parameters); // sends parameters as individual values

1

ES5 // ตรวจสอบตัวแปรที่ซ้อนกันอย่างลึกซึ้ง

โค้ดง่าย ๆ นี้สามารถตรวจสอบการมีอยู่ของตัวแปร / ค่าที่ซ้อนกันได้โดยไม่ต้องตรวจสอบแต่ละตัวแปรตลอดทาง ...

var getValue = function( s, context ){
    return Function.call( context || null, 'return ' + s )();
}

อดีต - อาร์เรย์ของวัตถุที่ซ้อนกันอย่างล้ำลึก:

a = [ 
    {
      b : [
          {
             a : 1,
             b : [
                 {
                    c : 1,
                    d : 2   // we want to check for this
                 }
             ]
           }
      ]
    } 
]

แทน :

if(a && a[0] && a[0].b && a[0].b[0] && a[0].b[0].b && a[0].b[0].b[0] && a[0].b[0].b[0].d && a[0].b[0].b[0].d == 2 )  // true

ตอนนี้เราสามารถ:

if( getValue('a[0].b[0].b[0].d') == 2 ) // true

ไชโย!


1
หากวิธีแก้ปัญหาคือใช้ eval คุณเพิ่งสร้างปัญหาอื่นอีกหลายล้าน
Rodrigo Leite

@RodrigoLeite ตกลงดังนั้นมันจะไม่เป็นปัญหาที่จะให้อย่างน้อยหนึ่ง ...

ที่นี่คุณจะไป :) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...... !
Rodrigo Leite

1
@RodrigoLeite ฉันได้อ่านและอัปเดตโซลูชันที่จะใช้Functionแทน

0

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

  • ประการแรกฉันต้องการที่อยู่ว่ามีหลายวิธีในการรับค่าของคุณสมบัติและเก็บไว้ในตัวแปรแบบไดนามิก วิธีแรกที่ได้รับความนิยมและง่ายที่สุด IMHO คือ:
let properyValue = element.style['enter-a-property'];

แต่ฉันไม่ค่อยไปเส้นทางนี้เพราะมันใช้ไม่ได้กับค่าคุณสมบัติที่กำหนดผ่านสไตล์ชีท เพื่อให้ตัวอย่างแก่คุณฉันจะสาธิตด้วยโค้ดหลอก

 let elem = document.getElementById('someDiv');
 let cssProp = elem.style['width'];

ใช้ตัวอย่างรหัสด้านบน หากคุณสมบัติความกว้างขององค์ประกอบ div ที่เก็บไว้ในตัวแปร 'elem' ถูกจัดรูปแบบในสไตล์ชีต CSS และไม่ได้เรียกใช้ภายในแท็ก HTML คุณไม่ต้องสงสัยเลยว่าจะได้รับค่าตอบแทนที่เก็บไว้ภายใน ของตัวแปร cssProp ค่าที่ไม่ได้กำหนดเกิดขึ้นเนื่องจากในการสั่งซื้อเพื่อรับค่าที่ถูกต้องรหัสที่เขียนภายใน CSS Style-Sheet จะต้องมีการคำนวณตามลำดับเพื่อให้ได้ค่าดังนั้น; คุณต้องใช้วิธีการที่จะคำนวณมูลค่าของทรัพย์สินที่มีค่าอยู่ในสไตล์ชีท

  • ต่อจากนี้ไปวิธี getComputedStyle ()!
function getCssProp(){
  let ele = document.getElementById("test");
  let cssProp = window.getComputedStyle(ele,null).getPropertyValue("width");
}

W3Schools getComputedValue Docนี่เป็นตัวอย่างที่ดีและให้คุณเล่นกับมันได้อย่างไรก็ตามลิงก์นี้Mozilla CSS getComputedValue docพูดถึงรายละเอียดเกี่ยวกับฟังก์ชั่น getComputedValue และควรอ่านโดยนักพัฒนาที่ต้องการไม่ชัดเจนในเรื่องนี้

  • ในฐานะที่เป็นบันทึกย่อด้านวิธี getComputedValue ได้รับเท่านั้นมันไม่ได้ตั้ง เห็นได้ชัดว่านี่เป็นข้อเสียที่สำคัญ แต่มีวิธีการที่ได้รับจาก CSS สไตล์ชีทเช่นเดียวกับการตั้งค่าแม้ว่ามันจะไม่ใช่จาวาสคริปต์มาตรฐาน วิธีการ JQuery ...
$(selector).css(property,value)

... ได้รับและตั้งค่า มันเป็นสิ่งที่ฉันใช้ข้อเสียเพียงอย่างเดียวคือคุณต้องรู้จัก JQuery แต่นี่เป็นหนึ่งในเหตุผลที่ดีมากที่นักพัฒนา Javascript ทุกคนควรเรียนรู้ JQuery มันทำให้ชีวิตง่ายขึ้นและมีวิธีการเช่นนี้ ไม่สามารถใช้ได้กับ Javascript มาตรฐาน หวังว่านี่จะช่วยให้ใครบางคน !!!


0

สำหรับทุกคนที่ต้องการกำหนดค่าของตัวแปรที่ซ้อนกันนี่คือวิธีการ:

const _ = require('lodash'); //import lodash module

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4

เอกสารประกอบ: https://lodash.com/docs/4.17.15#set

นอกจากนี้ยังมีเอกสารประกอบหากคุณต้องการรับค่า: https://lodash.com/docs/4.17.15#get



-4
const something = { bar: "Foobar!" };
const foo = 'bar';

something[\`${foo}\`];

7
คุณจะทำอย่างนั้นทำไมบนโลก? ของคุณfooที่มีอยู่แล้วสตริงเพื่อให้เป็นเหมือนกับ`${foo}` foo(นอกจากนี้รหัสของคุณดูเหมือนว่าจะมีเครื่องหมายพิเศษบางอย่างที่ทำไม่ได้อยู่ที่นั่น แต่ก็ยังจะไม่มีจุดหมายแม้ว่าคุณจะได้รับการแก้ไขข้อผิดพลาดทางไวยากรณ์ที่..)
Ilmari Karonen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.