Динамический доступ к свойству объекта с помощью переменной

722

Я пытаюсь получить доступ к свойству объекта, используя динамическое имя. Это возможно?

const something = { bar: "Foobar!" };
const foo = 'bar';
something.foo; // The idea is to access something.bar, getting "Foobar!"
RichW
источник

Ответы:

960

Есть два способа получить доступ к свойствам объекта:

  • Точечная запись: something.bar
  • Обозначение в скобках: something['bar']

Значение в скобках может быть любым выражением. Следовательно, если имя свойства хранится в переменной, вы должны использовать скобочные обозначения:

var foo = 'bar';
something[foo];
// both x = something[foo] and something[foo] = x work as expected
Ян Ханчич
источник
34
будьте осторожны с этим: компиляторы javascript будут
ошибаться,
6
Еще немного информации о том, почему это возможно: объекты JS являются ассоциативными массивами, поэтому. Дополнительная литература: quirksmode.org/js/associative.html stackoverflow.com/questions/14031368/…
Судханшу Мишра
@dotnetguy Нет, это не так. Массивы - это объекты, которые наследуются от простого прототипа JS-объекта, и поэтому вы можете добавить свойства на ходу, как любой простой объект. «Ассоциативное» поведение больше похоже на объект, чем на массив. Вы не можете перебрать «ассоциативную» версию по простому индексу, чтобы она не отображала поведение, подобное массиву. Вы можете определить свой «ассоциативный» массив как {} или [] и обрабатывать его одинаково в любом случае, если речь идет о случайном доступе к свойству.
Вомбат
3
@VanquishedWombat Не знаете, к чему относится ваше возражение? Я не говорил, что объекты JS являются массивами?
Судханшу Мишра
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'})
abahet
источник
Отличный ответ, см. Также: stackoverflow.com/questions/37510640/…
Джулиан Найт
2
Вы вдохновили меня на создание расширенной версии, позволяющей использовать обозначения в скобках и имена свойств с пробелами, а также проверять вводимые
Джулиан Найт,
Я люблю это решение. Однако я пытаюсь изменить значения в исходном объекте, похоже, ваша функция возвращает копию объекта. Можно ли изменить его так, чтобы модификация возвращаемого объекта изменяла оригинал?
Eagle1
40

В javascript мы можем получить доступ с:

  • точечная нотация - 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
Sonique
источник
4
Я смотрю на 2000 строк операторов if, потому что предыдущий разработчик не использовал квадратные скобки и статически обращался к свойствам объекта с помощью точечной нотации. Это приложение для процесса утверждения, в котором есть 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"

Это называется вычисляемым именем свойства

zloctb
источник
16

Вы можете достичь этого несколькими различными способами.

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

foo.bar;
foo['bar'];

Скобка нотации особенно мощная, так как она позволяет вам получить доступ к свойству на основе переменной:

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

let prop = 'bar';

foo[prop];

Это может быть расширено зацикливанием на каждом свойстве объекта. Это может показаться излишним из-за более новых конструкций JavaScript, таких как for ... of ..., но помогает проиллюстрировать пример использования:

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

_.get(object, 'a[0].b.c');
shalonteoh
источник
Существует много ситуаций, таких как глубокий поиск вложенных объектов, где это единственный вариант.
Джонатан Кемпф
8

ОБНОВЛЕНО

Я принял во внимание комментарии ниже и согласился. Эвала следует избегать.

Доступ к корневым свойствам в объекте легко достигается с помощью 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 get можно использовать по-разному, вот ссылка на документацию lodash.get

Мистер Бр
источник
4
Лучше избегать использования по evalвозможности. stackoverflow.com/questions/86513/…
Лука
8
Использование evalчего-то столь же тривиального, как доступ к свойствам, является излишним излишним и вряд ли целесообразным при любых обстоятельствах. Что за "беда"?obj['nested']['test']работает очень хорошо и не требует от вас встраивания кода в строки.
Kyll
3
eval работает в три раза медленнее или больше, я бы не советовал это новичкам, потому что это может научить их вредным привычкам. Я использую obj['nested']['value']- помните детей, Eval это зло!
Jaggedsoft
1
@Luke Теперь он единственный, кто хочет поднести Лодаша _.getк столу. Я думаю, что этот ответ заслуживает теперь положительных, а не отрицательных голосов. Это может быть излишним, но хорошо знать, что оно существует.
Эмиль Бержерон
1
Спасибо за введение lodash для этого. Я пришел сюда через Google в поисках метода для установки значения глубоко в объекте и использовал их метод _.set (который идентичен описанному выше, но с дополнительным аргументом для значения, которое нужно установить).
Т.П. Хьюз
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»] для доступа к тексту языка, в этом случае «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
Jacksonkr
источник
1

ES5 // Проверка глубоко вложенных переменных

Этот простой кусок кода может проверить наличие глубоко вложенных переменных / значений без необходимости проверять каждую переменную по пути ...

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

Ex. - глубоко вложенный массив объектов:

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, вы только что создали миллион других проблем.
Родриго Лейте
@RodrigoLeite хорошо, так что не было бы проблем дать хотя бы один ...
Вот и все :) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… !
Родриго Лейте
1
@RodrigoLeite Я прочитал его и обновил решение для использования Function вместо него
0

Я задал вопрос, который как бы дублировал эту тему некоторое время назад, и после чрезмерного исследования, и, увидев, что здесь не хватает большого количества информации, я чувствую, что могу добавить кое-что ценное в этот старый пост.

  • Во-первых, я хочу обратиться к тому, что есть несколько способов получить значение свойства и сохранить его в динамической переменной. Первым самым популярным и простым способом ИМХО будет:
let properyValue = element.style['enter-a-property'];

однако я редко иду по этому пути, потому что он не работает со значениями свойств, назначенными через таблицы стилей. Чтобы привести пример, я продемонстрирую немного псевдокода.

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

Используя пример кода выше; если свойство width элемента div, который был сохранен в переменной 'elem', было стилизовано в таблице стилей CSS, а не внутри его тега HTML, вы, без сомнения, получите возвращаемое значение undefined, хранящееся внутри переменной cssProp. Неопределенное значение возникает из-за того, что для получения правильного значения код, написанный внутри таблицы стилей CSS, должен быть рассчитан для получения значения; Вы должны использовать метод, который будет вычислять значение свойства, значение которого находится в таблице стилей.

  • Отныне метод 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, а также устанавливает значения, хотя это не стандартный Javascript. Метод JQuery ...
$(selector).css(property,value)

... получает и устанавливает. Это то, что я использую, единственным недостатком является то, что вы познакомились с JQuery, но это, честно говоря, одна из очень многих веских причин, по которой каждый Javascript Developer должен изучать JQuery, это просто упрощает жизнь и предлагает методы, подобные этому, который недоступно со стандартным Javascript. Надеюсь, это поможет кому-то !!!

Aft3rL1f3
источник
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

GavinBelson
источник
-4
const something = { bar: "Foobar!" };
const foo = 'bar';

something[\`${foo}\`];
Сергей
источник
7
С какой стати ты это сделал? Вы fooуже строка, так `${foo}`что точно так же, как foo. (Кроме того, ваш код, кажется, имеет некоторые дополнительные обратные слеши, которые не принадлежат ему. Но он все равно будет бессмысленным, даже если вы исправите эту синтаксическую ошибку.)
Ilmari