Свойства: точка и квадратные скобки

В прошлом уроке мы читали свойства через точку: user.name. У JavaScript есть второй способ – квадратные скобки: имя свойства пишут строкой в кавычках и заключают в [ ]. Результат тот же:

two-ways.jsjs
const user = {  name: 'Ира',  age: 20,};console.log(user.name);console.log(user['name']);
Консоль
Ира
Ира

Обе записи обращаются к свойству name. Пока имя свойства известно заранее, точка проще и читается лучше, поэтому обычно пишут именно её:

dot.jsjs
user.name;user.age;user.city;

Когда точка не подходит

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

old-price.jsjs
const product = {  name: 'Ноутбук',  'old-price': 90000,};console.log(product['old-price']);
Консоль
90000

Прочитать такое свойство через точку не выйдет: в записи product.old-price JavaScript увидел бы вычитание – свойство old минус переменная price. В квадратных скобках имя остаётся строкой целиком, поэтому свойство читается без хитростей. То же с пробелом в имени:

first-name.jsjs
const user = {  'first name': 'Ира',};console.log(user['first name']);
Консоль
Ира

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

Точка читает имя буквально, скобки вычисляют

dot-vs-brackets.jsjs
const user = {  name: 'Ира',  age: 20,};const key = 'age';console.log(user.key);console.log(user[key]);
Консоль
undefined
20

Эти две записи выглядят похоже, но означают разное.

В user.key слово key – само имя свойства. JavaScript ищет у объекта свойство буквально с именем 'key'.

В user[key] содержимое скобок сначала вычисляется. В переменной key лежит строка 'age', поэтому запись превращается по смыслу в user['age'], и результатом становится 20.

Точка читает буквально, скобки вычисляют
const user = {
name: 'Ира',
age: 20,
};
const key = 'age';
В key хранится строка 'age' – имя свойства, которое мы хотим получить.
console.log(user.key);
console.log(user[key]);

Когда имя заранее неизвестно

В предыдущем примере мы сами записали const key = 'age', поэтому скобки пока могут казаться лишними. Но в настоящей программе нужное свойство часто выбирает не программист, а данные: настройка, ответ сервера, ввод пользователя.

greetings.jsjs
const greetings = {  ru: 'Привет',  en: 'Hello',  es: 'Hola',};let lang = 'en';console.log(greetings[lang]);
Консоль
Hello

Значение lang может прийти из настроек пользователя. Мы заранее не знаем, будет там 'ru', 'en' или 'es'. Поэтому написать greetings.en нельзя: это навсегда выбрало бы английский язык. greetings[lang] говорит другое: «возьми имя свойства из lang и найди значение с этим именем». Поменяется lang – та же строка кода найдёт другое приветствие:

greetings-es.jsjs
lang = 'es';console.log(greetings[lang]);
Консоль
Hola

Ключ и значение

Имя свойства часто называют ключом. В объекте greetings ключи – 'ru', 'en' и 'es', а значения – соответствующие приветствия:

ключзначение
'ru''Привет'
'en''Hello'
'es''Hola'

Иногда объект удобно воспринимать как таблицу соответствий: по ключу получаем связанное с ним значение. Тогда квадратные скобки позволяют выбирать строку этой таблицы во время работы программы: greetings[lang].

Читать, менять и добавлять можно одинаково

write-by-key.jsjs
const user = {  name: 'Ира',  age: 20,};const key = 'age';// меняем: свойство age уже естьuser[key] = 21;console.log(user.age);const field = 'city';// добавляем: свойства city ещё нетuser[field] = 'Москва';console.log(user);
Консоль
21
{ name: 'Ира', age: 21, city: 'Москва' }

Скобки – не отдельный способ «только прочитать» свойство. После того как JavaScript получил имя, с таким свойством можно делать всё то же самое:

  • user[key] – прочитать
  • user[key] = value – изменить или добавить
  • delete user[key] – удалить

Есть ли такое свойство

Если имя приходит извне, иногда нужно сначала проверить: есть ли вообще такое свойство? Для этого есть оператор in:

in.jsjs
const user = {  name: 'Ира',  age: 20,};console.log('name' in user);console.log('email' in user);const field = 'age';console.log(field in user);
Консоль
true
false
true

Слева от in находится имя свойства – строкой или переменной, в которой лежит строка, – справа объект. Результат проверки – true или false.

Чтение не отличает отсутствие свойства от undefined

Кажется, что in здесь не обязателен. Ведь если отсутствующее свойство возвращает undefined, можно просто проверить: user.email !== undefined.

Но такая проверка не всегда показывает, существует ли свойство. Сравните два объекта:

in-vs-undefined.jsjs
// свойства email нетconst guest = {  name: 'Гость',};// свойство есть, но в нём undefinedconst user = {  name: 'Ира',  email: undefined,};console.log(guest.email !== undefined);console.log(user.email !== undefined);console.log('email' in guest);console.log('email' in user);
Консоль
false
false
false
true

Объекты разные, а сравнение в обоих случаях дало false. Оно смотрит только на значение, а чтение email и у guest, и у user возвращает undefined. По этому значению нельзя понять, что именно перед нами – свойства нет вовсе или свойство есть, а undefined записано в него как значение.

in смотрит не на значение, а на само наличие свойства: у guest его нет – false, у user есть – true.

Разница между этими случаями бывает важна. Скажем, у объекта с настройками отсутствие свойства theme означает «пользователь ничего не выбирал, подставим тему по умолчанию», а theme: undefined – «выбор сбросили намеренно».

Вычисляемое имя свойства при создании объекта

До сих пор динамический ключ использовался после того, как объект уже существовал: user[key]. Но иногда имя неизвестно и в момент создания объекта:

computed.jsjs
const key = 'score';const player = {  name: 'Ира',  [key]: 100,};console.log(player.score);
Консоль
100

Квадратные скобки внутри литерала имеют ту же идею: выражение сначала вычисляется, а его результат становится именем свойства. В key лежит 'score', поэтому объект получает свойство score:

как записано
const key = 'score';const player = {  name: 'Ира',  [key]: 100,};
какой объект получился
const key = 'score';const player = {  name: 'Ира',  score: 100,};

Одна идея – два места

one-idea.jsjs
// использовать вычисленное имя при обращенииuser[key];// использовать вычисленное имя при созданииconst player = { [key]: 100 };

В обоих случаях скобки означают: «имя нужно не читать буквально, а сначала вычислить».

// теперь вы знаете

  1. user.name и user['name'] читают одно свойство; когда имя известно заранее, обычно используют точку
  2. Квадратные скобки позволяют обращаться к именам, которые неудобно писать через точку: user['first name']
  3. user.key ищет буквально свойство key, а user[key] сначала вычисляет значение key и использует его как имя свойства
  4. Поэтому имя свойства может приходить из данных: greetings[lang]. Через скобки свойство можно читать, изменять, добавлять и удалять
  5. in позволяет проверить наличие свойства и отличить отсутствующее свойство от существующего со значением undefined
  6. Та же идея работает при создании объекта: { [key]: 100 } вычисляет имя свойства

Проверьте себя

Что появится в консоли?

const user = { name: 'Ира', age: 20 };
const name = 'age';
console.log(user.name);
console.log(user[name]);

Что появится в консоли?

const player = { name: 'Ира' };
const field = 'level';
player[field] = 5;
console.log(player.level);

Верно ли, что код напечатает 60?

const scores = {
round1: 40,
round2: 55,
};
const n = 2;
scores['round' + n] = 60;
console.log(scores.round2);

Что появится в консоли?

const settings = { theme: 'dark', sound: undefined };
console.log('sound' in settings);
console.log('volume' in settings);

Что появится в консоли?

const field = 'city';
const user = { city: 'Казань' };
console.log('field' in user);
console.log(field in user);

Какая запись правильно читает свойство 'old-price'?

const product = {
title: 'Ноутбук',
'old-price': 90000,
};

Что появится в консоли?

const key = 'score';
const player = {
key: 10,
[key]: 100,
};
console.log(player.key + player[key]);