You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/07-object-properties/01-property-descriptors/article.md
+13-14Lines changed: 13 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,13 +3,13 @@
3
3
4
4
Как мы знаем, объекты могут содержать свойства.
5
5
6
-
До этого момента мы рассматривали свойство только как пару "ключ-значение". Но на самом деле свойство объекта - это гораздо более гибкое и мощное средство.
6
+
До этого момента мы рассматривали свойство только как пару "ключ-значение". Но на самом деле свойство объекта гораздо мощнее и гибче.
7
7
8
-
В этой главе мы изучим дополнительные опции конфигурации для свойств, а в следующей увидим, как можно незаметно превратить их в специальные функции - геттеры и сеттеры.
8
+
В этой главе мы изучим дополнительные флаги конфигурации для свойств, а в следующей увидим, как можно незаметно превратить их в специальные функции - геттеры и сеттеры.
9
9
10
10
## Флаги свойств
11
11
12
-
Помимо **`значения`**, свойства объекта имеют три специальных атрибута (так называемые "флаги").
12
+
Помимо значения **`value`**, свойства объекта имеют три специальных атрибута (так называемые "флаги").
13
13
14
14
-**`writable`** -- если `true`, свойство можно изменить, иначе оно только для чтения.
15
15
-**`enumerable`** -- если `true`, свойство перечисляется в циклах, в противном случае циклы его игнорируют.
: Объект и свойство, с которыми мы будем работать.
66
+
: Объект и его свойство, для которого нужно применить дескриптор.
67
67
68
68
`descriptor`
69
69
: Применяемый дескриптор.
70
70
71
71
Если свойство существует, `defineProperty` обновит его флаги. В противном случае метод создает новое свойство с указанным значением и флагами; если какой-либо флаг не указан явно, ему присваивается значение `false`.
72
72
73
-
Например, все флаги свойства`name` в коде ниже создаются со значением `false`:
73
+
Например, здесь создаётся свойство`name`, все флаги которого имеют значение `false`:
user.name="Pete"; // Ошибка: Невозможно изменить доступное только для чтения свойство 'name'...
119
+
user.name="Pete"; // Ошибка: Невозможно изменить доступное только для чтения свойство 'name'
120
120
*/!*
121
121
```
122
122
123
123
Теперь никто не сможет изменить имя пользователя, если только не обновит соответствующий флаг новым вызовом `defineProperty`.
124
124
125
125
```smart header="Ошибки появляются только в строгом режиме"
126
-
В нестрогом режиме, без `use strict`, мы не увидим никаких ошибок при записи в свойства "только для чтения" и т.п. Эти операции всё равно не будут выполнены успешно. Действия, нарушающие ограничения флагов, в нестрогом режиме просто молча игнорируются.
126
+
В нестрогом режиме, без `use strict`, мы не увидим никаких ошибок при записи в свойства "только для чтения" и т.п. Но эти операции всё равно не будут выполнены успешно. Действия, нарушающие ограничения флагов, в нестрогом режиме просто молча игнорируются.
127
127
```
128
128
129
-
Вот та же самая операция, но в ситуации, когда свойства не существует:
129
+
Вот тот же пример, но свойство создано "с нуля":
130
130
131
131
```js run
132
132
let user = { };
133
133
134
134
Object.defineProperty(user, "name", {
135
135
*!*
136
-
value:"Pete",
136
+
value:"John",
137
137
// для нового свойства необходимо явно указывать все флаги, для которых значение true
138
138
enumerable:true,
139
139
configurable:true
140
140
*/!*
141
141
});
142
142
143
-
alert(user.name); //Pete
144
-
user.name="Alice"; // Ошибка
143
+
alert(user.name); //John
144
+
user.name="Pete"; // Ошибка
145
145
```
146
146
147
-
148
147
## Неперечислимое свойство
149
148
150
149
Теперь добавим собственный метод `toString` к объекту `user`.
151
150
152
-
Обычно встроенный метод `toString` в объектах - неперечислимый, его не видно в цикле `for..in`. Но если мы напишем свой собственный метод `toString`, цикл `for..in` будет выводить его по умолчанию:
151
+
Встроенный метод `toString` в объектах - неперечислимый, его не видно в цикле `for..in`. Но если мы напишем свой собственный метод `toString`, цикл `for..in` будет выводить его по умолчанию:
Copy file name to clipboardExpand all lines: 1-js/07-object-properties/02-property-accessors/article.md
+34-23Lines changed: 34 additions & 23 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,23 +9,23 @@
9
9
10
10
## Геттеры и сеттеры
11
11
12
-
Свойства-аксессоры записываются как методы: так называемый "геттер" - для чтения и "сеттер" - для записи. При литеральном объявлении объекта они обозначены`get` и `set`:
12
+
Свойства-аксессоры представлены методами: "геттер" - для чтения и "сеттер" - для записи. При литеральном объявлении объекта они обозначаются`get` и `set`:
13
13
14
14
```js
15
15
let obj = {
16
16
*!*get propName()*/!* {
17
-
// геттер, код получения obj.propName
17
+
// геттер, срабатывает при чтении obj.propName
18
18
},
19
19
20
20
*!*set propName(value)*/!* {
21
-
// сеттер, код присвоения obj.propName = value
21
+
// сеттер, срабатывает при записи obj.propName = value
22
22
}
23
23
};
24
24
```
25
25
26
26
Геттер срабатывает, когда `obj.propName` читается, сеттер -- когда значение назначается.
27
27
28
-
Для примера, у нас есть объект `user` со свойствами `name` и `surname`:
28
+
Например, у нас есть объект `user` со свойствами `name` и `surname`:
29
29
30
30
```js run
31
31
let user = {
@@ -34,7 +34,7 @@ let user = {
34
34
};
35
35
```
36
36
37
-
А теперь добавим свойство объекта "fullName" для полного имени, которое в нашем случае: "John Smith". Само собой, мы не хотим дублировать уже имеющуюся информацию, так что реализуем его при помощи аксессора:
37
+
А теперь добавим свойство объекта `fullName` для полного имени, которое в нашем случае: `"John Smith"`. Само собой, мы не хотим дублировать уже имеющуюся информацию, так что реализуем его при помощи аксессора:
38
38
39
39
```js run
40
40
let user = {
@@ -55,7 +55,19 @@ alert(user.fullName); // John Smith
55
55
56
56
Снаружи свойство-аксессор выглядит как обычное свойство. В этом и заключается смысл свойств-аксессоров. Мы не *вызываем*`user.fullName` как функцию, а *читаем* как обычное свойство: геттер выполнит всю работу за кулисами.
57
57
58
-
На данный момент `fullName` имеет только геттер. Если попытаться присвоить значение свойству `user.fullName`, то это вызовет ошибку.
58
+
На данный момент `fullName` имеет только геттер. Если попытаться присвоить значение свойству `user.fullName`, то это вызовет ошибку:
59
+
60
+
```js run
61
+
let user = {
62
+
getfullName() {
63
+
return`...`;
64
+
}
65
+
};
66
+
67
+
*!*
68
+
user.fullName="Тест"; // Ошибка (у свойства есть только геттер)
69
+
*/!*
70
+
```
59
71
60
72
Давайте исправим это, добавив сеттер для `user.fullName`:
61
73
@@ -82,15 +94,10 @@ alert(user.name); // Alice
82
94
alert(user.surname); // Cooper
83
95
```
84
96
85
-
В итоге мы получили "виртуальное" свойство fullName. Его можно прочитать и изменить, но по факту его не существует.
86
-
87
-
```smart header="Accessor properties are only accessible with get/set"
88
-
Как только свойство определено с помощью `get prop()` или `set prop()`, оно становится свойством-аксессором, а не свойством-данных.
97
+
В итоге мы получили "виртуальное" свойство `fullName`. Его можно прочитать и изменить, но по факту его не существует.
89
98
90
-
- Если определен геттер -- мы можем прочитать свойство `object.prop`, иначе нет.
91
-
- Если определен сеттер -- мы можем установить свойство `object.prop=...`, иначе нет.
92
-
93
-
В обоих случаях мы не можем удалить свойство-аксессор.
99
+
```smart header="Нет поддержки для `delete`"
100
+
При попытке удалить свойство-аксессор оператором `delete` будет ошибка.
94
101
```
95
102
96
103
@@ -100,14 +107,14 @@ alert(user.surname); // Cooper
100
107
101
108
Свойства-аксессоры не имеют `value` и `writable`, но взамен предлагают функции `get` и `set`.
102
109
103
-
Таким образом, дескриптор аксессора может иметь:
110
+
То есть, дескриптор аксессора может иметь:
104
111
105
112
- **`get`** -- функция без аргументов, которая сработает при чтении свойства,
106
113
- **`set`** -- функция, принимающая один аргумент, вызываемая при присвоении свойства,
107
114
- **`enumerable`** -- то же самое, что и для свойств-данных,
108
115
- **`configurable`** -- то же самое, что и для свойств-данных.
109
116
110
-
Например, для создания аксессора `fullName` при помощи `defineProperty`, мы можем передать дескриптор с использованием `get` и `set`:
117
+
Например, для создания аксессора `fullName` при помощи `defineProperty` мы можем передать дескриптор с использованием `get` и `set`:
111
118
112
119
```js run
113
120
let user = {
@@ -132,7 +139,7 @@ alert(user.fullName); // John Smith
132
139
for(let key in user) alert(key); // name, surname
133
140
```
134
141
135
-
Обратите внимание, что свойство объекта может быть только свойством-аксессора или свойством-данных.
142
+
Ещё раз заметим, что свойство объекта может быть только свойством-аксессором (с методами `get/set`) или свойством-данных (со значением `value`).
136
143
137
144
При попытке указать и `get` и `value` в одном дескрипторе будет ошибка:
Геттеры/сеттеры можно использовать как обёртки над "реальными" значениями свойств, чтобы получить больше контроля над ними.
161
+
Геттеры/сеттеры можно использовать как обёртки над "реальными" значениями свойств, чтобы получить больше контроля над операциями с ними.
155
162
156
-
Например, если мы хотим запретить устанавливать короткое имя для `user`, мы можем хранить `name`в специальном свойстве `_name`, отфильтровав значение в сеттере:
163
+
Например, если мы хотим запретить устанавливать короткое имя для `user`, мы можем использовать сеттер `name`для проверки, а само значение хранить в отдельном свойстве `_name`:
157
164
158
165
```js run
159
166
let user = {
@@ -176,14 +183,16 @@ alert(user.name); // Pete
176
183
user.name=""; // Имя слишком короткое...
177
184
```
178
185
186
+
Таким образом, само имя хранится в `_name`, доступ к которому производится через геттер и сеттер.
187
+
179
188
Технически, внешний код всё ещё может получить доступ к имени напрямую с помощью `user._name`, но существует широко известное соглашение о том, что свойства, которые начинаются с символа `"_"`, являются внутренними, и к ним не следует обращаться извне пределов объекта.
180
189
181
190
182
191
## Использование для совместимости
183
192
184
-
Одна из хороших идей, стоящих за геттерами и сеттерами -- они позволяют в любой момент взять "обычное" свойство и изменить его поведение, поменяв на геттер и сеттер.
193
+
У аксессоров есть интересная область применения - они позволяют в любой момент взять "обычное" свойство и изменить его поведение, поменяв на геттер и сеттер.
185
194
186
-
Например, мы начали реализовывать объект `user`, используя свойства-данные `name` и `age`:
195
+
Например, представим, что мы начали реализовывать объект `user`, используя свойства-данные имя `name` и возраст`age`:
187
196
188
197
```js
189
198
functionUser(name, age) {
@@ -196,7 +205,7 @@ let john = new User("John", 25);
196
205
alert( john.age ); // 25
197
206
```
198
207
199
-
...Но рано или поздно всё может измениться. Взамен `age` мы можем решить хранить `birthday`, потому что так более точно и удобно:
208
+
...Но рано или поздно всё может измениться. Взамен возраста `age` мы можем решить хранить дату рождения`birthday`, потому что так более точно и удобно:
200
209
201
210
```js
202
211
functionUser(name, birthday) {
@@ -209,7 +218,9 @@ let john = new User("John", new Date(1992, 6, 1));
209
218
210
219
Что нам делать со старым кодом, который использует свойство `age`?
211
220
212
-
Мы можем попытаться найти все такие места и изменить их, но это отнимает время и может быть выполнимо, если код был написан/используется многими другими людьми. И кроме того, `age` -- это отличное свойство для `user`, верно? В некоторых ситуациях это то, что нам нужно.
221
+
Мы можем попытаться найти все такие места и изменить их, но это отнимает время и может быть невыполнимо, если код был используется многими другими людьми. И кроме того, `age` -- это отличное свойство для `user`, верно?
0 commit comments