Skip to content

Commit bddf221

Browse files
committed
sync-07-object-properties
1 parent c121c02 commit bddf221

File tree

2 files changed

+67
-50
lines changed

2 files changed

+67
-50
lines changed

‎1-js/07-object-properties/01-property-descriptors/article.md‎

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33

44
As we know, objects can store properties.
55

6-
Till now, a property was a simple "key-value" pair to us. But an object property is actually a more flexible and powerful thing.
6+
Until now, a property was a simple "key-value" pair to us. But an object property is actually a more flexible and powerful thing.
77

88
In this chapter we'll study additional configuration options, and in the next we'll see how to invisibly turn them into getter/setter functions.
99

1010
## Property flags
1111

1212
Object properties, besides a **`value`**, have three special attributes (so-called "flags"):
1313

14-
-**`writable`** -- if `true`, can be changed, otherwise it's read-only.
14+
-**`writable`** -- if `true`, the value can be changed, otherwise it's read-only.
1515
-**`enumerable`** -- if `true`, then listed in loops, otherwise not listed.
1616
-**`configurable`** -- if `true`, the property can be deleted and these attributes can be modified, otherwise not.
1717

@@ -63,10 +63,10 @@ Object.defineProperty(obj, propertyName, descriptor)
6363
```
6464

6565
`obj`, `propertyName`
66-
: The object and property to work on.
66+
: The object and its property to apply the descriptor.
6767

6868
`descriptor`
69-
: Property descriptor to apply.
69+
: Property descriptor object to apply.
7070

7171
If the property exists, `defineProperty` updates its flags. Otherwise, it creates the property with the given value and flags; in that case, if a flag is not supplied, it is assumed `false`.
7272

@@ -100,9 +100,9 @@ Compare it with "normally created" `user.name` above: now all flags are falsy. I
100100

101101
Now let's see effects of the flags by example.
102102

103-
## Read-only
103+
## Non-writable
104104

105-
Let's make `user.name`read-only by changing `writable` flag:
105+
Let's make `user.name`non-writable (can't be reassigned) by changing `writable` flag:
106106

107107
```js run
108108
let user ={
@@ -116,36 +116,39 @@ Object.defineProperty(user, "name",{
116116
});
117117

118118
*!*
119-
user.name="Pete"; // Error: Cannot assign to read only property 'name'...
119+
user.name="Pete"; // Error: Cannot assign to read only property 'name'
120120
*/!*
121121
```
122122

123123
Now no one can change the name of our user, unless they apply their own `defineProperty` to override ours.
124124

125-
Here's the same operation, but for the case when a property doesn't exist:
125+
```smart header="Errors appear only in strict mode"
126+
In the non-strict mode, no errors occur when writing to non-writable properties and such. But the operation still won't succeed. Flag-violating actions are just silently ignored in non-strict.
127+
```
128+
129+
Here's the same example, but the property is created from scratch:
126130

127131
```js run
128132
let user ={};
129133

130134
Object.defineProperty(user, "name",{
131135
*!*
132-
value:"Pete",
133-
// for new properties need to explicitly list what's true
136+
value:"John",
137+
// for new properties we need to explicitly list what's true
134138
enumerable:true,
135139
configurable:true
136140
*/!*
137141
});
138142

139-
alert(user.name); //Pete
140-
user.name="Alice"; // Error
143+
alert(user.name); //John
144+
user.name="Pete"; // Error
141145
```
142146

143-
144147
## Non-enumerable
145148

146149
Now let's add a custom `toString` to `user`.
147150

148-
Normally, a built-in `toString` for objects is non-enumerable, it does not show up in `for..in`. But if we add `toString` of our own, then by default it shows up in `for..in`, like this:
151+
Normally, a built-in `toString` for objects is non-enumerable, it does not show up in `for..in`. But if we add a `toString` of our own, then by default it shows up in `for..in`, like this:
149152

150153
```js run
151154
let user ={
@@ -159,7 +162,7 @@ let user ={
159162
for (let key in user) alert(key); // name, toString
160163
```
161164

162-
If we don't like it, then we can set `enumerable:false`. Then it won't appear in `for..in` loop, just like the built-in one:
165+
If we don't like it, then we can set `enumerable:false`. Then it won't appear in a `for..in` loop, just like the built-in one:
163166

164167
```js run
165168
let user ={
@@ -191,9 +194,9 @@ alert(Object.keys(user)); // name
191194

192195
The non-configurable flag (`configurable:false`) is sometimes preset for built-in objects and properties.
193196

194-
A non-configurable property can not be deleted or altered with `defineProperty`.
197+
A non-configurable property can not be deleted.
195198

196-
For instance, `Math.PI` is read-only, non-enumerable and non-configurable:
199+
For instance, `Math.PI` is non-writable, non-enumerable and non-configurable:
197200

198201
```js run
199202
let descriptor =Object.getOwnPropertyDescriptor(Math, 'PI');
@@ -216,7 +219,13 @@ Math.PI = 3; // Error
216219
// delete Math.PI won't work either
217220
```
218221

219-
Making a property non-configurable is a one-way road. We cannot change it back, because `defineProperty` doesn't work on non-configurable properties.
222+
Making a property non-configurable is a one-way road. We cannot change it back with `defineProperty`.
223+
224+
To be precise, non-configurability imposes several restrictions on `defineProperty`:
225+
1. Can't change `configurable` flag.
226+
2. Can't change `enumerable` flag.
227+
3. Can't change `writable: false` to `true` (the other way round works).
228+
4. Can't change `get/set` for an accessor property (but can assign them if absent).
220229

221230
Here we are making `user.name` a "forever sealed" constant:
222231

@@ -234,13 +243,15 @@ Object.defineProperty(user, "name",{
234243
// all this won't work:
235244
// user.name = "Pete"
236245
// delete user.name
237-
// defineProperty(user, "name", ...)
246+
// defineProperty(user, "name", {value: "Pete" })
238247
Object.defineProperty(user, "name",{writable:true}); // Error
239248
*/!*
240249
```
241250

242-
```smart header="Errors appear only in use strict"
243-
In the non-strict mode, no errors occur when writing to read-only properties and such. But the operation still won't succeed. Flag-violating actions are just silently ignored in non-strict.
251+
```smart header="\"Non-configurable\" doesn't mean \"non-writable\""
252+
Notable exception: a value of non-configurable, but writable property can be changed.
253+
254+
The idea of `configurable: false` is to prevent changes to property flags and its deletion, not changes to its value.
244255
```
245256

246257
## Object.defineProperties
@@ -298,13 +309,13 @@ Property descriptors work at the level of individual properties.
298309
There are also methods that limit access to the *whole* object:
299310

300311
[Object.preventExtensions(obj)](mdn:js/Object/preventExtensions)
301-
: Forbids to add properties to the object.
312+
: Forbids the addition of new properties to the object.
302313

303314
[Object.seal(obj)](mdn:js/Object/seal)
304-
: Forbids to add/remove properties, sets for all existing properties`configurable: false`.
315+
: Forbids adding/removing of properties. Sets `configurable: false`for all existing properties.
305316

306317
[Object.freeze(obj)](mdn:js/Object/freeze)
307-
: Forbids to add/remove/change properties, sets for all existing properties`configurable: false, writable: false`.
318+
: Forbids adding/removing/changing of properties. Sets `configurable: false, writable: false` for all existing properties.
308319

309320
And also there are tests for them:
310321

‎1-js/07-object-properties/02-property-accessors/article.md‎

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
There are two kinds of properties.
55

6-
The first kind is *data properties*. We already know how to work with them. Actually, all properties that we've been using till now were data properties.
6+
The first kind is *data properties*. We already know how to work with them. All properties that we've been using until now were data properties.
77

88
The second type of properties is something new. It's *accessor properties*. They are essentially functions that work on getting and setting a value, but look like regular properties to an external code.
99

@@ -27,14 +27,14 @@ The getter works when `obj.propName` is read, the setter -- when it is assigned.
2727

2828
For instance, we have a `user` object with `name` and `surname`:
2929

30-
```js run
30+
```js
3131
let user ={
3232
name:"John",
3333
surname:"Smith"
3434
};
3535
```
3636

37-
Now we want to add a "fullName" property, that should be "John Smith". Of course, we don't want to copy-paste existing information, so we can implement it as an accessor:
37+
Now we want to add a `fullName` property, that should be `"John Smith"`. Of course, we don't want to copy-paste existing information, so we can implement it as an accessor:
3838

3939
```js run
4040
let user ={
@@ -55,7 +55,19 @@ alert(user.fullName); // John Smith
5555

5656
From outside, an accessor property looks like a regular one. That's the idea of accessor properties. We don't *call*`user.fullName` as a function, we *read* it normally: the getter runs behind the scenes.
5757

58-
As of now, `fullName` has only a getter. If we attempt to assign `user.fullName=`, there will be an error.
58+
As of now, `fullName` has only a getter. If we attempt to assign `user.fullName=`, there will be an error:
59+
60+
```js run
61+
let user ={
62+
getfullName(){
63+
return`...`;
64+
}
65+
};
66+
67+
*!*
68+
user.fullName="Test"; // Error (property has only a getter)
69+
*/!*
70+
```
5971

6072
Let's fix it by adding a setter for `user.fullName`:
6173

@@ -82,25 +94,15 @@ alert(user.name); // Alice
8294
alert(user.surname); // Cooper
8395
```
8496

85-
Now we have a "virtual" property. It is readable and writable, but in fact does not exist.
86-
87-
```smart header="Accessor properties are only accessible with get/set"
88-
Once a property is defined with `get prop()` or `set prop()`, it's an accessor property, not a data properety any more.
89-
90-
- If there's a getter -- we can read `object.prop`, othrewise we can't.
91-
- If there's a setter -- we can set `object.prop=...`, othrewise we can't.
92-
93-
And in either case we can't `delete` an accessor property.
94-
```
95-
97+
As the result, we have a "virtual" property `fullName`. It is readable and writable.
9698

9799
## Accessor descriptors
98100

99-
Descriptors for accessor properties are different -- as compared with data properties.
101+
Descriptors for accessor properties are different from those for data properties.
100102

101-
For accessor properties, there is no `value`and`writable`, but instead there are `get` and `set` functions.
103+
For accessor properties, there is no `value`or`writable`, but instead there are `get` and `set` functions.
102104

103-
So an accessor descriptor may have:
105+
That is, an accessor descriptor may have:
104106

105107
-**`get`** -- a function without arguments, that works when a property is read,
106108
-**`set`** -- a function with one argument, that is called when the property is set,
@@ -132,7 +134,7 @@ alert(user.fullName); // John Smith
132134
for(let key in user) alert(key); // name, surname
133135
```
134136

135-
Please note once again that a property can be either an accessor or a data property, not both.
137+
Please note that a property can be either an accessor (has `get/set` methods) or a data property (has a `value`), not both.
136138

137139
If we try to supply both `get` and `value` in the same descriptor, there will be an error:
138140

@@ -151,9 +153,9 @@ Object.defineProperty({}, 'prop',{
151153

152154
## Smarter getters/setters
153155

154-
Getters/setters can be used as wrappers over "real" property values to gain more control over them.
156+
Getters/setters can be used as wrappers over "real" property values to gain more control over operations with them.
155157

156-
For instance, if we want to forbid too short names for `user`, we can store `name` in a special property `_name`. And filter assignments in the setter:
158+
For instance, if we want to forbid too short names for `user`, we can have a setter `name`and keep the value in a separate property `_name`:
157159

158160
```js run
159161
let user ={
@@ -176,14 +178,16 @@ alert(user.name); // Pete
176178
user.name=""; // Name is too short...
177179
```
178180

179-
Technically, the external code may still access the name directly by using `user._name`. But there is a widely known agreement that properties starting with an underscore `"_"` are internal and should not be touched from outside the object.
181+
So, the name is stored in `_name` property, and the access is done via getter and setter.
182+
183+
Technically, external code is able to access the name directly by using `user._name`. But there is a widely known convention that properties starting with an underscore `"_"` are internal and should not be touched from outside the object.
180184

181185

182186
## Using for compatibility
183187

184-
One of the great ideas behind getters and setters -- they allow to take control over a "normal" data property and tweak it at any moment.
188+
One of the great uses of accessors is that they allow to take control over a "regular" data property at any moment by replacing it with a getter and a setter and tweak its behavior.
185189

186-
For instance, we started implementing user objects using data properties `name` and `age`:
190+
Imagine we started implementing user objects using data properties `name` and `age`:
187191

188192
```js
189193
functionUser(name, age){
@@ -209,9 +213,11 @@ let john = new User("John", new Date(1992, 6, 1));
209213

210214
Now what to do with the old code that still uses `age` property?
211215

212-
We can try to find all such places and fix them, but that takes time and can be hard to do if that code is written by other people. And besides, `age` is a nice thing to have in `user`, right? In some places it's just what we want.
216+
We can try to find all such places and fix them, but that takes time and can be hard to do if that code is used by many other people. And besides, `age` is a nice thing to have in `user`, right?
217+
218+
Let's keep it.
213219

214-
Adding a getter for `age`mitigates the problem:
220+
Adding a getter for `age`solves the problem:
215221

216222
```js run no-beautify
217223
functionUser(name, birthday){

0 commit comments

Comments
(0)