Skip to main content

Як отримати всі ключі та значення об'єкта в JavaScript

Object.keys(), Object.values() та Object.entries() - три статичні методи, які витягують власні перелічувані властивості об'єкта у вигляді масивів, кожен повертає різний зріз цих даних.

Теорія

Коротко

  • Об'єкт як картотека з підписаними ящиками: ключі - це підписи, значення - вміст, entries дають і те, і інше
  • Всі три методи повертають масиви тільки власних перелічуваних властивостей, без прототипного ланцюжка і Symbol-ключів
  • Object.keys() для імен, Object.values() для даних, Object.entries() для пар
  • Потрібна ітерація з деструктуризацією? entries() + for...of. Тільки значення для map()? values()
  • Порядок вставки зберігається (ES2015+)

Швидкий приклад

javascript
const user = { name: "Alice", age: 25, role: "admin" }; Object.keys(user); // ["name", "age", "role"] Object.values(user); // ["Alice", 25, "admin"] Object.entries(user); // [["name", "Alice"], ["age", 25], ["role", "admin"]] // Ітерація з деструктуризацією for (const [key, value] of Object.entries(user)) { console.log(`${key}: ${value}`); // "name: Alice", "age: 25", "role: admin" }

Всі три методи знімають знімок стану об'єкта в момент виклику.

Головна різниця

Ці методи бачать тільки власні перелічувані властивості. Успадковані з прототипу і Symbol-ключі вони пропускають. for...in обходить весь прототипний ланцюжок, тому раніше без hasOwnProperty() не обходилось. Ці три методи цієї проблеми не мають.

Коли що використовувати

  • Object.keys() - перевірити наявність властивостей або отримати список імен: Object.keys(config).includes("timeout")
  • Object.values() - трансформувати або агрегувати дані: Object.values(prices).reduce((sum, p) => sum + p, 0)
  • Object.entries() - коли потрібні одночасно ключ і значення, або конвертація в Map: new Map(Object.entries(obj))
  • for...in - тільки в старому коді, де явно потрібні успадковані властивості

Таблиця порівняння

МетодПовертаєТільки власні?Порядок збережено?
Object.keys()string[]ТакТак (ES2015+)
Object.values()any[]ТакТак (ES2015+)
Object.entries()[string, any][]ТакТак (ES2015+)
for...inПеребирає ключіНі (включає прототип)Не гарантовано
Коли використовуватиkeys для імен, values для даних, entries для пар або Map, for...in тільки якщо потрібні успадковані

Як це працює

V8 перебирає власні рядкові ключі об'єкта через дескриптори прихованого класу і будує новий масив у порядку вставки (стабільно з ES2015). Symbol-ключі повністю пропускаються. Геттери виконуються під час Object.values(), тому результат відображає те, що геттер повернув у момент виклику. Якщо потрібні і Symbol-ключі, використовуй Reflect.ownKeys(obj).

Типові помилки

Помилка: очікувати Symbol-ключі у результаті

javascript
const obj = { a: 1, [Symbol("id")]: 42 }; Object.keys(obj); // ["a"] - Symbol зник // Рішення: Reflect.ownKeys(obj); // ["a", Symbol(id)]

Symbol-ключі невидимі для всіх трьох методів. Це часта точка здивування.

Помилка: for...in без hasOwnProperty на звичайних об'єктах

javascript
function Base() {} Base.prototype.inherited = "oops"; const obj = new Base(); obj.own = "mine"; for (const key in obj) { console.log(key); // "own", "inherited" - прototипні властивості потрапляють у цикл } // Ці три методи такої проблеми не мають: Object.keys(obj); // ["own"]

Помилка: мутувати об'єкт під час ітерації

javascript
const obj = { a: 1, b: 2 }; for (const [k, v] of Object.entries(obj)) { obj[`copy_${k}`] = v; // додаємо властивості під час циклу } // Цикл бачить тільки "a" і "b" - entries() зробив знімок на початку // Але об'єкт отримає зайві ключі, що може призвести до непередбачуваних помилок // Рішення: спочатку клонуй for (const [k, v] of Object.entries({ ...obj })) { obj[`copy_${k}`] = v; }

Де зустрічається в реальному коді

  • React - Object.entries(props) для рендерингу динамічних <option> в select
  • Redux - Object.keys(state.entities) для отримання масиву ID в селекторах
  • Express - Object.values(req.headers) для логування всіх заголовків
  • Node.js - Object.entries(process.env) для безпечної ітерації змінних середовища
  • Lodash - використовує entries() всередині .toPairs() та .fromPairs()

Питання на співбесіді

Q: Що поверне Object.keys({})?
A: Порожній масив []. Немає властивостей - немає ключів.

Q: Яка різниця між Object.keys() і for...in?
A: Object.keys() повертає тільки власні перелічувані рядкові ключі в порядку вставки. for...in обходить весь прототипний ланцюжок і не гарантує порядок. У сучасному коді краще Object.keys().

Q: Чи виконує Object.values() геттер-функції?
A: Так. Якщо властивість оголошена як геттер, Object.values() викликає його і включає результат у масив.

Q: Як отримати всі ключі, включаючи Symbol?
A: Reflect.ownKeys(obj). Повертає і рядкові, і Symbol-ключі в одному масиві.

Q: Якщо додати властивість після виклику Object.entries(), чи побачить її цикл?
A: Ні. Object.entries() робить знімок у момент виклику. Властивості, додані після, в ітерацію не потраплять.

Приклади

Ключі, значення та пари для config-об'єкта

javascript
const config = { host: "localhost", port: 3000, debug: true }; // Перевірити наявність ключа (тільки власна властивість) Object.keys(config).includes("port"); // true // З'єднати значення в рядок Object.values(config).join(", "); // "localhost, 3000, true" // Побудувати query string з entries const query = Object.entries(config) .map(([key, value]) => `${key}=${value}`) .join("&"); // "host=localhost&port=3000&debug=true"

Object.keys() підходить для перевірки наявності без торкання значень. Object.entries() - коли потрібні обидві сторони одразу.

Конвертація відповіді API в Map

javascript
// API повертає плаский об'єкт з динамічними ключами const permissions = { read: true, write: false, delete: true }; // Конвертуємо в Map для швидкого пошуку за ключем const permMap = new Map(Object.entries(permissions)); permMap.get("write"); // false permMap.has("admin"); // false // Фільтруємо тільки активні права const active = Object.entries(permissions) .filter(([, value]) => value === true) .map(([key]) => key); // ["read", "delete"]

Я тягнусь до цього патерну кожного разу, коли API повертає об'єкт з правами або налаштуваннями, де ключі наперед невідомі. Map зручніший для динамічних пошуків, ніж повторні перевірки через in.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?