grammar, usage, punctuation edits, Part 1, sections 3.1 - 4.6
This commit is contained in:
parent
fa4a678d53
commit
374db1ebd4
11 changed files with 102 additions and 102 deletions
|
@ -1,7 +1,7 @@
|
|||
|
||||
# Objects
|
||||
|
||||
As we know from the chapter <info:types>, there are 7 language types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever).
|
||||
As we know from the chapter <info:types>, there are seven language types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever).
|
||||
|
||||
In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate almost every aspect of the language. So we must understand them first before going in-depth anywhere else.
|
||||
|
||||
|
@ -42,7 +42,7 @@ In the `user` object, there are two properties:
|
|||
1. The first property has the name `"name"` and the value `"John"`.
|
||||
2. The second one has the name `"age"` and the value `30`.
|
||||
|
||||
The resulting `user` object can be imagined as a cabinet with two signed files labelled "name" and "age".
|
||||
The resulting `user` object can be imagined as a cabinet with two signed files labeled "name" and "age".
|
||||
|
||||

|
||||
|
||||
|
@ -125,7 +125,7 @@ delete user["likes birds"];
|
|||
|
||||
Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do).
|
||||
|
||||
Square brackets also provide a way to obtain the property name as the result of any expression - as opposed to a literal string - like from a variable as follows:
|
||||
Square brackets also provide a way to obtain the property name as the result of any expression -- as opposed to a literal string -- like from a variable as follows:
|
||||
|
||||
```js
|
||||
let key = "likes birds";
|
||||
|
@ -246,7 +246,7 @@ let user = makeUser("John", 30);
|
|||
alert(user.name); // John
|
||||
```
|
||||
|
||||
In the example above, properties have same names as variables. The use-case of making a property from a variable is so common, that there's a special *property value shorthand* to make it shorter.
|
||||
In the example above, properties have the same names as variables. The use-case of making a property from a variable is so common, that there's a special *property value shorthand* to make it shorter.
|
||||
|
||||
Instead of `name:name` we can just write `name`, like this:
|
||||
|
||||
|
@ -280,7 +280,7 @@ let user = {};
|
|||
alert( user.noSuchProperty === undefined ); // true means "no such property"
|
||||
```
|
||||
|
||||
There also exists a special operator `"in"` to check for the existance of a property.
|
||||
There also exists a special operator `"in"` to check for the existence of a property.
|
||||
|
||||
The syntax is:
|
||||
```js
|
||||
|
@ -358,7 +358,7 @@ for(let key in user) {
|
|||
}
|
||||
```
|
||||
|
||||
Note that all "for" constructs allow to declare the looping variable inside the loop, like `let key` here.
|
||||
Note that all "for" constructs allow us to declare the looping variable inside the loop, like `let key` here.
|
||||
|
||||
Also, we could use another variable name here instead of `key`. For instance, `"for(let prop in obj)"` is also widely used.
|
||||
|
||||
|
@ -394,10 +394,10 @@ But if we run the code, we see a totally different picture:
|
|||
- USA (1) goes first
|
||||
- then Switzerland (41) and so on.
|
||||
|
||||
The phone codes go in the ascending sorted order, because they are integer. So we see `1, 41, 44, 49`.
|
||||
The phone codes go in the ascending sorted order, because they are integers. So we see `1, 41, 44, 49`.
|
||||
|
||||
````smart header="Integer properties? What's that?"
|
||||
The "integer property" term here means a string that can be converted to-and-from integer without a change.
|
||||
The "integer property" term here means a string that can be converted to-and-from an integer without a change.
|
||||
|
||||
So, "49" is an integer property name, because when it's transformed to an integer number and back, it's still the same. But "+49" and "1.2" are not:
|
||||
|
||||
|
@ -448,7 +448,7 @@ Now it works as intended.
|
|||
|
||||
## Copying by reference
|
||||
|
||||
One of fundamental differences of objects vs primitives is that they are stored and copied "by reference".
|
||||
One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
|
||||
|
||||
Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value".
|
||||
|
||||
|
@ -509,7 +509,7 @@ admin.name = 'Pete'; // changed by the "admin" reference
|
|||
alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
|
||||
```
|
||||
|
||||
The example above demonstrates that there is only one object. Like if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we see changes.
|
||||
The example above demonstrates that there is only one object. Like if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we would see changes.
|
||||
|
||||
### Comparison by reference
|
||||
|
||||
|
@ -579,9 +579,9 @@ user = {
|
|||
|
||||
So, copying an object variable creates one more reference to the same object.
|
||||
|
||||
But what if we need to duplicate an object? Create an independant copy, a clone?
|
||||
But what if we need to duplicate an object? Create an independent copy, a clone?
|
||||
|
||||
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed, copying by reference is good most of the time.
|
||||
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed. Copying by reference is good most of the time.
|
||||
|
||||
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.
|
||||
|
||||
|
@ -660,7 +660,7 @@ let clone = Object.assign({}, user);
|
|||
|
||||
It copies all properties of `user` into the empty object and returns it. Actually, the same as the loop, but shorter.
|
||||
|
||||
Till now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
|
||||
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
|
||||
|
||||
Like this:
|
||||
```js run
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Memory management in JavaScript is performed automatically and invisibly to us. We create primitives, objects, functions... All that takes memory.
|
||||
|
||||
What happens when something is not needed any more? How JavaScript engine discovers that and cleans up?
|
||||
What happens when something is not needed any more? How does the JavaScript engine discover it and clean it up?
|
||||
|
||||
[cut]
|
||||
|
||||
|
@ -10,7 +10,7 @@ What happens when something is not needed any more? How JavaScript engine discov
|
|||
|
||||
The main concept of memory management in JavaScript is *reachability*.
|
||||
|
||||
Simply put, "reachable" values are those that are accessible or useable somehow. They are guaranteed to be stored in memory.
|
||||
Simply put, "reachable" values are those that are accessible or usable somehow. They are guaranteed to be stored in memory.
|
||||
|
||||
1. There's a base set of inherently reachable values, that cannot be deleted for obvious reasons.
|
||||
|
||||
|
@ -25,9 +25,9 @@ Simply put, "reachable" values are those that are accessible or useable somehow.
|
|||
|
||||
2. Any other value is considered reachable if it's reachable from a root by a reference or by a chain of references.
|
||||
|
||||
For instance, if there's an object in a local variable, and that object has a property referencing another object, that object is considered reachable. And those that it references -- are also reachable. Detailed examples to follow.
|
||||
For instance, if there's an object in a local variable, and that object has a property referencing another object, that object is considered reachable. And those that it references are also reachable. Detailed examples to follow.
|
||||
|
||||
There's a background process in the JavaScript engine that is called [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). It monitors all objects and removes those that became unreachable.
|
||||
There's a background process in the JavaScript engine that is called [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). It monitors all objects and removes those that have become unreachable.
|
||||
|
||||
## A simple example
|
||||
|
||||
|
@ -153,11 +153,11 @@ The former `"family"` object has been unlinked from the root, there's no referen
|
|||
|
||||
The basic garbage collection algorithm is called "mark-and-sweep".
|
||||
|
||||
Regularly the following "garbage collection" steps are performed:
|
||||
The following "garbage collection" steps are regularly performed:
|
||||
|
||||
- The garbage collector takes roots and "marks" (remembers) them.
|
||||
- Then it visits and "marks" all references from them.
|
||||
- Then it visits marked objects and marks *their* references. All visited objects are remembered, not to visit the same object twice in the future.
|
||||
- Then it visits marked objects and marks *their* references. All visited objects are remembered, so as not to visit the same object twice in the future.
|
||||
- ...And so on until there are unvisited references (reachable from the roots).
|
||||
- All objects except marked ones are removed.
|
||||
|
||||
|
@ -183,7 +183,7 @@ Now the objects that could not be visited in the process are considered unreacha
|
|||
|
||||

|
||||
|
||||
That's the concept how garbage collection works.
|
||||
That's the concept of how garbage collection works.
|
||||
|
||||
JavaScript engines apply many optimizations to make it run faster and not affect the execution.
|
||||
|
||||
|
@ -193,7 +193,7 @@ Some of the optimizations:
|
|||
- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine tries to split the garbage collection into pieces. Then the pieces are executed one by one, separately. That requires some extra bookkeeping between them to track changes, but we have many tiny delays instead of a big one.
|
||||
- **Idle-time collection** -- the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution.
|
||||
|
||||
There are other optimizations and flavours of garbage collection algorithms. As much as I'd like to describe them here, I have to hold off, because different engines implement different tweaks and techniques. And -- what's even more important, things change as engines develop, so going deeper "in advance", without a real need is probably not worth that. Unless, of course, it is a matter of pure interest, then there will be some links for you below.
|
||||
There are other optimizations and flavours of garbage collection algorithms. As much as I'd like to describe them here, I have to hold off, because different engines implement different tweaks and techniques. And, what's even more important, things change as engines develop, so going deeper "in advance", without a real need is probably not worth that. Unless, of course, it is a matter of pure interest, then there will be some links for you below.
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -205,7 +205,7 @@ The main things to know:
|
|||
|
||||
Modern engines implement advanced algorithms of garbage collection.
|
||||
|
||||
A general book "The Garbage Collection Handbook: The Art of Automatic Memory Management" (R. Jones at al) covers some of them.
|
||||
A general book "The Garbage Collection Handbook: The Art of Automatic Memory Management" (R. Jones et al) covers some of them.
|
||||
|
||||
If you are familiar with low-level programming, the more detailed information about V8 garbage collector is in the article [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection).
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
By specification, object property keys may be either of string type, or of symbol type. Not numbers, not booleans, only strings or symbols, these two types.
|
||||
|
||||
Till now we only saw strings. Now let's see the advantages that symbols can give us.
|
||||
Till now we've only seen strings. Now let's see the advantages that symbols can give us.
|
||||
|
||||
[cut]
|
||||
|
||||
|
@ -60,7 +60,7 @@ That's a "language guard" against messing up, because strings and symbols are fu
|
|||
|
||||
## "Hidden" properties
|
||||
|
||||
Symbols allow to create "hidden" properties of an object, that no other part of code can occasionally access or overwrite.
|
||||
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:
|
||||
|
||||
|
@ -72,11 +72,11 @@ 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 his own "id" property inside `user`, for his own purposes. That may be another JavaScript library, so the scripts are completely unaware for each other.
|
||||
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.
|
||||
|
||||
No problem. It can create its own `Symbol("id")`.
|
||||
|
||||
Their script:
|
||||
The script:
|
||||
|
||||
```js
|
||||
// ...
|
||||
|
@ -176,7 +176,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.
|
||||
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.
|
||||
|
||||
For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property.
|
||||
|
||||
|
@ -246,7 +246,7 @@ They are listed in the specification in the [Well-known symbols](https://tc39.gi
|
|||
- `Symbol.toPrimitive`
|
||||
- ...and so on.
|
||||
|
||||
For instance, `Symbol.toPrimitive` allows to describe object to primitive conversion. We'll see its use very soon.
|
||||
For instance, `Symbol.toPrimitive` allows us 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.
|
||||
|
||||
|
@ -265,6 +265,6 @@ Symbols have two main use cases:
|
|||
|
||||
So we can "covertly" hide something into objects that we need, but others should not see, using symbolic properties.
|
||||
|
||||
2. There are many system symbols used by JavaScript and 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.
|
||||
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 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 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 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.
|
||||
|
|
|
@ -36,7 +36,7 @@ user.sayHi(); // Hello!
|
|||
|
||||
Here we've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object.
|
||||
|
||||
Then we can call it. The user now can speak!
|
||||
Then we can call it. The user can now speak!
|
||||
|
||||
A function that is the property of an object is called its *method*.
|
||||
|
||||
|
@ -92,7 +92,7 @@ let user = {
|
|||
|
||||
As demonstrated, we can omit `"function"` and just write `sayHi()`.
|
||||
|
||||
To say the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases the shorter syntax is preferred.
|
||||
To tell the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases the shorter syntax is preferred.
|
||||
|
||||
## "this" in methods
|
||||
|
||||
|
@ -271,7 +271,7 @@ This doesn't (evaluated method):
|
|||
(user.name == "John" ? user.hi : user.bye)(); // Error!
|
||||
```
|
||||
|
||||
Why? If we want to understand why it happens -- let's get under the hood of how `obj.method()` call works.
|
||||
Why? If we want to understand why it happens, let's get under the hood of how `obj.method()` call works.
|
||||
|
||||
Looking closely, we may notice two operations in `obj.method()` statement:
|
||||
|
||||
|
@ -352,4 +352,4 @@ The value of `this` is defined at run-time.
|
|||
- That function can be copied between objects.
|
||||
- When a function is called in the "method" syntax: `object.method()`, the value of `this` during the call is `object`.
|
||||
|
||||
Please note that arrow functions are special: they have no `this`. When `this` is accessed inside an arrow function -- it is taken from outside.
|
||||
Please note that arrow functions are special: they have no `this`. When `this` is accessed inside an arrow function, it is taken from outside.
|
||||
|
|
|
@ -19,11 +19,11 @@ As for the string conversion -- it usually happens when we output an object like
|
|||
|
||||
When an object is used in the context where a primitive is required, for instance, in an `alert` or mathematical operations, it's converted to a primitive value using the `ToPrimitive` algorithm ([specification](https://tc39.github.io/ecma262/#sec-toprimitive)).
|
||||
|
||||
That algorithm allows to customize the conversion using in a special object method.
|
||||
That algorithm allows us to customize the conversion using a special object method.
|
||||
|
||||
Depending on the context, the conversion has a so-called "hint".
|
||||
|
||||
There are 3 variants:
|
||||
There are three variants:
|
||||
|
||||
`"string"`
|
||||
: When an operation expects a string, for object-to-string conversions, like `alert`:
|
||||
|
@ -68,7 +68,7 @@ There are 3 variants:
|
|||
|
||||
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 are only three hints. That simple. There is no "boolean" hint (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.
|
||||
Please note -- there are only three hints. It's that simple. There is no "boolean" hint (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 three object methods:**
|
||||
|
||||
|
@ -159,12 +159,12 @@ alert(user); // toString -> John
|
|||
alert(user + 500); // toString -> John500
|
||||
```
|
||||
|
||||
In the absense of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions.
|
||||
In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions.
|
||||
|
||||
|
||||
## 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 do not necessarily return the "hinted" primitive.
|
||||
|
||||
There is no control whether `toString()` returns exactly a string, or whether `Symbol.toPrimitive` method returns a number for a hint "number".
|
||||
|
||||
|
@ -178,7 +178,7 @@ For instance:
|
|||
|
||||
```js run
|
||||
let obj = {
|
||||
toString() { // toString handles all conversions in the absense of other methods
|
||||
toString() { // toString handles all conversions in the absence of other methods
|
||||
return "2";
|
||||
}
|
||||
};
|
||||
|
@ -186,7 +186,7 @@ For instance:
|
|||
alert(obj * 2); // 4, ToPrimitive gives "2", then it becomes 2
|
||||
```
|
||||
|
||||
- Binary plus performs checks the primitive -- if it's a string, then it does concatenation, otherwise performs `ToNumber` and works with numbers.
|
||||
- Binary plus checks the primitive -- if it's a string, then it does concatenation, otherwise it performs `ToNumber` and works with numbers.
|
||||
|
||||
String example:
|
||||
```js run
|
||||
|
@ -235,4 +235,4 @@ The conversion algorithm is:
|
|||
3. Otherwise if hint is `"number"` or `"default"`
|
||||
- try `obj.valueOf()` and `obj.toString()`, whatever exists.
|
||||
|
||||
In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for all conversions that returns a "human-readable" representation of an object, for logging or debugging purposes.
|
||||
In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for all conversions that return a "human-readable" representation of an object, for logging or debugging purposes.
|
||||
|
|
|
@ -103,7 +103,7 @@ User(); // undefined
|
|||
new User(); // function User { ... }
|
||||
```
|
||||
|
||||
That can be used to allow both `new` and regular syntax work the same:
|
||||
That can be used to allow both `new` and regular syntax to work the same:
|
||||
|
||||
```js run
|
||||
function User(name) {
|
||||
|
@ -118,7 +118,7 @@ let john = User("John"); // redirects call to new User
|
|||
alert(john.name); // John
|
||||
```
|
||||
|
||||
This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because omitting `new` makes a bit less obvious what's going on. With `new` we all know that the new object is being created, that's a good thing.
|
||||
This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because omitting `new` makes it a bit less obvious what's going on. With `new` we all know that the new object is being created, that's a good thing.
|
||||
|
||||
## Return from constructors
|
||||
|
||||
|
@ -176,7 +176,7 @@ Omitting parentheses here is not considered a "good style", but the syntax is pe
|
|||
|
||||
## Methods in constructor
|
||||
|
||||
Using constuctor functions to create objects gives a great deal of flexibility. The constructor function may have parameters that define how to construct the object, what to put in it.
|
||||
Using constructor functions to create objects gives a great deal of flexibility. The constructor function may have parameters that define how to construct the object, and what to put in it.
|
||||
|
||||
Of course, we can add to `this` not only properties, but methods as well.
|
||||
|
||||
|
@ -207,8 +207,8 @@ john = {
|
|||
|
||||
## Summary
|
||||
|
||||
- Constructor functions or, shortly, constructors, are regular functions, but there's a common agreement to name them with capital letter first.
|
||||
- Constructor functions should only be called using `new`. Such call implies a creation of empty `this` at the start and returning the populated one at the end.
|
||||
- Constructor functions or, briefly, constructors, are regular functions, but there's a common agreement to name them with capital letter first.
|
||||
- Constructor functions should only be called using `new`. Such a call implies a creation of empty `this` at the start and returning the populated one at the end.
|
||||
|
||||
We can use constructor functions to make multiple similar objects.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue