This commit is contained in:
Ilya Kantor 2017-03-23 11:06:57 +03:00
parent b6ed18e70d
commit c9401b3104
15 changed files with 231 additions and 233 deletions

View file

@ -1,10 +1,11 @@
# Hello, world!
The tutorial that you're reading is about the core JavaScript, that is platform-independant. So you'll be able to learn how to use Node.JS and other things based on that knowledge.
The tutorial that you're reading is about the core JavaScript, that is platform-independant. Further on you will be able learn Node.JS and other platforms that use it.
But we need a working environment to run our scripts, and, just because this book is online, the browser is probably a good choice. We'll use a few browser-specific commands like `alert`, but will keep their amount to the minimum.
But we need a working environment to run our scripts, and, just because this book is online, the browser a good choice. We'll keep the amount of browser-specific commands (like `alert`) to minimum, so that you don't spend time on them if you plan to concentrate on another environment like Node.JS. From the other hand, browser details are explained in detail in the [next part](/ui) of the tutorial. So
So first let's see how to attach a script to the webpage. For server-side environments you can just execute it with a command like `"node my.js"` for Node.JS.
So here we'll see how to attach a script to the webpage, that's simple enough. For server-side environments you can just execute it with a command like `"node my.js"` for Node.JS.
[cut]

View file

@ -38,7 +38,7 @@ alert( 'World' )
Here JavaScript interprets the line break as an "implicit" semicolon. That's also called an [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion).
**In most cases a newline implies a simicolon. But "in most cases" does not mean "always"!**
**In most cases a newline implies a semicolon. But "in most cases" does not mean "always"!**
There are cases when a newline does not mean a semicolon, for example:

View file

@ -1,6 +1,6 @@
# Date and time
Let's meet a new built-in object: [Date](mdn:js/Date). It stores the date and provides methods for date/time management.
Let's meet a new built-in object: [Date](mdn:js/Date). It stores the date, time and provides methods for date/time management.
For instance, we can use it to store creation/modification times, or to measure time, or just to print out the current date.
@ -33,15 +33,15 @@ To create a new `Date` object call `new Date()` with one of the following argume
The number of milliseconds that has passed since the beginning of 1970 is called a *timestamp*.
It is a lightweight numeric representation of a date. We can always create a date from a timestamp using `new Date(timestamp)` and convert the existing `Date` object to a timestamp, there's a `date.getTime()` method (see below).
It's a lightweight numeric representation of a date. We can always create a date from a timestamp using `new Date(timestamp)` and convert the existing `Date` object to a timestamp using the `date.getTime()` method (see below).
`new Date(datestring)`
: If there is a single argument and it's a string, then it is parsed with the `Date.parse` algorithm (see below).
: If there is a single argument, and it's a string, then it is parsed with the `Date.parse` algorithm (see below).
```js run
let date = new Date("2017-01-26");
alert( date ); // Thu Jan 26 2017 ...
alert(date); // Thu Jan 26 2017 ...
```
`new Date(year, month, date, hours, minutes, seconds, ms)`
@ -52,7 +52,7 @@ To create a new `Date` object call `new Date()` with one of the following argume
- The `year` must have 4 digits: `2013` is okay, `98` is not.
- The `month` count starts with `0` (Jan), up to `11` (Dec).
- The `date` parameter is actually the day of month, if absent then `1` is assumed.
- If `hours/minutes/seconds/ms` is absent, then it is equal `0`.
- If `hours/minutes/seconds/ms` is absent, they are assumed to be equal `0`.
For instance:
@ -61,7 +61,7 @@ To create a new `Date` object call `new Date()` with one of the following argume
new Date(2011, 0, 1); // the same, hours etc are 0 by default
```
The precision is 1 ms (1/1000 sec):
The minimal precision is 1 ms (1/1000 sec):
```js run
let date = new Date(2011, 0, 1, 2, 3, 4, 567);
@ -70,7 +70,7 @@ To create a new `Date` object call `new Date()` with one of the following argume
## Access date components
The are many methods to access the year, month etc from the `Date` object. But they can be easily remembered when categorized.
The are many methods to access the year, month and so on from the `Date` object. But they can be easily remembered when categorized.
`getFullYear()`
: Get the year (4 digits)
@ -85,7 +85,7 @@ The are many methods to access the year, month etc from the `Date` object. But t
: Get the corresponding time components.
```warn header="Not `getYear()`, but `getFullYear()`"
Many JavaScript engines implement a non-standard method `getYear()`. This method is non-standard. It returns 2-digit year sometimes. Please never use it. There is `getFullYear()` for that.
Many JavaScript engines implement a non-standard method `getYear()`. This method is deprecated. It returns 2-digit year sometimes. Please never use it. There is `getFullYear()` for the year.
```
Additionally, we can get a day of week:
@ -95,7 +95,7 @@ Additionally, we can get a day of week:
**All the methods above return the components relative to the local time zone.**
There are also their UTC-counterparts, that return day, month, year etc for the time zone UTC+0: `getUTCFullYear()`, `getUTCMonth()`, `getUTCDay()`. Just insert the `"UTC"` right after `"get"`.
There are also their UTC-counterparts, that return day, month, year and so on for the time zone UTC+0: `getUTCFullYear()`, `getUTCMonth()`, `getUTCDay()`. Just insert the `"UTC"` right after `"get"`.
If your local time zone is shifted relative to UTC, then the code below shows different hours:
@ -106,8 +106,7 @@ let date = new Date();
// the hour in your current time zone
alert( date.getHours() );
// what time is it now in London winter time (UTC+0)?
// the hour in UTC+0 time zone
// the hour in UTC+0 time zone (London time without daylight savings)
alert( date.getUTCHours() );
```
@ -120,7 +119,10 @@ Besides the given methods, there are two special ones, that do not have a UTC-va
: Returns the difference between the local time zene and UTC, in minutes:
```js run
alert( new Date().getTimezoneOffset() ); // For UTC-1 outputs 60
// if you are in timezone UTC-1, outputs 60
// if you are in timezone UTC+3, outputs -180
alert( new Date().getTimezoneOffset() );
```
## Setting date components
@ -138,7 +140,7 @@ The following methods allow to set date/time components:
Every one of them except `setTime()` has a UTC-variant, for instance: `setUTCHours()`.
As we can see, some methods can set multiple components at once, for example `setHours`. Those components that are not mentioned -- are not modified.
As we can see, some methods can set multiple components at once, for example `setHours`. The components that are not mentioned are not modified.
For instance:
@ -146,10 +148,10 @@ For instance:
let today = new Date();
today.setHours(0);
alert( today ); // today, but the hour is changed to 0
alert(today); // still today, but the hour is changed to 0
today.setHours(0, 0, 0, 0);
alert( today ); // today, 00:00:00 sharp.
alert(today); // still today, now 00:00:00 sharp.
```
## Autocorrection
@ -163,7 +165,7 @@ let date = new Date(2013, 0, *!*32*/!*); // 32 Jan 2013 ?!?
alert(date); // ...is 1st Feb 2013!
```
**Out-of-range date components are distributed automatically.**
Out-of-range date components are distributed automatically.
Let's say we need to increase the date "28 Feb 2016" by 2 days. It may be "2 Mar" or "1 Mar" in case of a leap-year. We don't need to think about it. Just add 2 days. The `Date` object will do the rest:
@ -185,7 +187,7 @@ date.setSeconds(date.getSeconds() + 70);
alert( date ); // shows the correct date
```
We can also set zero or even negative componens. For example:
We can also set zero or even negative values. For example:
```js run
let date = new Date(2016, 0, 2); // 2 Jan 2016
@ -199,16 +201,16 @@ alert( date ); // 31 Dec 2015
## Date to number, date diff
When a `Date` object is converted to number, it becomes its number of milliseconds:
When a `Date` object is converted to number, it becomes the timestamp same as `date.getTime()`:
```js run
let date = new Date();
alert( +date ); // will the milliseconds, same as date.getTime()
alert(+date); // the number of milliseconds, same as date.getTime()
```
**The important side effect: dates can be substracted, the result is their difference in ms.**
The important side effect: dates can be substracted, the result is their difference in ms.
That's some times used for time measurements:
That can be used for time measurements:
```js run
let start = new Date(); // start counting
@ -229,7 +231,7 @@ If we only want to measure the difference, we don't need `Date` object.
There's a special method `Date.now()` that returns the current timestamp.
It is semantically equivalent to `new Date().getTime()`, but it does not create an intermediate `Date` object. So it's faster and does not put pressure on garbage collection.
It is semantically equivalent to `new Date().getTime()`, but it doesn't create an intermediate `Date` object. So it's faster and doesn't put pressure on garbage collection.
It is used mostly for convenience or when performance matters, like in games in JavaScript or other specialized applications.
@ -256,21 +258,25 @@ alert( `The loop took ${end - start} ms` ); // substract numbers, not dates
If we want a reliable benchmark of CPU-hungry function, we should be careful.
For instance, let's measure two function that get a delta between two dates, which one is faster?
For instance, let's measure two functions that calculate the difference between two dates: which one is faster?
```js
// we have date1 and date2, which function faster returns their difference in ms?
function diffSubstract(date1, date2) {
return date2 - date1;
}
// or
function diffGetTime(date1, date2) {
return date2.getTime() - date1.getTime();
}
```
These two do exactly the same, because when dates are substracted, they are coerced into numbers of milliseconds, exactly the same thing as `date.getTime()` returns.
These two do exactly the same thing, but one of them uses an explicit `date.getTime()` to get the date in ms, and the other one relies on a date-to-number transform. Their result is always the same.
The first idea is to run them many times in a row and measure the difference. For our case, functions are very simple, so we have to do it around 100000 times.
So, which one is faster?
The first idea may be to run them many times in a row and measure the time difference. For our case, functions are very simple, so we have to do it around 100000 times.
Let's measure:
@ -300,11 +306,11 @@ Wow! Using `getTime()` is so much faster! That's because there's no type convers
Okay, we have something. But that's not a good benchmark yet.
Imagine that in the time of running `bench(diffSubstract)` CPU was doing something in parallel, and it was taking resources. And at the time of the second benchmark the work was finished.
Imagine that at the time of running `bench(diffSubstract)` CPU was doing something in parallel, and it was taking resources. And by the time of running `bench(diffGetTime)` the work has finished.
Is that real? Of course it is, especially for the modern multi-process OS.
A pretty real scenario for a modern multi-process OS.
As a result, the first benchmark will have less CPU resources than the second. That's a reason for wrong results.
As a result, the first benchmark will have less CPU resources than the second. That may lead to wrong results.
**For more reliable benchmarking, the whole pack of benchmarks should be rerun multiple times.**
@ -346,7 +352,7 @@ alert( 'Total time for diffGetTime: ' + time2 );
Modern JavaScript engines start applying advanced optimizations only to "hot code" that executes many times (no need to optimize rarely executed things). So, in the example above, first executions are not well-optimized. We may want to add a heat-up run:
```js
// heat up
// added for "heating up" prior to the main loop
bench(diffSubstract);
bench(diffGetTime);
@ -357,24 +363,22 @@ for (let i = 0; i < 10; i++) {
}
```
```warn header="Be careful doing micro-benchmarking"
Modern JavaScript engines perform many optimizations. Some of them may tweak artificial tests results compared to normal usage.
So if you seriously want to understand performance, then please first study how the JavaScript engine works. And then you probably won't need microbenchmarks at all, or apply them rarely.
```warn header="Be careful doing microbenchmarking"
Modern JavaScript engines perform many optimizations. They may tweak results of "artificial tests" compared to "normal usage", especially when we benchmark something very small. So if you seriously want to understand performance, then please study how the JavaScript engine works. And then you probably won't need microbenchmarks at all.
The great pack of articles about V8 can be found at <http://mrale.ph>.
```
## Date.parse from a string
The method [Date.parse](mdn:js/Date/parse) can read the date from a string.
The method [Date.parse(str)](mdn:js/Date/parse) can read a date from a string.
The format is: `YYYY-MM-DDTHH:mm:ss.sssZ`, where:
The string format should be: `YYYY-MM-DDTHH:mm:ss.sssZ`, where:
- `YYYY-MM-DD` -- is the date: year-month-day.
- The ordinary character `T` is used as the delimiter.
- The character `"T"` is used as the delimiter.
- `HH:mm:ss.sss` -- is the time: hours, minutes, seconds and milliseconds.
- The optional `'Z'` part denotes the time zone in the format `+-hh:mm` or a single `Z` that would mean UTC+0.
- The optional `'Z'` part denotes the time zone in the format `+-hh:mm`. A single letter `Z` that would mean UTC+0.
Shorter variants are also possible, like `YYYY-MM-DD` or `YYYY-MM` or even `YYYY`.
@ -385,7 +389,7 @@ For instance:
```js run
let ms = Date.parse('2012-01-26T13:51:50.417-07:00');
alert( ms ); // 1327611110417 (timestam)
alert(ms); // 1327611110417 (timestamp)
```
We can instantly create a `new Date` object from the timestamp:
@ -393,29 +397,27 @@ We can instantly create a `new Date` object from the timestamp:
```js run
let date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );
alert( date );
alert(date);
```
## Summary
- Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time".
- Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time": `Date` objects always carry both.
- Months are counted from the zero (yes, January is a zero month).
- Days of week (for `getDay()`) are also counted from the zero (Sunday).
- Days of week in `getDay()` are also counted from the zero (that's Sunday).
- `Date` auto-corrects itself when out-of-range components are set. Good for adding/substracting days/months/hours.
- Dates can be substructed, giving their difference in milliseconds. That's because a `Date` becomes the timestamp if converted to a number.
- Use `Date.now()` to get the current timestamp fast.
Note that unlike many other systems, timestamps in JavaScript are in milliseconds, not in seconds.
Also, sometimes we need more precise time measurements. JavaScript itself does not have a way to measure time in microseconds (1 millionth of a second), but most environments provide it.
For instance, browser has [performance.now()](mdn:api/Performance/now) that gives the number of milliseconds from the start of page loading, but adds 3 digits after the point to it. So totally it becomes microsecond percision:
Also, sometimes we need more precise time measurements. JavaScript itself does not have a way to measure time in microseconds (1 millionth of a second), but most environments provide it. For instance, browser has [performance.now()](mdn:api/Performance/now) that gives the number of milliseconds from the start of page loading with microsecond precision (3 digits after the point):
```js run
alert(`Loading started ${performance.now()}ms ago`);
// Something like: "Loading started 34731.26000000001ms ago"
// it may show more than 3 digits after the decimal point, but only 3 first are correct
// .26 is microseconds (260 microseconds)
// more than 3 digits after the decimal point are precision errors, but only 3 first are correct
```
Node.JS has `microtime` module and other ways. Technically, any device and environment allows to get that, it's just not in `Date`.
Node.JS has `microtime` module and other ways. Technically, any device and environment allows to get more precision, it's just not in `Date`.

View file

@ -4,8 +4,8 @@ We may decide to execute a function not right now, but at a certain time later.
There are two methods for it:
- `setTimeout` allows to run a function once after the given interval of time.
- `setInterval` allows to run a function regularly with the given interval between the runs.
- `setTimeout` allows to run a function once after the interval of time.
- `setInterval` allows to run a function regularly with the interval between the runs.
These methods are not a part of JavaScript specification. But most environments have the internal scheduler and provide these methods. In particular, they are supported in all browsers and Node.JS.
@ -168,6 +168,7 @@ let timerId = setTimeout(function request() {
...send request...
if (request failed due to server overload) {
// increase the interval to the next run
delay *= 2;
}
@ -179,7 +180,7 @@ let timerId = setTimeout(function request() {
And if we regulary have CPU-hungry tasks, then we can measure the time taken by the execition and plan the next call sooner or later.
**Recursive `setTimeout` guarantees a delay before the executions, `setInterval` -- does not.**
**Recursive `setTimeout` guarantees a delay between the executions, `setInterval` -- does not.**
Let's compare two code fragments. The first one uses `setInterval`:

View file

@ -4,9 +4,9 @@ importance: 5
# Working with prototype
Here's the code that creates a pair of object, then alters them.
Here's the code that creates a pair of objects, then modifies them.
Which values will be shown in the process?
Which values are shown in the process?
```js
let animal = {
@ -28,4 +28,4 @@ delete animal.jumps;
alert( rabbit.jumps ); // ? (3)
```
There should be 3 answers.
There should be 3 answers.

View file

@ -4,7 +4,9 @@ importance: 5
# Searching algorithm
We have object:
The task has two parts.
We have an object:
```js
let head = {
@ -25,9 +27,5 @@ let pockets = {
};
```
The task has two parts:
1. Use `__proto__` to assign prototypes in a way that any property lookup will follow the path: `pockets -> bed -> table -> head`.
For instance, `pockets.pen` should be `3` (found in `table`), and `bed.glasses` should be `1` (found in `head`).
1. Use `__proto__` to assign prototypes in a way that any property lookup will follow the path: `pockets` -> `bed` -> `table` -> `head`. For instance, `pockets.pen` should be `3` (found in `table`), and `bed.glasses` should be `1` (found in `head`).
2. Answer the question: is it faster to get `glasses` as `pocket.glasses` or `head.glasses`? Benchmark if needed.

View file

@ -1,5 +1,6 @@
**The answer: `rabbit`.**
That's because `this` is an object before the dot, so `rabbit.eat()` naturally means `rabbit`.
That's because `this` is an object before the dot, so `rabbit.eat()` modifies `rabbit`.
Property lookup and execution are two successive things. The method is found in the prototype, but then is run in the context of `rabbit`.
Property lookup and execution are two different things.
The method `rabbit.eat` is first found in the prototype, then executed with `this=rabbit`

View file

@ -6,7 +6,7 @@ importance: 5
We have `rabbit` inheriting from `animal`.
If we call `rabbit.eat()`, which object receives `full`: `animal` or `rabbit`?
If we call `rabbit.eat()`, which object receives the `full` property: `animal` or `rabbit`?
```js
let animal = {
@ -21,4 +21,3 @@ let rabbit = {
rabbit.eat();
```

View file

@ -1,16 +1,18 @@
Let's look carefully at what's going on in the call `speedy.eat("apple")`.
1. The method `speedy.eat` is found in the prototype (`hamster`), but called in the context of `speedy`. So the value of `this` is correct.
1. The method `speedy.eat` is found in the prototype (`=hamster`), then executed with `this=speedy` (the object before the dot).
2. Then `this.stomach.push()` needs to find `stomach` property and call `push` on it. It looks for `stomach` in `this` (`=speedy`), but nothing found.
2. Then `this.stomach.push()` needs to find `stomach` property and call `push` on it. It looks for `stomach` in `this` (`=speedy`), but nothing found.
3. Then it follows the prototype chain and finds `stomach` in `hamster`.
4. Then it calls `push` on it, adding the food into *the stomach of the prototype*.
It turns out that all hamsters share a single stomach!
So all hamsters share a single stomach!
Please note that it would not happen in case of a simple assignment `this.stomach=`:
Every time the `stomach` is taken from the prototype, then `stomach.push` modifies it "at place".
Please note that such thing doesn't happen in case of a simple assignment `this.stomach=`:
```js run
let hamster = {
@ -24,9 +26,13 @@ let hamster = {
}
};
let speedy = { __proto__: hamster };
let speedy = {
__proto__: hamster
};
let lazy = { __proto__: hamster };
let lazy = {
__proto__: hamster
};
// Speedy one found the food
speedy.eat("apple");
@ -36,9 +42,9 @@ alert( speedy.stomach ); // apple
alert( lazy.stomach ); // <nothing>
```
Now all works fine, because `this.stomach=` does not perform a lookup of `stomach`. The value is written directly into `this` object. And for a method call `this.stomach.push`, the object is to be found first (in the prototype), then called, that's the difference.
Now all works fine, because `this.stomach=` does not perform a lookup of `stomach`. The value is written directly into `this` object.
But more often, we can totally evade the problem by making sure that each hamster has his own stomach, explicitly:
Also we can totally evade the problem by making sure that each hamster has his own stomach:
```js run
let hamster = {
@ -49,14 +55,14 @@ let hamster = {
}
};
let speedy = {
let speedy = {
__proto__: hamster,
*!*
stomach: []
*/!*
};
let lazy = {
let lazy = {
__proto__: hamster,
*!*
stomach: []
@ -71,5 +77,4 @@ alert( speedy.stomach ); // apple
alert( lazy.stomach ); // <nothing>
```
As a common solution, all object properties, like `stomach` above, are usually written into each object. That prevents such problems. From the other hand, methods and primives can safely stay in prototypes.
As a common solution, all properties that describe the state of a particular object, like `stomach` above, are usually written into that object. That prevents such problems.

View file

@ -6,7 +6,6 @@ For instance, we have a `user` object with its properties and methods, and want
*Prototypal inheritance* is a language feature that helps in that.
[cut]
## [[Prototype]]
@ -15,7 +14,7 @@ In JavaScript, objects have a special hidden property `[[Prototype]]` (as named
![prototype](object-prototype-empty.png)
That `[[Prototype]]` has a "magical" meaning. When we want to read a property from `object`, and it's missing, JavaScript automatically takes it from the prototype. In programming, such thing is called a "prototypal inheritance". Many cool language features and approaches are based on it.
That `[[Prototype]]` has a "magical" meaning. When we want to read a property from `object`, and it's missing, JavaScript automatically takes it from the prototype. In programming, such thing is called "prototypal inheritance". Many cool language features and programming techniques are based on it.
The property `[[Prototype]]` is internal and hidden, but there are many ways to set it.
@ -34,9 +33,9 @@ rabbit.__proto__ = animal;
*/!*
```
Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. We'll talk about other ways of setting it later, as for now `__proto__` will do just fine.
Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. We'll talk about other ways of setting it later, but for now `__proto__` will do just fine.
So now if we look for something in `rabbit` and it's missing, JavaScript automatically takes it from `animal`.
If we look for a property in `rabbit`, and it's missing, JavaScript automatically takes it from `animal`.
For instance:
@ -54,14 +53,14 @@ rabbit.__proto__ = animal; // (*)
// we can find both properties in rabbit now:
*!*
alert( rabbit.eats ); // true
alert( rabbit.eats ); // true (**)
*/!*
alert( rabbit.jumps ); // true
```
Here the line `(*)` sets `animal` to be a prototype of `rabbit`.
Then, when `alert` tries to read property `rabbit.eats`, it can find it `rabbit`, so it follows the `[[Prototype]]` reference and finds it in `animal` (look from the bottom up):
Then, when `alert` tries to read property `rabbit.eats` `(**)`, it's not in `rabbit`, so JavaScript follows the `[[Prototype]]` reference and finds it in `animal` (look from the bottom up):
![](proto-animal-rabbit.png)
@ -126,21 +125,25 @@ alert(longEar.jumps); // true (from rabbit)
There are actually only two limitations:
1. The references can't go in circles. JavaScript will throw an error if we try to assign `__proto__` in circle.
1. The references can't go in circles. JavaScript will throw an error if we try to assign `__proto__` in a circle.
2. The value of `__proto__` can be either an object or `null`. All other values (like primitives) are ignored.
Also it may be obvious, but still: there can be only one `[[Prototype]]`. An object may not inherit from two others.
## Read/write rules
The prototype is only used for reading properties, write/delete for data properties works directly with the object.
The prototype is only used for reading properties.
For data properties (not getters/setters) write/delete operations work directly with the object.
In the example below, we assign its own `walk` method to `rabbit`:
```js run
let animal = {
eats: true,
walk() { /* unused by rabbit, because (see below) it has its own */ }
walk() {
/* this method won't be used by rabbit */
}
};
let rabbit = {
@ -160,18 +163,22 @@ Since now, `rabbit.walk()` call finds the method immediately in the object and e
![](proto-animal-rabbit-walk-2.png)
The assignment handling is different for accessor properties, because these properties behave more like functions. For instance, check out `admin.fullName` property in the code below:
For getters/setters -- if we read/write a property, they are looked up in the prototype and invoked.
For instance, check out `admin.fullName` property in the code below:
```js run
let user = {
name: "John",
surname: "Smith",
*!*
set fullName(value) {
[this.name, this.surname] = value.split(" ");
}
*/!*
get fullName() {
return `${this.name} ${this.surname}`;
}
};
let admin = {
@ -179,21 +186,13 @@ let admin = {
isAdmin: true
};
// setter triggers!
*!*
admin.fullName = "Alice Cooper";
*/!*
alert(admin.fullName); // John Smith (*)
alert(admin.name); // Alice
alert(admin.surname); // Cooper
// setter triggers!
admin.fullName = "Alice Cooper"; // (**)
```
Here in the line `(*)` the property `admin.fullName` has a setter in the prototype `user`. So it is not written into `admin`. Instead, the inherited setter is called.
So, the general rule would be:
1. For accessor properties use a setter (from the prototype chain).
2. Otherwise assign directly to the object.
Here in the line `(*)` the property `admin.fullName` has a getter in the prototype `user`, so it is called. And in the line `(**)` the property is has a setter in the prototype, so it is called.
## The value of "this"
@ -201,7 +200,7 @@ An interesting question may arise in the example above: what's the value of `thi
The answer is simple: `this` is not affected by prototypes at all.
**No matter where a method is found: in an object or its prototype. In a method call, `this` is always the object before the dot.**
**No matter where the method is found: in an object or its prototype. In a method call, `this` is always the object before the dot.**
So, the setter actually uses `admin` as `this`, not `user`.
@ -240,14 +239,14 @@ The resulting picture:
![](proto-animal-rabbit-walk-3.png)
If we had other objects like `bird`, `snake` etc inheriting from `animal`, they would also gain access to its methods. But `this` in each method would be the corresponding object, not `animal`.
If we had other objects like `bird`, `snake` etc inheriting from `animal`, they would also gain access to methods of `animal`. But `this` in each method would be the corresponding object, evaluated at the call-time (before dot), not `animal`. So when we write data into `this`, it is stored into these objects.
In other words, methods are shared, but the state will be not.
As a result, methods are shared, but the object state is not.
## Summary
- In JavaScript, all objects have a hidden `[[Prototype]]` property that's either another object or `null`.
- We can use `obj.__proto__` to access it (there are other ways too, to be covered soon).
- The object references by `[[Prototype]]` is called a "prototype".
- The object referenced by `[[Prototype]]` is called a "prototype".
- If we want to read a property of `obj` or call a method, and it doesn't exist, then JavaScript tries to find it in the prototype. Write/delete operations work directly on the object, they don't use the prototype (unless the property is actually a setter).
- If we call `obj.method()`, and the `method` is taken from the prototype, `this` still references `obj`. So methods always work with the current objects even if they are inherited.
- If we call `obj.method()`, and the `method` is taken from the prototype, `this` still references `obj`. So methods always work with the current object even if they are inherited.