fixes
This commit is contained in:
parent
ab13ef8685
commit
0a2b936133
5 changed files with 41 additions and 34 deletions
|
@ -70,7 +70,7 @@ While choosing a name try to use the most abstract word. Like `obj`, `data`, `va
|
|||
|
||||
...But what to do if `data` is already taken? Try `value`, it's also universal. After all, a variable eventually gets a *value*.
|
||||
|
||||
- **Name the variable by its type: `str`, `num`...**
|
||||
- **Name a variable by its type: `str`, `num`...**
|
||||
|
||||
Give them a try. A young ninja may wonder -- do such names make the code worse? Actually, yes!
|
||||
|
||||
|
@ -78,7 +78,7 @@ While choosing a name try to use the most abstract word. Like `obj`, `data`, `va
|
|||
|
||||
Indeed, the value type is easy to find out by debugging. But what's the meaning of the variable? Which string/number does it store? There's just no way to figure out without a good meditation!
|
||||
|
||||
- **...But what if there are no more such names?** Just add a letter: `item1, item2, elem5, data1`...
|
||||
- **...But what if there are no more such names?** Just add a number: `data1, item2, elem5`...
|
||||
|
||||
## Attention test
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ alert('Press the "Play" button in the upper-right corner to run');
|
|||
```
|
||||
|
||||
Examples that use modern JS will work only if your browser supports it.
|
||||
```
|
||||
````
|
||||
|
||||
```offline
|
||||
As you're reading the offline version, examples are not runnable. But they usually work :)
|
||||
|
|
|
@ -222,7 +222,11 @@ 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. If we want to store *arbitrary* (user-provided) keys, then such behavior can be the source of bugs and even vulnerabilities, because it's unexpected. There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in the chapter <info:map-set-weakmap-weakset>, which supports arbitrary keys.
|
||||
As we see from the code, the assignment to a primitive `5` is ignored.
|
||||
|
||||
That can be come a source of bugs and even vulnerabilies if we intent 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's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in the chapter <info:map-set-weakmap-weakset>, which supports arbitrary keys. Also there's a way to make objects treat `__proto__` as a regular property, but first we need to know more about objects to understand it.
|
||||
````
|
||||
|
||||
|
||||
|
|
|
@ -9,18 +9,25 @@ Till now we've only seen strings. Now let's see the advantages that symbols can
|
|||
|
||||
## Symbols
|
||||
|
||||
"Symbol" value represents an unique identifier with a given name.
|
||||
"Symbol" value represents an unique identifier.
|
||||
|
||||
A value of this type can be created using `Symbol(name)`:
|
||||
A value of this type can be created using `Symbol()`:
|
||||
|
||||
```js
|
||||
// id is a symbol with the name "id"
|
||||
// id is a new symbol
|
||||
let id = Symbol();
|
||||
```
|
||||
|
||||
We can also give symbol a description (also called a symbol name), mostly useful for debugging purposes:
|
||||
|
||||
```js
|
||||
// id is a symbol with the description "id"
|
||||
let id = Symbol("id");
|
||||
```
|
||||
|
||||
Symbols are guaranteed to be unique. Even if we create many symbols with the same name, they are different values.
|
||||
Symbols are guaranteed to be unique. Even if we create many symbols with the same description, they are different values. The description is just a label that doesn't affect anything.
|
||||
|
||||
For instance, here are two symbols with the same name -- they are not equal:
|
||||
For instance, here are two symbols with the same description -- they are not equal:
|
||||
|
||||
```js run
|
||||
let id1 = Symbol("id");
|
||||
|
@ -56,13 +63,11 @@ alert(id.toString()); // Symbol(id), now it works
|
|||
That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another.
|
||||
````
|
||||
|
||||
|
||||
|
||||
## "Hidden" properties
|
||||
|
||||
Symbols allow us to create "hidden" properties of an object, that no other part of code can occasionally access or overwrite.
|
||||
|
||||
For instance, if we want to store an "identifier" for the object `user`, we can create a symbol with the name `id` for it:
|
||||
For instance, if we want to store an "identifier" for the object `user`, we can use a symbol as a key for it:
|
||||
|
||||
```js run
|
||||
let user = { name: "John" };
|
||||
|
@ -72,11 +77,13 @@ user[id] = "ID Value";
|
|||
alert( user[id] ); // we can access the data using the symbol as the key
|
||||
```
|
||||
|
||||
Now let's imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other.
|
||||
What's the benefit over using `Symbol("id")` over a string `"id"`?
|
||||
|
||||
No problem. It can create its own `Symbol("id")`.
|
||||
Let's make the example a bit deeper to see that.
|
||||
|
||||
The script:
|
||||
Imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other.
|
||||
|
||||
Then that script can create its own `Symbol("id")`, like this:
|
||||
|
||||
```js
|
||||
// ...
|
||||
|
@ -87,7 +94,7 @@ user[id] = "Their id value";
|
|||
|
||||
There will be no conflict, because symbols are always different, even if they have the same name.
|
||||
|
||||
Please note that if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict:
|
||||
Now note that if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict:
|
||||
|
||||
```js run
|
||||
let user = { name: "John" };
|
||||
|
@ -176,25 +183,27 @@ 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 the same entities.
|
||||
As we've seen, usually all symbols are different, even if they have the same names. 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.
|
||||
|
||||
To achieve that, there exists a *global symbol registry*. We can create symbols in it and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.
|
||||
|
||||
In order to create or read a symbol in the registry, use `Symbol.for(name)`.
|
||||
In order to create or read a symbol in the registry, use `Symbol.for(key)`.
|
||||
|
||||
That call checks the global registry, and if there's a symbol described as `key`, then returns it, otherwise creates a new symbol `Symbol(key)` and stores it in the registry by the given `key`.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
// read from the global registry
|
||||
let name = Symbol.for("name"); // if the symbol did not exist, it is created
|
||||
let id = Symbol.for("id"); // if the symbol did not exist, it is created
|
||||
|
||||
// read it again
|
||||
let nameAgain = Symbol.for("name");
|
||||
let idAgain = Symbol.for("id");
|
||||
|
||||
// the same symbol
|
||||
alert( name === nameAgain ); // true
|
||||
alert( id === idAgain ); // true
|
||||
```
|
||||
|
||||
Symbols inside the registry are called *global symbols*. If we want an application-wide symbol, accessible everywhere in the code -- that's what they are for.
|
||||
|
@ -207,7 +216,7 @@ In JavaScript, as we can see, that's right for global symbols.
|
|||
|
||||
### Symbol.keyFor
|
||||
|
||||
For global symbols, not only `Symbol.for(name)` returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol.
|
||||
For global symbols, not only `Symbol.for(key)` returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -220,20 +229,16 @@ alert( Symbol.keyFor(sym) ); // name
|
|||
alert( Symbol.keyFor(sym2) ); // id
|
||||
```
|
||||
|
||||
The `Symbol.keyFor` internally uses the global symbol registry to look up the name for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and return `undefined`.
|
||||
The `Symbol.keyFor` internally uses the global symbol registry to look up the key for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and return `undefined`.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
alert( Symbol.keyFor(Symbol.for("name")) ); // name, global symbol
|
||||
|
||||
alert( Symbol.keyFor(Symbol("name2")) ); // undefined, non-global symbol
|
||||
alert( Symbol.keyFor(Symbol("name2")) ); // undefined, the argument isn't a global symbol
|
||||
```
|
||||
|
||||
So, for global symbols the name may be indeed helpful, as we can get a symbol by id.
|
||||
|
||||
And for non-global symbols the name is only used for debugging purposes, like printing out a symbol.
|
||||
|
||||
## System symbols
|
||||
|
||||
There exist many "system" symbols that JavaScript uses internally, and we can use them to fine-tune various aspects of our objects.
|
||||
|
@ -254,9 +259,9 @@ Other symbols will also become familiar when we study the corresponding language
|
|||
|
||||
`Symbol` is a primitive type for unique identifiers.
|
||||
|
||||
Symbols are created with `Symbol(name)` call.
|
||||
Symbols are created with `Symbol()` call with an optional description.
|
||||
|
||||
Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(name)` returns (creates if needed) a global symbol with the given name. Multiple calls of `Symbol.for` return exactly the same symbol.
|
||||
Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(key)` returns (creates if needed) a global symbol with `key` as the name. Multiple calls of `Symbol.for` return exactly the same symbol.
|
||||
|
||||
Symbols have two main use cases:
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# F.prototype
|
||||
|
||||
In modern JavaScript we can set a prototype using `__proto__`. But it wasn't like that all the time.
|
||||
In modern JavaScript we can set a prototype using `__proto__`, as described in the previous article. But it wasn't like that all the time.
|
||||
|
||||
[cut]
|
||||
|
||||
|
@ -10,9 +10,7 @@ But in the old times, there was another (and the only) way to set it: to use a `
|
|||
|
||||
## The "prototype" property
|
||||
|
||||
As we know already, `new F()` creates a new object. But what we didn't use yet `F.prototype` property.
|
||||
|
||||
That property is used by the JavaScript itself to set `[[Prototype]]` for new objects.
|
||||
As we know already, `new F()` creates a new object. In the process, its "magic" `F.prototype` property is used by the JavaScript itself to set `[[Prototype]]` for new objects.
|
||||
|
||||
**When a new object is created with `new F()`, the object's `[[Prototype]]` is set to `F.prototype`.**
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue