minor fixes
This commit is contained in:
parent
d1a455080d
commit
f9feec2d69
7 changed files with 64 additions and 60 deletions
|
@ -94,7 +94,7 @@ But it should be two separate statements, not one. Such a merging in this case i
|
|||
|
||||
We recommend putting semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community. Let's note once again -- *it is possible* to leave out semicolons most of the time. But it's safer -- especially for a beginner -- to use them.
|
||||
|
||||
## Comments
|
||||
## Comments [#code-comments]
|
||||
|
||||
As time goes on, programs become more and more complex. It becomes necessary to add *comments* which describe what the code does and why.
|
||||
|
||||
|
|
|
@ -101,7 +101,9 @@ For multiword properties, the dot access doesn't work:
|
|||
user.likes birds = true
|
||||
```
|
||||
|
||||
That's because the dot requires the key to be a valid variable identifier. That is: no spaces and other limitations.
|
||||
JavaScript doesn't understand that. It thinks that we address `user.likes`, and then gives a syntax error when comes across unexpected `birds`.
|
||||
|
||||
The dot requires the key to be a valid variable identifier. That implies: contains no spaces, doesn't start with a digit and doesn't include special characters (`$` и `_` are allowed).
|
||||
|
||||
There's an alternative "square bracket notation" that works with any string:
|
||||
|
||||
|
@ -203,43 +205,6 @@ Square brackets are much more powerful than the dot notation. They allow any pro
|
|||
|
||||
So most of the time, when property names are known and simple, the dot is used. And if we need something more complex, then we switch to square brackets.
|
||||
|
||||
|
||||
|
||||
````smart header="Reserved words are allowed as property names"
|
||||
A variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
|
||||
|
||||
But for an object property, there's no such restriction. Any name is fine:
|
||||
|
||||
```js run
|
||||
let obj = {
|
||||
for: 1,
|
||||
let: 2,
|
||||
return: 3
|
||||
};
|
||||
|
||||
alert( obj.for + obj.let + obj.return ); // 6
|
||||
```
|
||||
|
||||
Basically, any name is allowed, but there's a special one: `"__proto__"` that gets special treatment for historical reasons. For instance, we can't set it to a non-object value:
|
||||
|
||||
```js run
|
||||
let obj = {};
|
||||
obj.__proto__ = 5;
|
||||
alert(obj.__proto__); // [object Object], didn't work as intended
|
||||
```
|
||||
|
||||
As we see from the code, the assignment to a primitive `5` is ignored.
|
||||
|
||||
That can become a source of bugs and even vulnerabilities if we intend to store arbitrary key-value pairs in an object, and allow a visitor to specify the keys.
|
||||
|
||||
In that case the visitor may choose `__proto__` as the key, and the assignment logic will be ruined (as shown above).
|
||||
|
||||
There is a way to make objects treat `__proto__` as a regular property, which we'll cover later, but first we need to know more about objects.
|
||||
|
||||
There's also another data structure [Map](info:map-set), that we'll learn in the chapter <info:map-set>, which supports arbitrary keys.
|
||||
````
|
||||
|
||||
|
||||
## Property value shorthand
|
||||
|
||||
In real code we often use existing variables as values for property names.
|
||||
|
@ -284,7 +249,63 @@ let user = {
|
|||
};
|
||||
```
|
||||
|
||||
## Existence check
|
||||
## Property names limitations
|
||||
|
||||
Property names (keys) must be either strings or symbols (a special type for identifiers, to be covered later).
|
||||
|
||||
Other types are automatically converted to strings.
|
||||
|
||||
For instance, a number `0` becomes a string `"0"` when used as a property key:
|
||||
|
||||
```js run
|
||||
let obj = {
|
||||
0: "test" // same as "0": "test"
|
||||
};
|
||||
|
||||
// both alerts access the same property (the number 0 is converted to string "0")
|
||||
alert( obj["0"] ); // test
|
||||
alert( obj[0] ); // test (same property)
|
||||
```
|
||||
|
||||
**Reserved words are allowed as property names.**
|
||||
|
||||
As we already know, a variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
|
||||
|
||||
But for an object property, there's no such restriction. Any name is fine:
|
||||
|
||||
```js run
|
||||
let obj = {
|
||||
for: 1,
|
||||
let: 2,
|
||||
return: 3
|
||||
};
|
||||
|
||||
alert( obj.for + obj.let + obj.return ); // 6
|
||||
```
|
||||
|
||||
We can use any string as a key, but there's a special property named `__proto__` that gets special treatment for historical reasons.
|
||||
|
||||
For instance, we can't set it to a non-object value:
|
||||
|
||||
```js run
|
||||
let obj = {};
|
||||
obj.__proto__ = 5; // assign a number
|
||||
alert(obj.__proto__); // [object Object] - the value is an object, didn't work as intended
|
||||
```
|
||||
|
||||
As we see from the code, the assignment to a primitive `5` is ignored.
|
||||
|
||||
The nature of `__proto__` will be revealed in detail later in the chapter [](info:prototype-inheritance).
|
||||
|
||||
As for now, it's important to know that such behavior of `__proto__` can become a source of bugs and even vulnerabilities if we intend to store user-provided keys in an object.
|
||||
|
||||
The problem is that a visitor may choose `__proto__` as the key, and the assignment logic will be ruined (as shown above).
|
||||
|
||||
Later we'll see workarounds for the problem:
|
||||
1. We'll see how to make an objects treat `__proto__` as a regular property in the chapter [](info:prototype-methods).
|
||||
2. There's also study another data structure [Map](info:map-set) in the chapter <info:map-set>, which supports arbitrary keys.
|
||||
|
||||
## Property existance test, "in" operator
|
||||
|
||||
A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined:
|
||||
|
||||
|
|
|
@ -178,22 +178,6 @@ alert( clone[id] ); // 123
|
|||
|
||||
There's no paradox here. That's by design. The idea is that when we clone an object or merge objects, we usually want *all* properties to be copied (including symbols like `id`).
|
||||
|
||||
````smart header="Property keys of other types are coerced to strings"
|
||||
We can only use strings or symbols as keys in objects. Other types are converted to strings.
|
||||
|
||||
For instance, a number `0` becomes a string `"0"` when used as a property key:
|
||||
|
||||
```js run
|
||||
let obj = {
|
||||
0: "test" // same as "0": "test"
|
||||
};
|
||||
|
||||
// both alerts access the same property (the number 0 is converted to string "0")
|
||||
alert( obj["0"] ); // test
|
||||
alert( obj[0] ); // test (same property)
|
||||
```
|
||||
````
|
||||
|
||||
## Global symbols
|
||||
|
||||
As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be same entities. For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property.
|
||||
|
|
|
@ -294,7 +294,7 @@ Objects that can be used in `for..of` are called *iterable*.
|
|||
|
||||
- Technically, iterables must implement the method named `Symbol.iterator`.
|
||||
- The result of `obj[Symbol.iterator]` is called an *iterator*. It handles the further iteration process.
|
||||
- An iterator must have the method named `next()` that returns an object `{done: Boolean, value: any}`, here `done:true` denotes the iteration end, otherwise the `value` is the next value.
|
||||
- An iterator must have the method named `next()` that returns an object `{done: Boolean, value: any}`, here `done:true` denotes the end of the iteration process, otherwise the `value` is the next value.
|
||||
- The `Symbol.iterator` method is called automatically by `for..of`, but we also can do it directly.
|
||||
- Built-in iterables like strings or arrays, also implement `Symbol.iterator`.
|
||||
- String iterator knows about surrogate pairs.
|
||||
|
|
|
@ -141,7 +141,6 @@ And here's another part of the code, maybe another file using it:
|
|||
let john = { name: "John" };
|
||||
|
||||
countUser(john); // count his visits
|
||||
countUser(john);
|
||||
|
||||
// later john leaves us
|
||||
john = null;
|
||||
|
|
|
@ -16,7 +16,7 @@ The prototype is a little bit "magical". When we want to read a property from `o
|
|||
|
||||
The property `[[Prototype]]` is internal and hidden, but there are many ways to set it.
|
||||
|
||||
One of them is to use `__proto__`, like this:
|
||||
One of them is to use the special name `__proto__`, like this:
|
||||
|
||||
```js run
|
||||
let animal = {
|
||||
|
|
|
@ -268,7 +268,7 @@ If we're going to open a popup, a good practice is to inform the user about it.
|
|||
- Browsers block `open` calls from the code outside of user actions. Usually a notification appears, so that a user may allow them.
|
||||
- Browsers open a new tab by default, but if sizes are provided, then it'll be a popup window.
|
||||
- The popup may access the opener window using the `window.opener` property.
|
||||
- The main window and the popup can freely read and modify each other if they havee the same origin. Otherwise, they can change location of each other and [exchange messages.
|
||||
- The main window and the popup can freely read and modify each other if they have the same origin. Otherwise, they can change location of each other and [exchange messages](info:cross-window-communication).
|
||||
|
||||
To close the popup: use `close()` call. Also the user may close them (just like any other windows). The `window.closed` is `true` after that.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue