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(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.
```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.
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.
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?
@ -160,7 +161,7 @@ alert(obj); // Error (no toString)
...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

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.
@ -53,7 +53,7 @@ When `new User("John")` is called:
1. A new object is created.
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"
@ -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:
@ -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)
let User = class *!*MyClass*/!* {
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
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
function f() { return "sayHi"; }
class User {
[f()]() {
*!*
['say' + 'Hi']() {
*/!*
alert("Hello");
}
@ -309,7 +310,9 @@ In the example above, `User` only had methods. Let's add a property:
```js run
class User {
*!*
name = "Anonymous";
*/!*
sayHi() {
alert(`Hello, ${this.name}!`);
@ -319,8 +322,7 @@ class User {
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
@ -328,7 +330,7 @@ The basic class syntax looks like this:
```js
class MyClass {
prop = value; // field
prop = value; // property
constructor(...) { // constructor
// ...
@ -339,7 +341,7 @@ class MyClass {
get something(...) {} // getter 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`.
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`"
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.
Classes provide `"super"` keyword for that.

View file

@ -5,7 +5,7 @@
<desc>Created with sketchtool.</desc>
<g id="inheritance" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<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">
<tspan x="248" y="29"></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">
<tspan x="10" y="15">Animal</tspan>
</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">
<tspan x="239" y="160">new Animal</tspan>
</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>
<g id="inheritance" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<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">
<tspan x="256" y="42">constructor: Animal</tspan>
<tspan x="256" y="57">run: function</tspan>
</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">
<tspan x="245" y="15">Animal.prototype</tspan>
</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">
<tspan x="256" y="177">constructor: Rabbit</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.