minor
This commit is contained in:
parent
3ba28aa104
commit
34e9cdca36
10 changed files with 55 additions and 62 deletions
|
@ -322,7 +322,7 @@ alert( *!*key*/!* in user ); // true, takes the name from key and checks for suc
|
|||
```
|
||||
|
||||
````smart header="Using \"in\" for properties that store `undefined`"
|
||||
Usually, the strict comparison `"=== undefined"` check works fine. But there's a special case when it fails, but `"in"` works correctly.
|
||||
Usually, the strict comparison `"=== undefined"` check the property existance just fine. But there's a special case when it fails, but `"in"` works correctly.
|
||||
|
||||
It's when an object property exists, but stores `undefined`:
|
||||
|
||||
|
@ -569,7 +569,7 @@ user.age = 25; // (*)
|
|||
alert(user.age); // 25
|
||||
```
|
||||
|
||||
It might seem that the line `(*)` would cause an error, but no, there's totally no problem. That's because `const` fixes the value of `user` itself. And here `user` stores the reference to the same object all the time. The line `(*)` goes *inside* the object, it doesn't reassign `user`.
|
||||
It might seem that the line `(*)` would cause an error, but no, there's totally no problem. That's because `const` fixes only value of `user` itself. And here `user` stores the reference to the same object all the time. The line `(*)` goes *inside* the object, it doesn't reassign `user`.
|
||||
|
||||
The `const` would give an error if we try to set `user` to something else, for instance:
|
||||
|
||||
|
|
|
@ -192,9 +192,7 @@ 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -230,22 +228,29 @@ For global symbols, not only `Symbol.for(key)` returns a symbol by name, but the
|
|||
For instance:
|
||||
|
||||
```js run
|
||||
// get symbol by name
|
||||
let sym = Symbol.for("name");
|
||||
let sym2 = Symbol.for("id");
|
||||
|
||||
// get name from symbol
|
||||
// get name by symbol
|
||||
alert( Symbol.keyFor(sym) ); // name
|
||||
alert( Symbol.keyFor(sym2) ); // id
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
That said, any symbols have `description` property.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
alert( Symbol.keyFor(Symbol.for("name")) ); // name, global symbol
|
||||
let globalSymbol = Symbol.for("name");
|
||||
let localSymbol = Symbol("name");
|
||||
|
||||
alert( Symbol.keyFor(Symbol("name2")) ); // undefined, the argument isn't a global symbol
|
||||
alert( Symbol.keyFor(globalSymbol) ); // name, global symbol
|
||||
alert( Symbol.keyFor(localSymbol) ); // undefined, not global
|
||||
|
||||
alert( localSymbol.description ); // name
|
||||
```
|
||||
|
||||
## System symbols
|
||||
|
@ -281,4 +286,4 @@ Symbols have two main use cases:
|
|||
|
||||
2. There are many system symbols used by JavaScript which are accessible as `Symbol.*`. We can use them to alter some built-in behaviors. For instance, later in the tutorial we'll use `Symbol.iterator` for [iterables](info:iterable), `Symbol.toPrimitive` to setup [object-to-primitive conversion](info:object-toprimitive) and so on.
|
||||
|
||||
Technically, symbols are not 100% hidden. There is a built-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows us to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. So they are not really hidden. But most libraries, built-in methods and syntax constructs adhere to a common agreement that they are. And the one who explicitly calls the aforementioned methods probably understands well what he's doing.
|
||||
Technically, symbols are not 100% hidden. There is a built-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows us to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. So they are not really hidden. But most libraries, built-in functions and syntax constructs don't use these methods.
|
||||
|
|
|
@ -111,6 +111,7 @@ let user = {
|
|||
|
||||
sayHi() {
|
||||
*!*
|
||||
// "this" is the "current object"
|
||||
alert(this.name);
|
||||
*/!*
|
||||
}
|
||||
|
@ -176,7 +177,7 @@ function sayHi() {
|
|||
}
|
||||
```
|
||||
|
||||
The value of `this` is evaluated during the run-time, depending on the context. And it can be anything.
|
||||
The value of `this` is evaluated during the run-time, depending on the context.
|
||||
|
||||
For instance, here the same function is assigned to two different objects and has different "this" in the calls:
|
||||
|
||||
|
|
|
@ -15,9 +15,7 @@ In the chapter <info:type-conversions> we've seen the rules for numeric, string
|
|||
|
||||
We can fine-tune string and numeric conversion, using special object methods.
|
||||
|
||||
The conversion algorithm is called `ToPrimitive` in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive). It's called with a "hint" that specifies the conversion type.
|
||||
|
||||
There are three variants:
|
||||
There are three variants of type conversion, so-called "hints", described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive):
|
||||
|
||||
`"string"`
|
||||
: For an object-to-string conversion, when we're doing an operation on an object that expects a string, like `alert`:
|
||||
|
@ -66,7 +64,7 @@ Please note -- there are only three hints. It's that simple. There is no "boolea
|
|||
|
||||
**To do the conversion, JavaScript tries to find and call three object methods:**
|
||||
|
||||
1. Call `obj[Symbol.toPrimitive](hint)` if the method exists,
|
||||
1. Call `obj[Symbol.toPrimitive](hint)` - the method with the symbolic key `Symbol.toPrimitive` (system symbol), if such method exists,
|
||||
2. Otherwise if hint is `"string"`
|
||||
- try `obj.toString()` and `obj.valueOf()`, whatever exists.
|
||||
3. Otherwise if hint is `"number"` or `"default"`
|
||||
|
@ -78,9 +76,9 @@ Let's start from the first method. There's a built-in symbol named `Symbol.toPri
|
|||
|
||||
```js
|
||||
obj[Symbol.toPrimitive] = function(hint) {
|
||||
// return a primitive value
|
||||
// must return a primitive value
|
||||
// hint = one of "string", "number", "default"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
For instance, here `user` object implements it:
|
||||
|
@ -138,6 +136,8 @@ alert(+user); // valueOf -> 1000
|
|||
alert(user + 500); // valueOf -> 1500
|
||||
```
|
||||
|
||||
As we can see, the behavior is the same as the previous example with `Symbol.toPrimitive`.
|
||||
|
||||
Often we want a single "catch-all" place to handle all primitive conversions. In this case, we can implement `toString` only, like this:
|
||||
|
||||
```js run
|
||||
|
@ -171,25 +171,24 @@ In contrast, `Symbol.toPrimitive` *must* return a primitive, otherwise there wil
|
|||
|
||||
## Further operations
|
||||
|
||||
An operation that initiated the conversion gets that primitive, and then continues to work with it, applying further conversions if necessary.
|
||||
An operation that initiated the conversion gets the primitive, and then continues to work with it, applying further conversions if necessary.
|
||||
|
||||
For instance:
|
||||
|
||||
- Mathematical operations (except binary plus) perform `ToNumber` conversion:
|
||||
- Mathematical operations, except binary plus, convert the primitive to a number:
|
||||
|
||||
```js run
|
||||
let obj = {
|
||||
toString() { // toString handles all conversions in the absence of other methods
|
||||
// toString handles all conversions in the absence of other methods
|
||||
toString() {
|
||||
return "2";
|
||||
}
|
||||
};
|
||||
|
||||
alert(obj * 2); // 4, ToPrimitive gives "2", then it becomes 2
|
||||
alert(obj * 2); // 4, object converted to primitive "2", then multiplication made it a number
|
||||
```
|
||||
|
||||
- Binary plus checks the primitive -- if it's a string, then it does concatenation, otherwise it performs `ToNumber` and works with numbers.
|
||||
|
||||
String example:
|
||||
- Binary plus will concatenate strings in the same situation:
|
||||
```js run
|
||||
let obj = {
|
||||
toString() {
|
||||
|
@ -200,24 +199,12 @@ For instance:
|
|||
alert(obj + 2); // 22 (ToPrimitive returned string => concatenation)
|
||||
```
|
||||
|
||||
Number example:
|
||||
```js run
|
||||
let obj = {
|
||||
toString() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
alert(obj + 2); // 3 (ToPrimitive returned boolean, not string => ToNumber)
|
||||
```
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
The object-to-primitive conversion is called automatically by many built-in functions and operators that expect a primitive as a value.
|
||||
|
||||
There are 3 types (hints) of it:
|
||||
- `"string"` (for `alert` and other string conversions)
|
||||
- `"string"` (for `alert` and other operations that need a string)
|
||||
- `"number"` (for maths)
|
||||
- `"default"` (few operators)
|
||||
|
||||
|
|
|
@ -17,8 +17,10 @@ Here's the demo of the code:
|
|||
|
||||
```js
|
||||
let accumulator = new Accumulator(1); // initial value 1
|
||||
|
||||
accumulator.read(); // adds the user-entered value
|
||||
accumulator.read(); // adds the user-entered value
|
||||
|
||||
alert(accumulator.value); // shows the sum of these values
|
||||
```
|
||||
|
||||
|
|
|
@ -215,6 +215,8 @@ john = {
|
|||
*/
|
||||
```
|
||||
|
||||
To create complex objects, there's a more advanced syntax, [classes](info:classes), that we'll cover later.
|
||||
|
||||
## Summary
|
||||
|
||||
- Constructor functions or, briefly, constructors, are regular functions, but there's a common agreement to name them with capital letter first.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue