Як отримати всі ключі та значення об'єкта в JavaScript
Object.keys(), Object.values() та Object.entries() - три статичні методи, які витягують власні перелічувані властивості об'єкта у вигляді масивів, кожен повертає різний зріз цих даних.
Теорія
Коротко
- Об'єкт як картотека з підписаними ящиками: ключі - це підписи, значення - вміст, entries дають і те, і інше
- Всі три методи повертають масиви тільки власних перелічуваних властивостей, без прототипного ланцюжка і Symbol-ключів
Object.keys()для імен,Object.values()для даних,Object.entries()для пар- Потрібна ітерація з деструктуризацією?
entries()+for...of. Тільки значення дляmap()?values() - Порядок вставки зберігається (ES2015+)
Швидкий приклад
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-ключі у результаті
const obj = { a: 1, [Symbol("id")]: 42 };
Object.keys(obj); // ["a"] - Symbol зник
// Рішення:
Reflect.ownKeys(obj); // ["a", Symbol(id)]Symbol-ключі невидимі для всіх трьох методів. Це часта точка здивування.
Помилка: for...in без hasOwnProperty на звичайних об'єктах
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"]Помилка: мутувати об'єкт під час ітерації
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-об'єкта
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
// 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.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.