Skip to content

Commit b0976b5

Browse files
committed
up
1 parent f99574f commit b0976b5

File tree

153 files changed

+585
-528
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

153 files changed

+585
-528
lines changed

1-js/9-object-inheritance/01-property-flags-descriptors/article.md

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11

2-
# Property flags and descriptors [todo move to objects?]
3-
4-
Now as we know how to work with primitives and several concrete object-based types, let's return to objects in general.
2+
# Property flags and descriptors
53

64
As we know, objects can store properties.
75

8-
But an object property is actually more complex thing than just a "key-value" mapping.
6+
Till now, a property was a simple "key-value" pair to us. But an object property is actually more complex and tunable thing.
97

108
[cut]
119

@@ -17,22 +15,22 @@ Object properties, besides a **`value`**, have three special attributes (so-call
1715
- **`enumerable`** -- if `true`, then listed in loops, otherwise not listed.
1816
- **`configurable`** -- if `true`, the property can be deleted and these attributes can be modified, otherwise not.
1917

20-
We didn't see them yet, because by default they are concealed. When we create a property "the usual way", all of them are `true`. But we also can change them any time.
18+
We didn't see them yet, because generally they do not show up. When we create a property "the usual way", all of them are `true`. But we also can change them any time.
2119

22-
First, let's see how to read the flags.
20+
First, let's see how to get those flags.
2321

24-
The method [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) allows to query the information about a property.
22+
The method [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) allows to query the *full* information about a property.
2523

2624
The syntax is:
2725
```js
2826
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
2927
```
3028

3129
`obj`
32-
: The object to get information about.
30+
: The object to get information from.
3331

3432
`propertyName`
35-
: The name of the property of interest.
33+
: The name of the property.
3634

3735
The returned value is a so-called "property descriptor" object: it contains the value and all the flags.
3836

@@ -46,17 +44,17 @@ let user = {
4644
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
4745

4846
alert( JSON.stringify(descriptor, null, 2 ) );
49-
/* descriptor:
47+
/* property descriptor:
5048
{
5149
"value": "John",
5250
"writable": true,
5351
"enumerable": true,
5452
"configurable": true
5553
}
56-
*/
54+
*/
5755
```
5856

59-
Now, we'll use [Object.defineProperty](mdn:js/Object/defineProperty) to change flags.
57+
To change the flags, we can use [Object.defineProperty](mdn:js/Object/defineProperty).
6058

6159
The syntax is:
6260

@@ -70,23 +68,23 @@ Object.defineProperty(obj, propertyName, descriptor)
7068
`descriptor`
7169
: Property descriptor to apply.
7270

73-
If the property exist, it update its flags.
74-
75-
Otherwise, it creates the property with the provided value and flags. Please note, that if a flag is not supplied, it is assumed `false`.
71+
If the property exist, it updates its flags, otherwise, it creates the property with the given value and flags. Please note, that if a flag is not supplied, it is assumed `false`.
7672

7773
For instance, here a property `name` is created with all falsy flags:
7874

7975
```js run
8076
let user = {};
8177

78+
*!*
8279
Object.defineProperty(user, "name", {
8380
value: "John"
8481
});
82+
*/!*
8583

8684
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
8785

8886
alert( JSON.stringify(descriptor, null, 2 ) );
89-
/* compare it with "normally created" user.name above:
87+
/*
9088
{
9189
"value": "John",
9290
*!*
@@ -96,7 +94,9 @@ alert( JSON.stringify(descriptor, null, 2 ) );
9694
*/!*
9795
}
9896
*/
99-
```
97+
```
98+
99+
Compare it with "normally created" user.name above: now all flags are falsy. If that's not what we want then we'd better to set them to `true` in the `descriptor`.
100100

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

@@ -143,9 +143,11 @@ user.name = "Alice"; // Error
143143

144144
## Non-enumerable
145145

146-
Now let's a custom `toString` to `user`.
146+
Now let's a custom `toString` to `user`.
147147

148-
Normally, a built-in `toString` for objects is non-enumerable, it does not show up in `for..in`. So we'll make ours behave the same.
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`.
149+
150+
...But 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:
149151

150152
```js run
151153
let user = {
@@ -170,11 +172,17 @@ Object.defineProperty(user, "toString", {
170172
for(let key in user) alert(key); // name
171173
```
172174

173-
Non-enumerable properties are also excluded from `Object.keys`.
175+
Non-enumerable properties are also excluded from `Object.keys`:
176+
177+
```js
178+
alert(Object.keys(user)); // name
179+
```
180+
181+
174182

175183
## Non-configurable
176184

177-
Non-configurable flag is often preset for built-in objects and properties.
185+
The non-configurable flag (`configurable:false`) is usually set for built-in objects and properties.
178186

179187
A non-configurable property can not be deleted or altered with `defineProperty`.
180188

@@ -193,17 +201,17 @@ alert( JSON.stringify(descriptor, null, 2 ) );
193201
}
194202
*/
195203
```
196-
So, a programmer is unable to change the value of that built-in constant or overwrite it.
204+
So, a programmer is unable to change the value of `Math.PI` or overwrite it.
197205

198206
```js run
199207
Math.PI = 3; // Error
200208

201209
// delete Math.PI won't work either
202210
```
203211

204-
Making non-configurable is one-way road. We cannot change it back, because `defineProperty` doesn't work on non-configurable properties.
212+
Making a property non-configurable is one-way road. We cannot change it back, because `defineProperty` doesn't work on non-configurable properties.
205213

206-
Here `user.name` is a forever sealed constant:
214+
Here we are making `user.name` a "forever sealed" constant:
207215

208216
```js run
209217
let user = { };
@@ -215,45 +223,70 @@ Object.defineProperty(user, "name", {
215223
});
216224

217225
*!*
218-
// can't change it or its flags
219-
// user.name = "Pete" won't work
220-
// delete user.name won't work
221-
// defineProperty won't work either:
226+
// won't be able to change user.name or its flags
227+
// all this won't work:
228+
// user.name = "Pete"
229+
// delete user.name
230+
// defineProperty(user, "name", ...)
222231
Object.defineProperty(user, "name", {writable: true}); // Error
223232
*/!*
224233
```
225234

226235
```smart header="Errors appear only in use strict"
227-
In non-strict mode, there are no errors for writing to read-only properties and such, flag-violating actions are silently ignored.
236+
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.
228237
```
229238

230-
## Many properties at once
239+
## Object.defineProperties
240+
241+
There's a method [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) that allows to define many properties at once.
242+
243+
The syntax is:
244+
245+
```js
246+
Object.defineProperties(obj, {
247+
prop1: descriptor1,
248+
prop2: descriptor2
249+
// ...
250+
});
251+
```
231252

232-
There's a method [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) that allows to define many properties at once:
253+
For instance:
233254

234255
```js
235256
Object.defineProperties(user, {
236-
name: { writable: false },
237-
surname: { ... },
257+
name: { value: "John", writable: false },
258+
surname: { value: "Smith", writable: false },
238259
// ...
239260
});
240261
```
241262

242-
And, to get all descriptors, use [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors).
263+
So, we can set many properties at once.
264+
265+
## Object.getOwnPropertyDescriptors
243266

244-
Together they can be used as an "property flags-aware" way of cloning an object:
267+
To get many descriptors at once, we can use the method [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors).
268+
269+
Together with `Object.defineProperties` it can be used as an "flags-aware" way of cloning an object:
245270

246271
```js
247272
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
248273
```
249274

250-
(For the clone to be fully identical, we need to care about one more thing: "prototype", we'll see into it soon in the chapter [todo])
275+
Normally when we clone an object, we use an assignment to copy properties, like this:
276+
277+
```js
278+
for(let key in user) {
279+
clone[key] = user[key]
280+
}
281+
```
282+
283+
...But that does not copy flags. So if we want a "better" clone then `Object.defineProperties` is preferred.
251284

252285
## Sealing an object globally
253286

254-
Property descriptors allow to forbid modifications of individual properties.
287+
Property descriptors work at the level of individual properties.
255288

256-
There are also methods that limit access to the whole object:
289+
There are also methods that limit access to the *whole* object:
257290

258291
[Object.preventExtensions(obj)](mdn:js/Object/preventExtensions)
259292
: Forbids to add properties to the object.
@@ -264,7 +297,7 @@ There are also methods that limit access to the whole object:
264297
[Object.freeze(obj)](mdn:js/Object/freeze)
265298
: Forbids to add/remove/change properties, sets for all existing properties `configurable: false, writable: false`.
266299

267-
And the tests for them:
300+
And also there are tests for them:
268301

269302
[Object.isExtensible(obj)](mdn:js/Object/isExtensible)
270303
: Returns `false` if adding properties is forbidden, otherwise `true`.
@@ -276,10 +309,3 @@ And the tests for them:
276309
: Returns `true` if adding/removing/changing properties is forbidden, and all current properties are `configurable: false, writable: false`.
277310

278311
These methods are rarely used in practice.
279-
280-
281-
282-
283-
## Tasks
284-
285-
Check: new property has all non-enum, test

0 commit comments

Comments
 (0)