javascript - Åtkomst till objektets barnegenskaper med hjälp av en sträng i punktnotation


Jag sitter tillfälligt fast med vad som verkar vara ett mycket enkelt JavaScript-problem, men kanske saknar jag bara rätt sökord!

Säg att vi har ett objekt

var r = { a:1, b: {b1:11, b2: 99}};

Det finns flera sätt att få tillgång till 99:

r.b.b2
r['b']['b2']

Det jag vill är att kunna definiera en sträng

var s = "b.b2";

och sedan komma åt 99 med

r.s or r[s] //(which of course won't work)

Ett sätt är att skriva en funktion för det som delar upp strängen på punkt och kanske rekursivt/iterativt hämtar egenskapen. Men finns det något enklare/mer effektivt sätt? Finns det något användbart i någon av jQuery APIs här?

Svar #1

Här är en naiv funktion jag skrev för ett tag sedan, men den fungerar för grundläggande objekt-egenskaper:

function getDescendantProp(obj, desc) {
    var arr = desc.split(".");
    while(arr.length && (obj = obj[arr.shift()]));
    return obj;
}

console.log(getDescendantProp(r, "b.b2"));
//-> 99

Även om det finns svar som utvidgar detta till att "tillåta" åtkomst till array-index, är det egentligen inte nödvändigt eftersom du bara kan specificera numeriska index med punktnotation med denna metod:

getDescendantProp({ a: [ 1, 2, 3 ] }, 'a.2');
//-> 3

Svar #2

split och reduce medan objektet skickas som initalValue

Uppdatering (tack till kommentaren från TeChn4K)

Med ES6-syntax är det ännu kortare

var r = { a:1, b: {b1:11, b2: 99}};
var s = "b.b2";

var value = s.split('.').reduce((a, b) => a[b], r);

console.log(value);

Gammal version

var r = { a:1, b: {b1:11, b2: 99}};
var s = "b.b2";

var value = s.split('.').reduce(function(a, b) {
  return a[b];
}, r);

console.log(value);

Svar #3

Du kan använda lodash get() och set() metoder.

Får

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

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

Inställning

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

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

Svar #4

Om det är möjligt i ditt scenario att du kan sätta hela array-variabeln du är ute efter i en sträng, kan du använda eval()-funktionen.

var r = { a:1, b: {b1:11, b2: 99}};
var s = "r.b.b2";
alert(eval(s)); // 99

Jag kan känna människor rygga tillbaka i skräck

Svar #5

Förlänger @JohnB:s svar, jag lade också till ett inställningsvärde. Kolla in plunkern på

http://plnkr.co/edit/lo0thC?p=preview

function getSetDescendantProp(obj, desc, value) {
  var arr = desc ? desc.split(".") : [];

  while (arr.length && obj) {
    var comp = arr.shift();
    var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(comp);

    // handle arrays
    if ((match !== null) && (match.length == 3)) {
      var arrayData = {
        arrName: match[1],
        arrIndex: match[2]
      };
      if (obj[arrayData.arrName] !== undefined) {
        if (typeof value !== 'undefined' && arr.length === 0) {
          obj[arrayData.arrName][arrayData.arrIndex] = value;
        }
        obj = obj[arrayData.arrName][arrayData.arrIndex];
      } else {
        obj = undefined;
      }

      continue;
    }

    // handle regular things
    if (typeof value !== 'undefined') {
      if (obj[comp] === undefined) {
        obj[comp] = {};
      }

      if (arr.length === 0) {
        obj[comp] = value;
      }
    }

    obj = obj[comp];
  }

  return obj;
}

Relaterade artiklar