This commit is contained in:
Ilya Kantor 2019-08-04 11:29:15 +03:00
parent 61bc426051
commit 800d47c1e1
6 changed files with 25 additions and 23 deletions

View file

@ -26,6 +26,7 @@ let rabbit = Object.create(animal);
*/!* */!*
alert(rabbit.eats); // true alert(rabbit.eats); // true
*!* *!*
alert(Object.getPrototypeOf(rabbit) === animal); // get the prototype of rabbit alert(Object.getPrototypeOf(rabbit) === animal); // get the prototype of rabbit
*/!* */!*
@ -78,7 +79,7 @@ As of now we have all these ways at our disposal.
Why was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`? That's an interesting question, requiring us to understand why `__proto__` is bad. Read on to get the answer. Why was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`? That's an interesting question, requiring us to understand why `__proto__` is bad. Read on to get the answer.
```warn header="Don't reset `[[Prototype]]` unless the speed doesn't matter" ```warn header="Don't change `[[Prototype]]` on existing objects if speed matters"
Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time, and then do not modify: `rabbit` inherits from `animal`, and that is not going to change. Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time, and then do not modify: `rabbit` inherits from `animal`, and that is not going to change.
And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation, it breaks internal optimizations for object property access operations. So evade it unless you know what you're doing, or JavaScript speed totally doesn't matter for you. And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation, it breaks internal optimizations for object property access operations. So evade it unless you know what you're doing, or JavaScript speed totally doesn't matter for you.
@ -111,7 +112,7 @@ Here the consequences are not terrible. But in other cases, we may be assigning
What's worst -- usually developers do not think about such possibility at all. That makes such bugs hard to notice and even turn them into vulnerabilities, especially when JavaScript is used on server-side. What's worst -- usually developers do not think about such possibility at all. That makes such bugs hard to notice and even turn them into vulnerabilities, especially when JavaScript is used on server-side.
Unexpected things also may happen when accessing `toString` property -- that's a function by default, and other built-in properties. Unexpected things also may happen when assigning to `toString` -- that's a function by default, and other built-in methods.
How to evade the problem? How to evade the problem?
@ -160,7 +161,7 @@ alert(obj); // Error (no toString)
...But that's usually fine for associative arrays. ...But that's usually fine for associative arrays.
Please note that most object-related methods are `Object.something(...)`, like `Object.keys(obj)` -- they are not in the prototype, so they will keep working on such objects: Note that most object-related methods are `Object.something(...)`, like `Object.keys(obj)` -- they are not in the prototype, so they will keep working on such objects:
```js run ```js run

View file

@ -25,7 +25,7 @@ class MyClass {
} }
``` ```
Then `new MyClass()` creates a new object with all the listed methods. Then use `new MyClass()` to create a new object with all the listed methods.
The `constructor()` method is called automatically by `new`, so we can initialize the object there. The `constructor()` method is called automatically by `new`, so we can initialize the object there.
@ -53,7 +53,7 @@ When `new User("John")` is called:
1. A new object is created. 1. A new object is created.
2. The `constructor` runs with the given argument and assigns `this.name` to it. 2. The `constructor` runs with the given argument and assigns `this.name` to it.
...Then we can call methods, such as `user.sayHi`. ...Then we can call object methods, such as `user.sayHi()`.
```warn header="No comma between class methods" ```warn header="No comma between class methods"
@ -191,7 +191,7 @@ let User = class {
}; };
``` ```
Similar to Named Function Expressions, class expressions may or may not have a name. Similar to Named Function Expressions, class expressions may have a name.
If a class expression has a name, it's visible inside the class only: If a class expression has a name, it's visible inside the class only:
@ -200,13 +200,13 @@ If a class expression has a name, it's visible inside the class only:
// (no such term in the spec, but that's similar to Named Function Expression) // (no such term in the spec, but that's similar to Named Function Expression)
let User = class *!*MyClass*/!* { let User = class *!*MyClass*/!* {
sayHi() { sayHi() {
alert(MyClass); // MyClass is visible only inside the class alert(MyClass); // MyClass name is visible only inside the class
} }
}; };
new User().sayHi(); // works, shows MyClass definition new User().sayHi(); // works, shows MyClass definition
alert(MyClass); // error, MyClass not visible outside of the class alert(MyClass); // error, MyClass name isn't visible outside of the class
``` ```
@ -282,13 +282,14 @@ Object.defineProperties(User.prototype, {
}); });
``` ```
Here's an example with computed properties: Here's an example with a computed property in brackets `[...]`:
```js run ```js run
function f() { return "sayHi"; }
class User { class User {
[f()]() {
*!*
['say' + 'Hi']() {
*/!*
alert("Hello"); alert("Hello");
} }
@ -309,7 +310,9 @@ In the example above, `User` only had methods. Let's add a property:
```js run ```js run
class User { class User {
*!*
name = "Anonymous"; name = "Anonymous";
*/!*
sayHi() { sayHi() {
alert(`Hello, ${this.name}!`); alert(`Hello, ${this.name}!`);
@ -319,8 +322,7 @@ class User {
new User().sayHi(); new User().sayHi();
``` ```
The property is not placed into `User.prototype`. Instead, it is created by `new`, separately for every object. So, the property will never be shared between different objects of the same class. The property `name` is not placed into `User.prototype`. Instead, it is created by `new` before calling constructor, it's the property of the object itself.
## Summary ## Summary
@ -328,7 +330,7 @@ The basic class syntax looks like this:
```js ```js
class MyClass { class MyClass {
prop = value; // field prop = value; // property
constructor(...) { // constructor constructor(...) { // constructor
// ... // ...
@ -339,7 +341,7 @@ class MyClass {
get something(...) {} // getter method get something(...) {} // getter method
set something(...) {} // setter method set something(...) {} // setter method
[Symbol.iterator]() {} // method with computed name/symbol name [Symbol.iterator]() {} // method with computed name (symbol here)
// ... // ...
} }
``` ```

View file

@ -92,7 +92,7 @@ Internally, `extends` keyword adds `[[Prototype]]` reference from `Rabbit.protot
So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `Animal.prototype`. So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `Animal.prototype`.
As we can recall from the chapter <info:native-prototypes>, JavaScript uses the same prototypal inheritance for build-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`, so dates have generic object methods. As we can recall from the chapter <info:native-prototypes>, JavaScript uses prototypal inheritance for build-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`, so dates have generic object methods.
````smart header="Any expression is allowed after `extends`" ````smart header="Any expression is allowed after `extends`"
Class syntax allows to specify not just a class, but any expression after `extends`. Class syntax allows to specify not just a class, but any expression after `extends`.
@ -131,7 +131,6 @@ class Rabbit extends Animal {
} }
``` ```
...But usually we don't want to totally replace a parent method, but rather to build on top of it, tweak or extend its functionality. We do something in our method, but call the parent method before/after it or in the process. ...But usually we don't want to totally replace a parent method, but rather to build on top of it, tweak or extend its functionality. We do something in our method, but call the parent method before/after it or in the process.
Classes provide `"super"` keyword for that. Classes provide `"super"` keyword for that.

View file

@ -5,7 +5,7 @@
<desc>Created with sketchtool.</desc> <desc>Created with sketchtool.</desc>
<g id="inheritance" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="inheritance" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="rabbit-animal-independent-animal.svg"> <g id="rabbit-animal-independent-animal.svg">
<path d="M238,23 L238,87 L416,87 L416,23 L238,23 Z" id="Rectangle-1" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB"></path> <path d="M238,23 L238,87 L440,87 L440,23 L238,23 Z" id="Rectangle-1" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB"></path>
<text id="constructor:-Animal" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="constructor:-Animal" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="248" y="29"></tspan> <tspan x="248" y="29"></tspan>
<tspan x="248" y="44">constructor: Animal</tspan> <tspan x="248" y="44">constructor: Animal</tspan>
@ -19,7 +19,7 @@
<text id="Animal" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="Animal" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="10" y="15">Animal</tspan> <tspan x="10" y="15">Animal</tspan>
</text> </text>
<path d="M237,168 L237,196 L415,196 L415,168 L237,168 Z" id="Rectangle-1-Copy-3" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB"></path> <path d="M237,168 L237,196 L439,196 L439,168 L237,168 Z" id="Rectangle-1-Copy-3" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB"></path>
<text id="new-Animal" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="new-Animal" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="239" y="160">new Animal</tspan> <tspan x="239" y="160">new Animal</tspan>
</text> </text>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

View file

@ -5,16 +5,16 @@
<desc>Created with sketchtool.</desc> <desc>Created with sketchtool.</desc>
<g id="inheritance" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="inheritance" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="animal-rabbit-static.svg"> <g id="animal-rabbit-static.svg">
<rect id="Rectangle-1" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="246" y="23" width="182" height="48"></rect> <rect id="Rectangle-1" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="246" y="23" width="207" height="48"></rect>
<text id="constructor:-Animal" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="constructor:-Animal" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="256" y="42">constructor: Animal</tspan> <tspan x="256" y="42">constructor: Animal</tspan>
<tspan x="256" y="57">run: function</tspan> <tspan x="256" y="57">run: function</tspan>
</text> </text>
<rect id="Rectangle-1-Copy" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="246" y="278" width="182" height="28"></rect> <rect id="Rectangle-1-Copy" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="246" y="278" width="207" height="28"></rect>
<text id="Animal.prototype" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="Animal.prototype" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="245" y="15">Animal.prototype</tspan> <tspan x="245" y="15">Animal.prototype</tspan>
</text> </text>
<rect id="Rectangle-1-Copy-4" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="246" y="158" width="182" height="48"></rect> <rect id="Rectangle-1-Copy-4" stroke="#E8C48E" stroke-width="2" fill="#FFF9EB" x="246" y="158" width="207" height="48"></rect>
<text id="constructor:-Rabbit" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="constructor:-Rabbit" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="256" y="177">constructor: Rabbit</tspan> <tspan x="256" y="177">constructor: Rabbit</tspan>
<tspan x="256" y="192">hide: function</tspan> <tspan x="256" y="192">hide: function</tspan>

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before After
Before After

Binary file not shown.