ok
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 5 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -1,10 +1,10 @@
|
||||||
# Methods of primitives
|
# Methods of primitives
|
||||||
|
|
||||||
JavaScript allows to work with primitives (strings, numbers etc) as if they were objects. They have methods and such. Of course, primitives are not objects (and here we plan to make it even more clear), but can be used like them.
|
JavaScript allows to work with primitives (strings, numbers etc) as if they were objects. They also have methods and such. Of course, primitives are not objects (and here we plan to make it even more clear), but can be used like them.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
Let's formulate the definitive distinction between primitives and objects.
|
Let's formulate the key distinction between primitives and objects.
|
||||||
|
|
||||||
A primitive
|
A primitive
|
||||||
: Is a value of a primitive type. There are 6 primitive types: `string`, `number`, `boolean`, `symbol`, `null` and `undefined`.
|
: Is a value of a primitive type. There are 6 primitive types: `string`, `number`, `boolean`, `symbol`, `null` and `undefined`.
|
||||||
|
@ -77,7 +77,7 @@ let n = 1.23456;
|
||||||
alert( n.toFixed(2) ); // 1.23
|
alert( n.toFixed(2) ); // 1.23
|
||||||
```
|
```
|
||||||
|
|
||||||
We'll see more specific methods in next chapters.
|
We'll see more specific methods in chapters <info:number> and <info:string>.
|
||||||
|
|
||||||
````warn header="null/undefined have no methods"
|
````warn header="null/undefined have no methods"
|
||||||
Special primitives `null` and `undefined` are exceptions. They have no corresponding "wrapper objects" and provide no methods. In a sense, they are "the most primitive".
|
Special primitives `null` and `undefined` are exceptions. They have no corresponding "wrapper objects" and provide no methods. In a sense, they are "the most primitive".
|
|
@ -31,7 +31,7 @@ alert(id1 == id2); // false
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are familiar with Ruby or another language that uses the "symbol" concept -- please don't be misguided. Javascript symbols are pretty different.
|
If you are familiar with Ruby or another language that also has some sort of "symbols" -- please don't be misguided. Javascript symbols are different.
|
||||||
|
|
||||||
|
|
||||||
## "Private" properties
|
## "Private" properties
|
||||||
|
@ -155,11 +155,11 @@ alert( obj[0] ); // test (same property)
|
||||||
|
|
||||||
Normally, all symbols are different. But sometimes we want same-named symbols to be the same.
|
Normally, all symbols are different. But sometimes we want same-named symbols to be the same.
|
||||||
|
|
||||||
For instance, different parts of our application want to use `Symbol("id")` meaning the exactly the same property.
|
For instance, different parts of our application want to access symbol `"id"` meaning the exactly the same property.
|
||||||
|
|
||||||
To achieve that, there's a *global symbol registry*. That's a place where we can create symbols and access them later, and it is guaranteed that repeating access by the same name returns the same symbol.
|
To achieve that, there exists a *global symbol registry*. We can create symbols in it and and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.
|
||||||
|
|
||||||
To can create or read a symbol, use `Symbol.for(name)`.
|
To can create or read a symbol in the registry, use `Symbol.for(name)`.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -174,17 +174,17 @@ let nameAgain = Symbol.for("name");
|
||||||
alert( name === nameAgain ); // true
|
alert( name === nameAgain ); // true
|
||||||
```
|
```
|
||||||
|
|
||||||
If we want an application-wide symbol, accessible everywhere in the code -- that's what the registry is for.
|
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.
|
||||||
|
|
||||||
```smart header="That sounds like Ruby"
|
```smart header="That sounds like Ruby"
|
||||||
In some programming languages, like Ruby, there's a single symbol per name.
|
In some programming languages, like Ruby, there's a single symbol per name.
|
||||||
|
|
||||||
In Javascript, as we can see, it's correct for global symbols.
|
In Javascript, as we can see, that's right for global symbols.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Symbol.keyFor
|
### 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 returns a name for a global symbol.
|
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 instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -197,9 +197,7 @@ alert( Symbol.keyFor(sym) ); // name
|
||||||
alert( Symbol.keyFor(sym2) ); // id
|
alert( Symbol.keyFor(sym2) ); // id
|
||||||
```
|
```
|
||||||
|
|
||||||
The `Symbol.keyFor` internally uses the global symbol registry, to look up the name for the symbol.
|
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`.
|
||||||
|
|
||||||
So it doesn't work for non-global symbols. If the symbol is not global, it returns `undefined.
|
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -215,18 +213,27 @@ For non-global symbols, the name is only used for debugging purposes.
|
||||||
|
|
||||||
There exist many "system" symbols that Javascript uses internally, and we can use them to fine-tune various aspects of our objects.
|
There exist many "system" symbols that Javascript uses internally, and we can use them to fine-tune various aspects of our objects.
|
||||||
|
|
||||||
They are listed in the specification in the [Well-known symbols](https://tc39.github.io/ecma262/#sec-well-known-symbols) table. For instance, `Symbol.toPrimitive` allows to describe object to primitive conversion. We'll see its use very soon. Other symbols will become more obvious as we'll study the corresponding language features.
|
They are listed in the specification in the [Well-known symbols](https://tc39.github.io/ecma262/#sec-well-known-symbols) table:
|
||||||
|
|
||||||
|
- `Symbol.hasInstance`
|
||||||
|
- `Symbol.isConcatSpreadable`
|
||||||
|
- `Symbol.iterator`
|
||||||
|
- `Symbol.toPrimitive`
|
||||||
|
- ...and so on.
|
||||||
|
|
||||||
|
For instance, `Symbol.toPrimitive` allows to describe object to primitive conversion. We'll see its use very soon.
|
||||||
|
|
||||||
|
Other symbols will also become familiar when we study the corresponding language features.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
- Symbol is a primitive type for unique identifiers.
|
- Symbol is a primitive type for unique identifiers.
|
||||||
- Symbols are created with `Symbol(name)` call.
|
- Symbols are created with `Symbol(name)` call.
|
||||||
- Symbols are useful if we want to create a field that only those who know the symbol can access.
|
- Symbols are useful if we want to create a field that only those who know the symbol can access.
|
||||||
|
- Symbols don't appear in `for..in` loops.
|
||||||
- Symbols created with `Symbol(name)` are always different, 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 return the same symbol.
|
- Symbols created with `Symbol(name)` are always different, 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 return the same symbol.
|
||||||
- There are system symbols used by Javascript and accessible as `Symbol.*`. We can use them to alter some built-in behaviors.
|
- There are system symbols used by Javascript and accessible as `Symbol.*`. We can use them to alter some built-in behaviors.
|
||||||
|
|
||||||
Symbols don't appear in `for..in` loops. As we'll see further, there are other means to get object properties which also ignore symbols, so they remain hidden.
|
Technically, symbols are not 100% hidden. There is a build-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows 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 completely hidden and private.
|
||||||
|
|
||||||
Technically though, there is still a way to discover all symbols of an object with a build-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols). 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 completely hidden and private.
|
|
||||||
|
|
||||||
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.
|
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.
|
|
@ -14,15 +14,15 @@ The process of object to primitive conversion can be customized, here we'll see
|
||||||
|
|
||||||
The conversion of an object to primitive value (a number or a string) is a rare thing in practice.
|
The conversion of an object to primitive value (a number or a string) is a rare thing in practice.
|
||||||
|
|
||||||
Just think about cases when such conversion may be necessary. For instance, numeric conversion happens when we compare an object against a primitive: `user == 18`. But what such comparison actually means? Are we going to compare `18` against user's age? Then it would be more obvious to write `user.age == 18`? And it's easier to read it too.
|
Just think about cases when such conversion may be necessary. For instance, numeric conversion happens when we compare an object against a primitive: `user > 18`. But what such comparison actually means? Are we going to compare `18` against user's age? Then it would be more obvious to write `user.age > 18`. And it's easier to read and understand it too.
|
||||||
|
|
||||||
Or, for a string conversion... Where does it happen? Usually, when we output an object. But simple ways of object-as-string output like `alert(user)` are only used for debugging and logging purposes. In projects, the output is more complicated, we may need to tune it by additional parameters, so it should be implemented by special methods.
|
Or, for a string conversion... Where does it happen? Usually, when we output an object. But simple ways of object-as-string output like `alert(user)` are only used for debugging and logging purposes. For real stuff, the output is more complicated, we may need to provide it additional parameters. That's why we usually implement it using object methods like `user.format()` or even in more advanced ways.
|
||||||
|
|
||||||
So, most of the time, it's more flexible and gives more readable code to explicitly write an object property or call a method than rely on the conversion.
|
So, most of the time, it's more flexible and gives more readable code to explicitly write an object property or call a method than rely on the conversion.
|
||||||
|
|
||||||
That said, there are still valid reasons why we should know how to-primitive conversion works.
|
That said, there are still valid reasons why we should know how to-primitive conversion works.
|
||||||
|
|
||||||
- The object-as-string kind of output is still useful sometimes. Without a customized conversion it will show `[object Object]`.
|
- Simple object-as-string output may be useable sometimes. Without a customized conversion it will show `[object Object]`.
|
||||||
- Many built-in objects implement their own to-primitive conversion, we plan to cover that.
|
- Many built-in objects implement their own to-primitive conversion, we plan to cover that.
|
||||||
- Sometimes it just happens (on mistake?), and we should understand what's going on.
|
- Sometimes it just happens (on mistake?), and we should understand what's going on.
|
||||||
- Okay, the final one. There are quizzes and questions on interviews that rely on that knowledge. Looks like people think it's a good sigh that person understands Javascript if he knows type conversions well.
|
- Okay, the final one. There are quizzes and questions on interviews that rely on that knowledge. Looks like people think it's a good sigh that person understands Javascript if he knows type conversions well.
|
||||||
|
@ -72,11 +72,11 @@ There are 3 types (also called "hints") of object-to-primitive conversion:
|
||||||
if (user == 1) { ... };
|
if (user == 1) { ... };
|
||||||
```
|
```
|
||||||
|
|
||||||
Still, there's some inconsistency here. The greater/less operator `<>` can work with both strings and numbers, it compares them differently: dictionary order is used for strings. Still, it uses "number" hint. That's for historical reasons.
|
There's some inconsistency here. The greater/less operator `<>` can work with both strings and numbers, it compares them differently. Still, it uses "number" hint. That's for historical reasons.
|
||||||
|
|
||||||
In practice, all built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And probably we should do the same.
|
In practice, all built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And probably we should do the same.
|
||||||
|
|
||||||
Please note -- there's only three. That simple. There is no "boolean" (all objects are `true` in boolean context) or anything else. And if we treat `"default"` and `"number"` the same, like most builtins do, then there are only two conversions. Not many.
|
Please note -- there are only three conversions. That simple. There is no "boolean" (all objects are `true` in boolean context) or anything else. And if we treat `"default"` and `"number"` the same, like most built-ins do, then there are only two conversions.
|
||||||
|
|
||||||
To do the conversion, Javascript tries to find and call these three object methods:
|
To do the conversion, Javascript tries to find and call these three object methods:
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ alert(+user); // valueOf -> 1000
|
||||||
alert(user + 500); // valueOf -> 1500
|
alert(user + 500); // valueOf -> 1500
|
||||||
```
|
```
|
||||||
|
|
||||||
In practice, we often want a single "catch-all" place to handle all primitive conversions. In this case we can implement `toString` only, like this:
|
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
|
```js run
|
||||||
let user = {
|
let user = {
|
||||||
|
@ -155,6 +155,9 @@ alert(user); // toString -> John
|
||||||
alert(user + 500); // toString -> John500
|
alert(user + 500); // toString -> John500
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In the absense of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions.
|
||||||
|
|
||||||
|
|
||||||
## ToPrimitive and ToString/ToNumber
|
## ToPrimitive and ToString/ToNumber
|
||||||
|
|
||||||
The important thing to know about all primitive-conversion methods is that they not necessarily return the "hinted" primitive.
|
The important thing to know about all primitive-conversion methods is that they not necessarily return the "hinted" primitive.
|
||||||
|
@ -167,11 +170,11 @@ An operation that was the reason for the conversion gets that primitive, and the
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
- All mathematical operations except binary plus apply `ToNumber`
|
- All mathematical operations except binary plus apply `ToNumber`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let obj = {
|
let obj = {
|
||||||
toString() { // toString used for everything in the absense of other methods
|
toString() { // toString used for numeric conversion in the absense of valueOf
|
||||||
return "2";
|
return "2";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -179,8 +182,9 @@ For instance:
|
||||||
alert(obj * 2); // 4
|
alert(obj * 2); // 4
|
||||||
```
|
```
|
||||||
|
|
||||||
- Binary plus first checks if it's a string, and then does concatenation, otherwise performs `ToNumber` and works with numbers.
|
- Binary plus first checks if the primitive is a string, and then does concatenation, otherwise performs `ToNumber` and works with numbers.
|
||||||
|
|
||||||
|
String example:
|
||||||
```js run
|
```js run
|
||||||
let obj = {
|
let obj = {
|
||||||
toString() {
|
toString() {
|
||||||
|
@ -188,9 +192,10 @@ For instance:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
alert(obj + 2); // 22 (string => concatenation)
|
alert(obj + 2); // 22 (ToPrimitive returned string => concatenation)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Number example:
|
||||||
```js run
|
```js run
|
||||||
let obj = {
|
let obj = {
|
||||||
toString() {
|
toString() {
|
||||||
|
@ -198,7 +203,7 @@ For instance:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
alert(obj + 2); // 3 (not string => ToNumber)
|
alert(obj + 2); // 3 (ToPrimitive returned boolean, not string => ToNumber)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Summary
|
## Summary
|
2
1-js/4-object-basics/index.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Object basics
|
||||||
|
|