Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
1. `true` — มาจาก `rabbit`
2. `null` — มาจาก `animal`
3. `undefined` — ไม่มีพร็อพเพอร์ตี้นี้แล้ว
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.
ต้องตอบให้ได้ 3 ข้อ
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

1. Let's add `__proto__`:
1. เพิ่ม `__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.
2. ในเอนจินสมัยใหม่ ด้านประสิทธิภาพแล้วไม่ต่างกัน ไม่ว่าจะดึงพร็อพเพอร์ตี้จากตัวออบเจ็กต์เองหรือจากโปรโตไทป์ เพราะเอนจินจำไว้ว่าเจอพร็อพเพอร์ตี้ที่ไหน แล้วครั้งต่อไปก็ไปหาตรงนั้นเลย

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.
ยกตัวอย่าง `pockets.glasses` เอนจินจะจำไว้ว่าเจอ `glasses` ที่ `head` แล้วครั้งต่อไปก็ค้นหาตรงนั้นทันที นอกจากนี้ยังฉลาดพอที่จะอัปเดตแคชเมื่อมีการเปลี่ยนแปลง จึงเป็นการ optimize ที่ปลอดภัย
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.
โจทย์นี้มี 2 ส่วน

Given the following objects:
กำหนดออบเจ็กต์ต่อไปนี้:

```js
let head = {
Expand All @@ -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.
1. ใช้ `__proto__` กำหนดโปรโตไทป์ให้การค้นหาพร็อพเพอร์ตี้ไล่ตามลำดับ: `pockets` -> `bed` -> `table` -> `head` เช่น `pockets.pen` ควรเป็น `3` (หาเจอใน `table`) และ `bed.glasses` ควรเป็น `1` (หาเจอใน `head`)
2. ตอบคำถาม: ระหว่าง `pockets.glasses` กับ `head.glasses` อันไหนเร็วกว่ากัน? ลอง benchmark ดูถ้าจำเป็น
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
**The answer: `rabbit`.**
**คำตอบ: `rabbit`**

That's because `this` is an object before the dot, so `rabbit.eat()` modifies `rabbit`.
เพราะ `this` คือออบเจ็กต์ที่อยู่หน้าจุด ดังนั้น `rabbit.eat()` จึงแก้ไข `rabbit`

Property lookup and execution are two different things.
การค้นหาพร็อพเพอร์ตี้กับการเรียกใช้เป็นคนละขั้นตอนกัน

The method `rabbit.eat` is first found in the prototype, then executed with `this=rabbit`.
เมธอด `rabbit.eat` ถูกค้นพบในโปรโตไทป์ก่อน จากนั้นจึงรันด้วย `this=rabbit`
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Where does it write?
# เขียนค่าลงที่ไหน?

We have `rabbit` inheriting from `animal`.
เรามี `rabbit` สืบทอดจาก `animal`

If we call `rabbit.eat()`, which object receives the `full` property: `animal` or `rabbit`?
ถ้าเรียก `rabbit.eat()` ออบเจ็กต์ไหนจะได้รับพร็อพเพอร์ตี้ `full`: `animal` หรือ `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")`.
มาดูกันให้ละเอียดว่าเกิดอะไรขึ้นตอนเรียก `speedy.eat("apple")`

1. The method `speedy.eat` is found in the prototype (`=hamster`), then executed with `this=speedy` (the object before the dot).
1. เมธอด `speedy.eat` ถูกค้นพบในโปรโตไทป์ (`=hamster`) แล้วรันด้วย `this=speedy` (ออบเจ็กต์ที่อยู่หน้าจุด)

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. จากนั้น `this.stomach.push()` ต้องหาพร็อพเพอร์ตี้ `stomach` เพื่อเรียก `push` โดยไปหาใน `this` (`=speedy`) ก่อน แต่ไม่เจอ

3. Then it follows the prototype chain and finds `stomach` in `hamster`.
3. จึงไล่ตามห่วงโซ่โปรโตไทป์ขึ้นไป แล้วเจอ `stomach` ใน `hamster`

4. Then it calls `push` on it, adding the food into *the stomach of the prototype*.
4. จากนั้นเรียก `push` บนมัน ซึ่งก็คือเพิ่มอาหารเข้าไปใน *stomach ของโปรโตไทป์*

So all hamsters share a single stomach!
แฮมสเตอร์ทุกตัวจึงใช้ 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.
ไม่ว่าจะเป็น `lazy.stomach.push(...)` หรือ `speedy.stomach.push()` พร็อพเพอร์ตี้ `stomach` ล้วนถูกค้นพบในโปรโตไทป์ (เพราะไม่ได้อยู่ในตัวออบเจ็กต์เอง) แล้วข้อมูลใหม่ก็ถูก push เข้าไปในนั้น

Please note that such thing doesn't happen in case of a simple assignment `this.stomach=`:
สังเกตว่าปัญหานี้จะไม่เกิดขึ้น ถ้าใช้การ assign ตรงๆ แบบ `this.stomach=`:

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

eat(food) {
*!*
// assign to this.stomach instead of this.stomach.push
// ใช้ assign แทน 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>
// ตัวขี้เกียจท้องยังว่าง
alert( lazy.stomach ); // <ไม่มีอะไร>
```

Now all works fine, because `this.stomach=` does not perform a lookup of `stomach`. The value is written directly into `this` object.
ตอนนี้ทำงานถูกต้องแล้ว เพราะ `this.stomach=` ไม่ได้ไปค้นหา `stomach` จากโปรโตไทป์ แต่เขียนค่าลงในตัว `this` โดยตรง

Also we can totally avoid the problem by making sure that each hamster has their own stomach:
อีกวิธีหนึ่งคือกำหนดให้แฮมสเตอร์แต่ละตัวมี 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>
// ตัวขี้เกียจท้องยังว่าง
alert( lazy.stomach ); // <ไม่มีอะไร>
```

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.
โดยทั่วไป พร็อพเพอร์ตี้ที่เก็บสถานะเฉพาะของแต่ละออบเจ็กต์ เช่น `stomach` ข้างต้น ควรประกาศไว้ในตัวออบเจ็กต์นั้นเลย จะได้ไม่เกิดปัญหาแบบนี้
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Why are both hamsters full?
# ทำไมแฮมสเตอร์ทั้งสองตัวถึงอิ่ม?

We have two hamsters: `speedy` and `lazy` inheriting from the general `hamster` object.
เรามีแฮมสเตอร์สองตัว: `speedy` และ `lazy` สืบทอดจากออบเจ็กต์ `hamster`

When we feed one of them, the other one is also full. Why? How can we fix it?
เวลาให้อาหารตัวหนึ่ง อีกตัวก็อิ่มด้วย ทำไมถึงเป็นแบบนั้น? แก้ยังไงดี?

```js run
let hamster = {
Expand All @@ -25,11 +25,11 @@ let lazy = {
__proto__: hamster
};

// This one found the food
// ตัวนี้หาอาหารเจอ
speedy.eat("apple");
alert( speedy.stomach ); // apple

// This one also has it, why? fix please.
// ตัวนี้ก็มีด้วย ทำไม? แก้ด้วยนะ
alert( lazy.stomach ); // apple
```

Loading