06. Object Iteration

  • Перечисляемые свойства --- это те свойства, для которых внутренний атрибут ключа выставлен в enumerable = true. Перечисляемые свойства можно перебрать в цикле for..in, за исключением полей-символов.

  • Принадлежность свойства определяется тем, принадлежит ли свойство самому объекту, или находится в его цепочке прототипов.

    • Собственные свойства принадлежат самому объекту, а

    • Унаследованные свойства принадлежат его прототипу.

В ES6 порядок перебора ключей стандартизирован:

  • Все индексы массивов, отсортированные по возрастанию. Индекс массива - специальный ключ-строка, хранящий число 0..2^32−1.

  • Все строковые ключи, в том порядке, в котором они объявлены.

  • Все символы, в том порядке в котором они объявлены.

В зависимости от способа перечисления можно получить разные свойства:

for-in

Цикл for..in перебирает перечисляемые свойства объекта в произвольном порядке, включая унаследованные:

const obj = { a: 1, b: 2, c: 3 };
for (const prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

Особенности:

  • Порядок не определен

  • Перечисляются так же и наследуемые свойства по всей цепочке прототипов.

  • Если необходимо перечислять только собственные свойства объекта, их необходимо фильтровать

  • Если свойство объекта отмечено как enumerable: false, то оно не будет встречаться в цикле.

  • В теле цикла можно изменять объект, а так же удалять его свойства.

hasOwnProperty guard

Common pattern consisting of for..in with Object.prototype.hasOwnProperty guards:

const obj = {
  x: 1,
  y: 1,
  z: 1
};
const total = 0;
for (const prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    total += obj[prop];
  }
}

Note that in this particular case, the Object#hasOwnProperty guard is not used properly, because the lookup happens on the obj itself, which leads to potential performance issues, i.e. if you pass many different shapes of objects then the obj.hasOwnProperty access will become megamorphic, but might also turn into a correctness issue, i.e. if obj has a null prototype because obj was created via Object.create(null), then the expression obj.hasOwnProperty will raise an error. So make sure to always follow the advice to write Object.prototype.hasOwnProperty.call(obj, prop) instead, which is safe and avoids the potential negative performance impact:

const obj = {
  x: 1,
  y: 1,
  z: 1
};
const total = 0;
for (const prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    total += obj[prop];
  }
}

for-of

Конструкция for..of в начале своего выполнения автоматически вызывает Symbol.iterator(), получает итератор и далее вызывает метод next() до получения done: true. Если функционал по перебору (метод next) предоставляется самим объектом, то можно вернуть this в качестве итератора:

// Throw Error
const obj = { a: 1, b: 2, c: 3 };
for (const prop of obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

// Ok
const obj = { a: 1, b: 2, c: 3 };
for (const [key, value] of Object.entries(obj)) {
  console.log("obj." + key + " = " + value);
}

Создание собственного итератора:

const range = {
    from: 1,
    to: 5,

    [Symbol.iterator]() {
        return this;
    },

    next() {
        ...
    }
};

for (let num of range) {
    console.log(num); // 1, затем 2, 3, 4, 5
}

Object.keys(), Object.values()

Object.keys() : Array<string> --- возвращает массив имен собственных перечислимых свойств объекта.

const arr = ["a", "b", "c"];
Object.keys(arr); // console: ['0', '1', '2']

// array like object
const obj = { 0: "a", 1: "b", 2: "c" };
Object.keys(obj); // console: ['0', '1', '2']

// array like object with random key ordering
const an_obj = { 100: "a", 2: "b", 7: "c" };
Object.keys(an_obj); // console: ['2', '7', '100']

// getFoo is property which isn't enumerable
const my_obj = Object.create(
  {},
  {
    getFoo: {
      value: function() {
        return this.foo;
      }
    }
  }
);
my_obj.foo = 1;

Object.keys(my_obj); // console: ['foo']

Не возвращает унаследованные свойства

const simpleColors = {
  colorA: "white",
  colorB: "black"
};
const natureColors = {
  colorC: "green",
  colorD: "yellow"
};

Object.setPrototypeOf(natureColors, simpleColors);
Object.keys(natureColors); // => ['colorC', 'colorD']

natureColors["colorA"]; // => 'white'
natureColors["colorB"]; // => 'black'

Похожие функции, работающие по тем же принципам:

  • Object.values

  • Object.entries

Object.getOwnPropertyNames()

Object.getOwnPropertyNames() : Array<string> - действует подобно функции Object.keys(), но возвращает имена всех собственных свойств указанного объекта, а не только перечислимые. Порядок свойств такой же, как при ручном переборе объекта в цикле.

const arr = ["a", "b", "c"];
Object.getOwnPropertyNames(arr).sort(); // logs '0,1,2,length'

// Array-like object
const obj = { 0: "a", 1: "b", 2: "c" };
Object.getOwnPropertyNames(obj).sort(); // logs '0,1,2'

// non-enumerable property
const my_obj = Object.create(
  {},
  {
    getFoo: {
      value: function() {
        return this.foo;
      },
      enumerable: false
    }
  }
);
my_obj.foo = 1;

Object.getOwnPropertyNames(my_obj).sort(); // logs 'foo,getFoo'

Похожие методы:

  • Object.getOwnPropertySymbols(obj) : Array<symbol> -- возвращает все собственные ключи-символы объекта.

  • Reflect.ownKeys(obj) : Array<string|symbol> --- возвращает все ключи всех свойств объекта.

  • Reflect.enumerate(obj) : Iterator - возвращает все строковые значения всех перечисляемых свойств.

Last updated