◐ Shell
reader mode source ↗
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

1. `true`, taken from `rabbit`.
2. `null`, taken from `animal`.
3. `undefined`, there's no such property any more.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Working with prototype

Here's the code that creates a pair of objects, then modifies them.

Which values are shown in the process?

```js
let animal = {
Expand All @@ -28,4 +28,4 @@ delete animal.jumps;
alert( rabbit.jumps ); // ? (3)
```

There should be 3 answers.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

1. Let's add `__proto__`:

```js run
let head = {
Expand Down Expand Up @@ -27,6 +27,6 @@
alert( table.money ); // undefined
```

2. In modern engines, performance-wise, there's no difference whether we take a property from an object or its prototype. They remember where the property was found and reuse it in the next request.

For instance, for `pockets.glasses` they remember where they found `glasses` (in `head`), and next time will search right there. They are also smart enough to update internal caches if something changes, so that optimization is safe.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Searching algorithm

The task has two parts.

We have objects:

```js
let head = {
@@ -27,5 +27,5 @@ let pockets = {
};
```

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 `pockets.glasses` or `head.glasses`? Benchmark if needed.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
**The answer: `rabbit`.**

That's because `this` is an object before the dot, so `rabbit.eat()` modifies `rabbit`.

Property lookup and execution are two different things.

The method `rabbit.eat` is first found in the prototype, then executed with `this=rabbit`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Where it writes?

We have `rabbit` inheriting from `animal`.

If we call `rabbit.eat()`, which object receives the `full` property: `animal` or `rabbit`?

```js
let animal = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
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`), 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.

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*.

So all hamsters share a single stomach!

Both for `lazy.stomach.push(...)` and `speedy.stomach.push()`, the property `stomach` is found in the prototype (as it's not in the object itself), then the new data is pushed into it.

Please note that such thing doesn't happen in case of a simple assignment `this.stomach=`:

```js run
let hamster = {
stomach: [],

eat(food) {
*!*
// assign to this.stomach instead of this.stomach.push
this.stomach = [food];
*/!*
}
Expand All @@ -34,17 +34,17 @@ let lazy = {
__proto__: hamster
};

// Speedy one found the food
speedy.eat("apple");
alert( speedy.stomach ); // apple

// Lazy one's stomach is empty
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.

Also we can totally evade the problem by making sure that each hamster has their own stomach:

```js run
let hamster = {
Expand All @@ -69,12 +69,12 @@ let lazy = {
*/!*
};

// Speedy one found the food
speedy.eat("apple");
alert( speedy.stomach ); // apple

// Lazy one's stomach is empty
alert( lazy.stomach ); // <nothing>
```

As a common solution, all properties that describe the state of a particular object, like `stomach` above, should be written into that object. That prevents such problems.
Loading
Toggle all file notes Toggle all file annotations