Объекты: перебор свойств

Для перебора всех свойств из объекта используется цикл по свойствам for..in. Эта синтаксическая конструкция отличается от рассмотренного ранее цикла for(;;).

for..in

Синтаксис:

for (key in obj) {
  /* ... делать что-то с obj[key] ... */
}

При этом for..in последовательно переберёт свойства объекта obj, имя каждого свойства будет записано в key и вызвано тело цикла.

Объявление переменной в цикле for (var key in obj)

Вспомогательную переменную key можно объявить прямо в цикле:

for (var key in menu) {
  // ...
}

Так иногда пишут для краткости кода. Можно использовать и любое другое название, кроме key, например for(var propName in menu).

Пример итерации по свойствам:

var menu = {
  width: 300,
  height: 200,
  title: "Menu"
};

for (var key in menu) {
  // этот код будет вызван для каждого свойства объекта
  // ..и выведет имя свойства и его значение

  alert( "Ключ: " + key + " значение: " + menu[key] );
}

Обратите внимание, мы использовали квадратные скобки menu[key]. Как уже говорилось, если имя свойства хранится в переменной, то обратиться к нему можно только так, не через точку.

Количество свойств в объекте

Как узнать, сколько свойств хранит объект?

Готового метода для этого нет.

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

var menu = {
  width: 300,
  height: 200,
  title: "Menu"
};

var counter = 0;

for (var key in menu) {
  counter++;
}

alert( "Всего свойств: " + counter );

В следующих главах мы пройдём массивы и познакомимся с другим, более коротким, вызовом: Object.keys(menu).length.

В каком порядке перебираются свойства?

Для примера, рассмотрим объект, который задаёт список опций для выбора страны:

var codes = {
  // телефонные коды в формате "код страны": "название"
  "7": "Россия",
  "38": "Украина",
  // ..,
  "1": "США"
};

Здесь мы предполагаем, что большинство посетителей из России, и поэтому начинаем с 7, это зависит от проекта.

При выборе телефонного кода мы хотели бы предлагать варианты, начиная с первого. Обычно на основе списка генерируется select, но здесь нам важно не это, а важно другое.

Правда ли, что при переборе for(key in codes) ключи key будут перечислены именно в том порядке, в котором заданы?

По стандарту – нет. Но некоторое соглашение об этом, всё же, есть.

Соглашение говорит, что если имя свойства – нечисловая строка, то такие ключи всегда перебираются в том же порядке, в каком присваивались. Так получилось по историческим причинам и изменить это сложно: поломается много готового кода.

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

К примеру, рассмотрим объект с заведомо нечисловыми свойствами:

var user = {
  name: "Вася",
  surname: "Петров"
};
user.age = 25;

// порядок перебора соответствует порядку присвоения свойства
for (var prop in user) {
  alert( prop ); // name, surname, age
}

А теперь – что будет, если перебрать объект с кодами?

var codes = {
  // телефонные коды в формате "код страны": "название"
  "7": "Россия",
  "38": "Украина",
  "1": "США"
};

for (var code in codes) alert( code ); // 1, 7, 38

При запуске этого кода в современном браузере мы увидим, что на первое место попал код США!

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

А что, если мы хотим, чтобы порядок был именно таким, какой мы задали?

Это возможно. Можно применить небольшой хак, который заключается в том, чтобы сделать все ключи нечисловыми, например, добавим в начало дополнительный символ '+':

var codes = {
  "+7": "Россия",
  "+38": "Украина",
  "+1": "США"
};

for (var code in codes) {
  var value = codes[code];
  code = +code; // ..если нам нужно именно число, преобразуем: "+7" -> 7

  alert( code + ": " + value ); // 7, 38, 1 во всех браузерах
}

Итого

  • Цикл по ключам: for (key in obj).
  • Порядок перебора соответствует порядку объявления для нечисловых ключей, а числовые – сортируются (в современных браузерах).
  • Если нужно, чтобы порядок перебора числовых ключей соответствовал их объявлению в объекте, то используют трюк: числовые ключи заменяют на похожие, но содержащие не только цифры. Например, добавляют в начало +, как описано в примере выше, а потом, в процессе обработки, преобразуют такие ключи в числа.

Задачи

важность: 5

Создайте функцию isEmpty(obj), которая возвращает true, если в объекте нет свойств и false – если хоть одно свойство есть.

Работать должно так:

function isEmpty(obj) {
  /* ваш код */
}

var schedule = {};

alert( isEmpty(schedule) ); // true

schedule["8:30"] = "подъём";

alert( isEmpty(schedule) ); // false

Открыть песочницу с тестами для задачи.

function isEmpty(obj) {
  for (var key in obj) {
    return false;
  }
  return true;
}

var schedule = {};

alert( isEmpty(schedule) ); // true

schedule["8:30"] = "подъём";

alert( isEmpty(schedule) ); // false

Открыть решение с тестами в песочнице.

важность: 5

Есть объект salaries с зарплатами. Напишите код, который выведет сумму всех зарплат.

Если объект пустой, то результат должен быть 0.

Например:

"use strict";

var salaries = {
  "Вася": 100,
  "Петя": 300,
  "Даша": 250
};

//... ваш код выведет 650

P.S. Сверху стоит use strict, чтобы не забыть объявить переменные.

"use strict";

var salaries = {
  "Вася": 100,
  "Петя": 300,
  "Даша": 250
};

var sum = 0;
for (var name in salaries) {
  sum += salaries[name];
}

alert( sum );
важность: 5

Есть объект salaries с зарплатами. Напишите код, который выведет имя сотрудника, у которого самая большая зарплата.

Если объект пустой, то пусть он выводит «нет сотрудников».

Например:

"use strict";

var salaries = {
  "Вася": 100,
  "Петя": 300,
  "Даша": 250
};

// ... ваш код выведет "Петя"
"use strict";

var salaries = {
  "Вася": 100,
  "Петя": 300,
  "Даша": 250
};

var max = 0;
var maxName = "";
for (var name in salaries) {
  if (max < salaries[name]) {
    max = salaries[name];
    maxName = name;
  }
}

alert( maxName || "нет сотрудников" );
важность: 3

Создайте функцию multiplyNumeric, которая получает объект и умножает все численные свойства на 2. Например:

// до вызова
var menu = {
  width: 200,
  height: 300,
  title: "My menu"
};

multiplyNumeric(menu);

// после вызова
menu = {
  width: 400,
  height: 600,
  title: "My menu"
};

P.S. Для проверки на число используйте функцию:

function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n)
}

Открыть песочницу с тестами для задачи.

var menu = {
  width: 200,
  height: 300,
  title: "My menu"
};

function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function multiplyNumeric(obj) {
  for (var key in obj) {
    if (isNumeric(obj[key])) {
      obj[key] *= 2;
    }
  }
}

multiplyNumeric(menu);

alert( "menu width=" + menu.width + " height=" + menu.height + " title=" + menu.title );

Открыть решение с тестами в песочнице.

Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.