From e5316898f4ba6c93c634f2b36a08999872c46ece Mon Sep 17 00:00:00 2001 From: Prasit Tongpradit Date: Sun, 8 Mar 2026 09:14:52 +0700 Subject: [PATCH] docs: translate advanced-functions section (11 articles) --- .../01-recursion/01-sum-to/solution.md | 12 +- .../01-recursion/01-sum-to/task.md | 22 +- .../01-recursion/02-factorial/solution.md | 6 +- .../01-recursion/02-factorial/task.md | 12 +- .../03-fibonacci-numbers/solution.md | 60 ++-- .../01-recursion/03-fibonacci-numbers/task.md | 16 +- .../04-output-single-linked-list/solution.md | 26 +- .../04-output-single-linked-list/task.md | 10 +- .../solution.md | 16 +- .../task.md | 6 +- .../01-recursion/article.md | 294 +++++++++--------- .../02-rest-parameters-spread/article.md | 158 +++++----- .../1-closure-latest-changes/solution.md | 6 +- .../1-closure-latest-changes/task.md | 10 +- .../10-make-army/_js.view/solution.js | 4 +- .../10-make-army/_js.view/source.js | 10 +- .../03-closure/10-make-army/solution.md | 110 +++---- .../03-closure/10-make-army/task.md | 26 +- .../2-closure-variable-access/solution.md | 8 +- .../2-closure-variable-access/task.md | 14 +- .../3-counter-independent/solution.md | 6 +- .../03-closure/3-counter-independent/task.md | 6 +- .../4-counter-object-independent/solution.md | 6 +- .../4-counter-object-independent/task.md | 6 +- .../03-closure/5-function-in-if/solution.md | 4 +- .../03-closure/5-function-in-if/task.md | 4 +- .../03-closure/6-closure-sum/solution.md | 6 +- .../03-closure/6-closure-sum/task.md | 8 +- .../03-closure/7-let-scope/solution.md | 20 +- .../03-closure/7-let-scope/task.md | 6 +- .../_js.view/source.js | 4 +- .../8-filter-through-function/task.md | 20 +- .../9-sort-by-field/_js.view/source.js | 2 +- .../03-closure/9-sort-by-field/task.md | 16 +- .../03-closure/article.md | 260 ++++++++-------- 1-js/06-advanced-functions/04-var/article.md | 138 ++++---- .../05-global-object/article.md | 63 ++-- .../2-counter-inc-dec/_js.view/source.js | 8 +- .../2-counter-inc-dec/solution.md | 2 +- .../2-counter-inc-dec/task.md | 14 +- .../5-sum-many-brackets/_js.view/source.js | 2 +- .../5-sum-many-brackets/solution.md | 24 +- .../5-sum-many-brackets/task.md | 6 +- .../06-function-object/article.md | 170 +++++----- .../07-new-function/article.md | 71 ++--- .../1-output-numbers-100ms/solution.md | 12 +- .../1-output-numbers-100ms/task.md | 10 +- .../4-settimeout-result/solution.md | 8 +- .../4-settimeout-result/task.md | 18 +- .../08-settimeout-setinterval/article.md | 194 ++++++------ .../01-spy-decorator/_js.view/solution.js | 2 +- .../01-spy-decorator/_js.view/source.js | 2 +- .../01-spy-decorator/solution.md | 2 +- .../01-spy-decorator/task.md | 10 +- .../02-delay/solution.md | 14 +- .../09-call-apply-decorators/02-delay/task.md | 14 +- .../03-debounce/debounce.view/index.html | 10 +- .../03-debounce/solution.md | 2 +- .../03-debounce/task.md | 32 +- .../04-throttle/_js.view/solution.js | 10 +- .../04-throttle/solution.md | 10 +- .../04-throttle/task.md | 50 +-- .../09-call-apply-decorators/article.md | 246 +++++++-------- .../2-write-to-object-after-bind/solution.md | 6 +- .../2-write-to-object-after-bind/task.md | 4 +- .../10-bind/3-second-bind/solution.md | 6 +- .../10-bind/3-second-bind/task.md | 6 +- .../solution.md | 5 +- .../4-function-property-after-bind/task.md | 6 +- .../10-bind/5-question-use-bind/solution.md | 14 +- .../10-bind/5-question-use-bind/task.md | 8 +- .../10-bind/6-ask-partial/solution.md | 14 +- .../10-bind/6-ask-partial/task.md | 10 +- 1-js/06-advanced-functions/10-bind/article.md | 154 +++++---- .../12-arrow-functions/article.md | 74 ++--- 1-js/06-advanced-functions/index.md | 2 +- 76 files changed, 1326 insertions(+), 1327 deletions(-) diff --git a/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md b/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md index 11667f940..ff819662c 100644 --- a/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md +++ b/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md @@ -1,4 +1,4 @@ -The solution using a loop: +วิธีใช้ลูป: ```js run function sumTo(n) { @@ -12,7 +12,7 @@ function sumTo(n) { alert( sumTo(100) ); ``` -The solution using recursion: +วิธีใช้การเรียกซ้ำ: ```js run function sumTo(n) { @@ -23,7 +23,7 @@ function sumTo(n) { alert( sumTo(100) ); ``` -The solution using the formula: `sumTo(n) = n*(n+1)/2`: +วิธีใช้สูตร: `sumTo(n) = n*(n+1)/2`: ```js run function sumTo(n) { @@ -33,8 +33,8 @@ function sumTo(n) { alert( sumTo(100) ); ``` -P.S. Naturally, the formula is the fastest solution. It uses only 3 operations for any number `n`. The math helps! +P.S. แน่นอนว่าสูตรคณิตศาสตร์เร็วที่สุด ใช้แค่ 3 การดำเนินการไม่ว่า `n` จะเป็นเท่าไหร่ คณิตศาสตร์ช่วยได้! -The loop variant is the second in terms of speed. In both the recursive and the loop variant we sum the same numbers. But the recursion involves nested calls and execution stack management. That also takes resources, so it's slower. +วิธีลูปเร็วเป็นอันดับสอง ทั้งแบบเรียกซ้ำและแบบลูปบวกตัวเลขชุดเดียวกัน แต่การเรียกซ้ำมีการเรียกฟังก์ชันซ้อนและจัดการ execution stack ซึ่งกินทรัพยากรเพิ่ม จึงช้ากว่า -P.P.S. Some engines support the "tail call" optimization: if a recursive call is the very last one in the function, with no other calculations performed, then the outer function will not need to resume the execution, so the engine doesn't need to remember its execution context. That removes the burden on memory. But if the JavaScript engine does not support tail call optimization (most of them don't), there will be an error: maximum stack size exceeded, because there's usually a limitation on the total stack size. +P.P.S. บาง engine รองรับการปรับแต่ง "tail call" คือถ้าการเรียกซ้ำเป็นคำสั่งสุดท้ายของฟังก์ชัน โดยไม่มีการคำนวณอื่นอีก ฟังก์ชันภายนอกก็ไม่ต้องกลับมาทำงานต่อ ดังนั้น engine ไม่ต้องจำ execution context ช่วยลดภาระหน่วยความจำได้ แต่ถ้า JavaScript engine ไม่รองรับ tail call optimization (ส่วนใหญ่ไม่รองรับ) ก็จะเกิด error: maximum stack size exceeded เพราะปกติจะมีการจำกัดขนาดสแต็กรวมไว้ diff --git a/1-js/06-advanced-functions/01-recursion/01-sum-to/task.md b/1-js/06-advanced-functions/01-recursion/01-sum-to/task.md index cabc13290..08289aa83 100644 --- a/1-js/06-advanced-functions/01-recursion/01-sum-to/task.md +++ b/1-js/06-advanced-functions/01-recursion/01-sum-to/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Sum all numbers till the given one +# หาผลรวมของตัวเลขทั้งหมดจนถึงจำนวนที่กำหนด -Write a function `sumTo(n)` that calculates the sum of numbers `1 + 2 + ... + n`. +เขียนฟังก์ชัน `sumTo(n)` ที่คำนวณผลรวมของตัวเลข `1 + 2 + ... + n` -For instance: +ตัวอย่าง: ```js no-beautify sumTo(1) = 1 @@ -17,20 +17,20 @@ sumTo(4) = 4 + 3 + 2 + 1 = 10 sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050 ``` -Make 3 solution variants: +เขียนคำตอบ 3 แบบ: -1. Using a for loop. -2. Using a recursion, cause `sumTo(n) = n + sumTo(n-1)` for `n > 1`. -3. Using the [arithmetic progression](https://en.wikipedia.org/wiki/Arithmetic_progression) formula. +1. ใช้ for loop +2. ใช้การเรียกซ้ำ เพราะ `sumTo(n) = n + sumTo(n-1)` เมื่อ `n > 1` +3. ใช้สูตร[อนุกรมเลขคณิต](https://en.wikipedia.org/wiki/Arithmetic_progression) -An example of the result: +ตัวอย่างผลลัพธ์: ```js -function sumTo(n) { /*... your code ... */ } +function sumTo(n) { /*... โค้ดของคุณ ... */ } alert( sumTo(100) ); // 5050 ``` -P.S. Which solution variant is the fastest? The slowest? Why? +P.S. วิธีไหนเร็วที่สุด? ช้าที่สุด? ทำไม? -P.P.S. Can we use recursion to count `sumTo(100000)`? +P.P.S. เราใช้การเรียกซ้ำคำนวณ `sumTo(100000)` ได้ไหม? diff --git a/1-js/06-advanced-functions/01-recursion/02-factorial/solution.md b/1-js/06-advanced-functions/01-recursion/02-factorial/solution.md index 09e511db5..5b9f882a6 100644 --- a/1-js/06-advanced-functions/01-recursion/02-factorial/solution.md +++ b/1-js/06-advanced-functions/01-recursion/02-factorial/solution.md @@ -1,6 +1,6 @@ -By definition, a factorial `n!` can be written as `n * (n-1)!`. +จากนิยาม แฟกทอเรียล `n!` เขียนได้เป็น `n * (n-1)!` -In other words, the result of `factorial(n)` can be calculated as `n` multiplied by the result of `factorial(n-1)`. And the call for `n-1` can recursively descend lower, and lower, till `1`. +พูดอีกอย่างก็คือ ผลลัพธ์ของ `factorial(n)` คือ `n` คูณกับผลลัพธ์ของ `factorial(n-1)` แล้วการเรียกที่ `n-1` ก็เรียกซ้ำลดลงไปเรื่อยๆ จนถึง `1` ```js run function factorial(n) { @@ -10,7 +10,7 @@ function factorial(n) { alert( factorial(5) ); // 120 ``` -The basis of recursion is the value `1`. We can also make `0` the basis here, doesn't matter much, but gives one more recursive step: +ฐานของการเรียกซ้ำคือค่า `1` เราจะใช้ `0` เป็นฐานแทนก็ได้ ไม่ต่างกันมาก แค่เพิ่มขั้นตอนการเรียกซ้ำอีกหนึ่งครั้ง: ```js run function factorial(n) { diff --git a/1-js/06-advanced-functions/01-recursion/02-factorial/task.md b/1-js/06-advanced-functions/01-recursion/02-factorial/task.md index d2aef2d90..2157bcdee 100644 --- a/1-js/06-advanced-functions/01-recursion/02-factorial/task.md +++ b/1-js/06-advanced-functions/01-recursion/02-factorial/task.md @@ -2,17 +2,17 @@ importance: 4 --- -# Calculate factorial +# คำนวณแฟกทอเรียล -The [factorial](https://en.wikipedia.org/wiki/Factorial) of a natural number is a number multiplied by `"number minus one"`, then by `"number minus two"`, and so on till `1`. The factorial of `n` is denoted as `n!` +[แฟกทอเรียล](https://en.wikipedia.org/wiki/Factorial)ของจำนวนธรรมชาติ คือการนำจำนวนนั้นคูณกับ "จำนวนนั้นลบหนึ่ง" แล้วคูณกับ "จำนวนนั้นลบสอง" ไปเรื่อยๆ จนถึง `1` แฟกทอเรียลของ `n` เขียนแทนด้วย `n!` -We can write a definition of factorial like this: +เขียนเป็นนิยามได้แบบนี้: ```js n! = n * (n - 1) * (n - 2) * ...*1 ``` -Values of factorials for different `n`: +ค่าแฟกทอเรียลของ `n` ค่าต่างๆ: ```js 1! = 1 @@ -22,10 +22,10 @@ Values of factorials for different `n`: 5! = 5 * 4 * 3 * 2 * 1 = 120 ``` -The task is to write a function `factorial(n)` that calculates `n!` using recursive calls. +โจทย์คือเขียนฟังก์ชัน `factorial(n)` ที่คำนวณ `n!` โดยใช้การเรียกซ้ำ ```js alert( factorial(5) ); // 120 ``` -P.S. Hint: `n!` can be written as `n * (n-1)!` For instance: `3! = 3*2! = 3*2*1! = 6` +P.S. คำใบ้: `n!` เขียนในรูป `n * (n-1)!` ได้ เช่น `3! = 3*2! = 3*2*1! = 6` diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md index 36524a45a..32c59b496 100644 --- a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md +++ b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md @@ -1,6 +1,6 @@ -The first solution we could try here is the recursive one. +วิธีแรกที่ลองได้คือแบบเรียกซ้ำ -Fibonacci numbers are recursive by definition: +ตัวเลขฟีโบนักชีถูกนิยามแบบเรียกซ้ำอยู่แล้ว: ```js run function fib(n) { @@ -9,14 +9,14 @@ function fib(n) { alert( fib(3) ); // 2 alert( fib(7) ); // 13 -// fib(77); // will be extremely slow! +// fib(77); // จะช้ามาก! ``` -...But for big values of `n` it's very slow. For instance, `fib(77)` may hang up the engine for some time eating all CPU resources. +...แต่ถ้า `n` มีค่ามาก จะช้ามากๆ เช่น `fib(77)` อาจทำให้ engine ค้างเลย เพราะกิน CPU ไปหมด -That's because the function makes too many subcalls. The same values are re-evaluated again and again. +ที่เป็นแบบนี้เพราะฟังก์ชันเรียกซ้อนมากเกินไป ค่าเดิมๆ ถูกคำนวณซ้ำแล้วซ้ำอีก -For instance, let's see a piece of calculations for `fib(5)`: +ลองดูส่วนหนึ่งของการคำนวณ `fib(5)`: ```js no-beautify ... @@ -25,68 +25,68 @@ fib(4) = fib(3) + fib(2) ... ``` -Here we can see that the value of `fib(3)` is needed for both `fib(5)` and `fib(4)`. So `fib(3)` will be called and evaluated two times completely independently. +จะเห็นว่าค่า `fib(3)` ถูกใช้ทั้งใน `fib(5)` และ `fib(4)` ดังนั้น `fib(3)` จะถูกเรียกและคำนวณซ้ำสองครั้งโดยอิสระต่อกัน -Here's the full recursion tree: +นี่คือต้นไม้การเรียกซ้ำทั้งหมด: ![fibonacci recursion tree](fibonacci-recursion-tree.svg) -We can clearly notice that `fib(3)` is evaluated two times and `fib(2)` is evaluated three times. The total amount of computations grows much faster than `n`, making it enormous even for `n=77`. +เห็นชัดเลยว่า `fib(3)` ถูกคำนวณสองครั้ง และ `fib(2)` ถูกคำนวณสามครั้ง จำนวนการคำนวณทั้งหมดเพิ่มขึ้นเร็วกว่า `n` มาก จนมหาศาลแม้แค่ `n=77` -We can optimize that by remembering already-evaluated values: if a value of say `fib(3)` is calculated once, then we can just reuse it in future computations. +เราแก้ปัญหาได้โดยจำค่าที่คำนวณไปแล้ว: ถ้า `fib(3)` คำนวณแล้วครั้งหนึ่ง ก็นำค่ากลับมาใช้ได้เลยในการคำนวณครั้งถัดไป -Another variant would be to give up recursion and use a totally different loop-based algorithm. +อีกทางเลือกหนึ่งคือเลิกใช้การเรียกซ้ำ แล้วใช้อัลกอริทึมแบบลูปแทนเลย -Instead of going from `n` down to lower values, we can make a loop that starts from `1` and `2`, then gets `fib(3)` as their sum, then `fib(4)` as the sum of two previous values, then `fib(5)` and goes up and up, till it gets to the needed value. On each step we only need to remember two previous values. +แทนที่จะเริ่มจาก `n` แล้วลดลง เราเริ่มจาก `1` กับ `2` แล้วหา `fib(3)` จากผลรวมของสองตัว จากนั้นหา `fib(4)` จากผลรวมของสองค่าก่อนหน้า แล้วก็ `fib(5)` ไล่ขึ้นไปเรื่อยๆ จนถึงค่าที่ต้องการ ในแต่ละรอบต้องจำแค่สองค่าก่อนหน้า -Here are the steps of the new algorithm in details. +รายละเอียดขั้นตอนของอัลกอริทึมใหม่: -The start: +จุดเริ่มต้น: ```js -// a = fib(1), b = fib(2), these values are by definition 1 +// a = fib(1), b = fib(2) ค่าเหล่านี้เท่ากับ 1 ตามนิยาม let a = 1, b = 1; -// get c = fib(3) as their sum +// หา c = fib(3) จากผลรวม let c = a + b; -/* we now have fib(1), fib(2), fib(3) +/* ตอนนี้เรามี fib(1), fib(2), fib(3) a b c 1, 1, 2 */ ``` -Now we want to get `fib(4) = fib(2) + fib(3)`. +ต่อไปเราต้องการ `fib(4) = fib(2) + fib(3)` -Let's shift the variables: `a,b` will get `fib(2),fib(3)`, and `c` will get their sum: +เลื่อนตัวแปร: `a,b` จะรับค่า `fib(2),fib(3)` แล้ว `c` จะเป็นผลรวม: ```js no-beautify -a = b; // now a = fib(2) -b = c; // now b = fib(3) +a = b; // ตอนนี้ a = fib(2) +b = c; // ตอนนี้ b = fib(3) c = a + b; // c = fib(4) -/* now we have the sequence: +/* ตอนนี้ลำดับเป็น: a b c 1, 1, 2, 3 */ ``` -The next step gives another sequence number: +ขั้นตอนถัดไปก็ได้ตัวเลขในลำดับอีกตัว: ```js no-beautify -a = b; // now a = fib(3) -b = c; // now b = fib(4) +a = b; // ตอนนี้ a = fib(3) +b = c; // ตอนนี้ b = fib(4) c = a + b; // c = fib(5) -/* now the sequence is (one more number): +/* ตอนนี้ลำดับเป็น (เพิ่มอีกหนึ่งตัว): a b c 1, 1, 2, 3, 5 */ ``` -...And so on until we get the needed value. That's much faster than recursion and involves no duplicate computations. +...ทำไปเรื่อยๆ จนได้ค่าที่ต้องการ วิธีนี้เร็วกว่าการเรียกซ้ำมาก และไม่มีการคำนวณซ้ำ -The full code: +โค้ดเต็ม: ```js run function fib(n) { @@ -105,6 +105,6 @@ alert( fib(7) ); // 13 alert( fib(77) ); // 5527939700884757 ``` -The loop starts with `i=3`, because the first and the second sequence values are hard-coded into variables `a=1`, `b=1`. +ลูปเริ่มที่ `i=3` เพราะค่าแรกและค่าที่สองของลำดับถูกกำหนดไว้แล้วในตัวแปร `a=1`, `b=1` -The approach is called [dynamic programming bottom-up](https://en.wikipedia.org/wiki/Dynamic_programming). +วิธีนี้เรียกว่า [dynamic programming แบบ bottom-up](https://en.wikipedia.org/wiki/Dynamic_programming) diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md index 3cdadd219..9fefe1ed5 100644 --- a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md +++ b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md @@ -2,24 +2,24 @@ importance: 5 --- -# Fibonacci numbers +# ตัวเลขฟีโบนักชี -The sequence of [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) has the formula Fn = Fn-1 + Fn-2. In other words, the next number is a sum of the two preceding ones. +ลำดับ[ตัวเลขฟีโบนักชี](https://en.wikipedia.org/wiki/Fibonacci_number)มีสูตรว่า Fn = Fn-1 + Fn-2 พูดง่ายๆ ก็คือ ตัวเลขถัดไปเป็นผลรวมของสองตัวก่อนหน้า -First two numbers are `1`, then `2(1+1)`, then `3(1+2)`, `5(2+3)` and so on: `1, 1, 2, 3, 5, 8, 13, 21...`. +สองตัวแรกคือ `1` จากนั้นก็เป็น `2(1+1)` แล้วก็ `3(1+2)`, `5(2+3)` ไปเรื่อยๆ: `1, 1, 2, 3, 5, 8, 13, 21...` -Fibonacci numbers are related to the [Golden ratio](https://en.wikipedia.org/wiki/Golden_ratio) and many natural phenomena around us. +ตัวเลขฟีโบนักชีมีความเกี่ยวข้องกับ[อัตราส่วนทอง](https://en.wikipedia.org/wiki/Golden_ratio)และปรากฏการณ์ทางธรรมชาติมากมายรอบตัวเรา -Write a function `fib(n)` that returns the `n-th` Fibonacci number. +เขียนฟังก์ชัน `fib(n)` ที่คืนค่าตัวเลขฟีโบนักชีลำดับที่ `n` -An example of work: +ตัวอย่างการทำงาน: ```js -function fib(n) { /* your code */ } +function fib(n) { /* โค้ดของคุณ */ } alert(fib(3)); // 2 alert(fib(7)); // 13 alert(fib(77)); // 5527939700884757 ``` -P.S. The function should be fast. The call to `fib(77)` should take no more than a fraction of a second. +P.S. ฟังก์ชันต้องทำงานเร็ว การเรียก `fib(77)` ควรใช้เวลาไม่ถึงวินาที diff --git a/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md b/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md index cfcbffea5..63ad28241 100644 --- a/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md +++ b/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md @@ -1,6 +1,6 @@ -# Loop-based solution +# วิธีใช้ลูป -The loop-based variant of the solution: +คำตอบแบบใช้ลูป: ```js run let list = { @@ -30,7 +30,7 @@ function printList(list) { printList(list); ``` -Please note that we use a temporary variable `tmp` to walk over the list. Technically, we could use a function parameter `list` instead: +สังเกตว่าเราใช้ตัวแปรชั่วคราว `tmp` ในการท่องผ่าน list ในทางเทคนิค เราใช้พารามิเตอร์ `list` โดยตรงก็ได้: ```js function printList(list) { @@ -43,15 +43,15 @@ function printList(list) { } ``` -...But that would be unwise. In the future we may need to extend a function, do something else with the list. If we change `list`, then we lose such ability. +...แต่นั่นไม่ใช่ความคิดที่ดี เพราะในอนาคตเราอาจต้องขยายฟังก์ชัน หรือทำอะไรอย่างอื่นกับ list ถ้าเปลี่ยนค่า `list` ไปแล้ว จะทำไม่ได้ -Talking about good variable names, `list` here is the list itself. The first element of it. And it should remain like that. That's clear and reliable. +ในแง่ชื่อตัวแปรที่ดี `list` ตรงนี้คือตัว list เอง คือสมาชิกตัวแรก ควรคงค่าไว้แบบนั้น เพื่อความชัดเจนและเชื่อถือได้ -From the other side, the role of `tmp` is exclusively a list traversal, like `i` in the `for` loop. +ส่วน `tmp` มีหน้าที่แค่ท่องผ่าน list เท่านั้น เหมือน `i` ในลูป `for` -# Recursive solution +# วิธีใช้การเรียกซ้ำ -The recursive variant of `printList(list)` follows a simple logic: to output a list we should output the current element `list`, then do the same for `list.next`: +คำตอบแบบเรียกซ้ำของ `printList(list)` ใช้หลักคิดง่ายๆ คือ แสดงสมาชิกปัจจุบัน `list` แล้วทำแบบเดียวกันกับ `list.next`: ```js run let list = { @@ -70,10 +70,10 @@ let list = { function printList(list) { - alert(list.value); // output the current item + alert(list.value); // แสดงสมาชิกปัจจุบัน if (list.next) { - printList(list.next); // do the same for the rest of the list + printList(list.next); // ทำแบบเดียวกันกับสมาชิกที่เหลือ } } @@ -81,8 +81,8 @@ function printList(list) { printList(list); ``` -Now what's better? +แล้วแบบไหนดีกว่ากัน? -Technically, the loop is more effective. These two variants do the same, but the loop does not spend resources for nested function calls. +ในทางเทคนิค ลูปมีประสิทธิภาพดีกว่า ทั้งสองวิธีทำงานเหมือนกัน แต่ลูปไม่เปลืองทรัพยากรในการเรียกฟังก์ชันซ้อน -From the other side, the recursive variant is shorter and sometimes easier to understand. +อีกด้านหนึ่ง แบบเรียกซ้ำเขียนสั้นกว่า และบางครั้งก็เข้าใจง่ายกว่า diff --git a/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md b/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md index 1076b952a..0d3c44bca 100644 --- a/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md +++ b/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Output a single-linked list +# แสดงผล linked list -Let's say we have a single-linked list (as described in the chapter ): +สมมติเรามี linked list (ตามที่อธิบายในบท ): ```js let list = { @@ -22,8 +22,8 @@ let list = { }; ``` -Write a function `printList(list)` that outputs list items one-by-one. +เขียนฟังก์ชัน `printList(list)` ที่แสดงสมาชิกใน list ทีละตัว -Make two variants of the solution: using a loop and using recursion. +เขียนคำตอบ 2 แบบ: ใช้ลูป และใช้การเรียกซ้ำ -What's better: with recursion or without it? +แบบไหนดีกว่ากัน? แบบเรียกซ้ำ หรือไม่เรียกซ้ำ? diff --git a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md index 0eb76ea1c..11a71dbcc 100644 --- a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md +++ b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md @@ -1,8 +1,8 @@ -# Using a recursion +# วิธีใช้การเรียกซ้ำ -The recursive logic is a little bit tricky here. +หลักคิดแบบเรียกซ้ำตรงนี้ต้องคิดสักนิด -We need to first output the rest of the list and *then* output the current one: +เราต้องแสดงผลสมาชิกที่เหลือของ list ก่อน *แล้วค่อย* แสดงสมาชิกปัจจุบัน: ```js run let list = { @@ -31,13 +31,13 @@ function printReverseList(list) { printReverseList(list); ``` -# Using a loop +# วิธีใช้ลูป -The loop variant is also a little bit more complicated than the direct output. +วิธีลูปก็ซับซ้อนกว่าการแสดงผลตามลำดับปกติเล็กน้อย -There is no way to get the last value in our `list`. We also can't "go back". +ไม่มีทางดึงค่าตัวสุดท้ายของ `list` ได้โดยตรง และไม่สามารถ "ย้อนกลับ" ได้ -So what we can do is to first go through the items in the direct order and remember them in an array, and then output what we remembered in the reverse order: +ดังนั้น สิ่งที่ทำได้คือท่องผ่านสมาชิกตามลำดับปกติก่อน แล้วเก็บไว้ในอาร์เรย์ จากนั้นค่อยแสดงผลจากท้ายมาหน้า: ```js run let list = { @@ -71,4 +71,4 @@ function printReverseList(list) { printReverseList(list); ``` -Please note that the recursive solution actually does exactly the same: it follows the list, remembers the items in the chain of nested calls (in the execution context stack), and then outputs them. +สังเกตว่าวิธีเรียกซ้ำทำงานเหมือนกันทุกประการ — ท่องผ่าน list แล้วจำสมาชิกไว้ในสายการเรียกซ้อน (ใน execution context stack) จากนั้นค่อยแสดงผลออกมา diff --git a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md index 81b1f3e33..24abcd217 100644 --- a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md +++ b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md @@ -2,8 +2,8 @@ importance: 5 --- -# Output a single-linked list in the reverse order +# แสดงผล linked list แบบย้อนกลับ -Output a single-linked list from the previous task in the reverse order. +แสดงผล linked list จากโจทย์ก่อนหน้า แบบย้อนกลับ -Make two solutions: using a loop and using a recursion. +เขียนคำตอบ 2 แบบ: ใช้ลูป และใช้การเรียกซ้ำ diff --git a/1-js/06-advanced-functions/01-recursion/article.md b/1-js/06-advanced-functions/01-recursion/article.md index 5ae894474..243375608 100644 --- a/1-js/06-advanced-functions/01-recursion/article.md +++ b/1-js/06-advanced-functions/01-recursion/article.md @@ -1,18 +1,18 @@ -# Recursion and stack +# การเรียกซ้ำ (Recursion) และสแต็ก -Let's return to functions and study them more in-depth. +กลับมาศึกษาเรื่องฟังก์ชันกันต่อ คราวนี้เราจะเจาะลึกมากขึ้น -Our first topic will be *recursion*. +หัวข้อแรกที่เราจะพูดถึงคือ *การเรียกซ้ำ (recursion)* -If you are not new to programming, then it is probably familiar and you could skip this chapter. +ถ้าเขียนโปรแกรมมาบ้างแล้ว อาจจะคุ้นเคยกับเรื่องนี้อยู่ ข้ามบทนี้ไปได้เลย -Recursion is a programming pattern that is useful in situations when a task can be naturally split into several tasks of the same kind, but simpler. Or when a task can be simplified into an easy action plus a simpler variant of the same task. Or, as we'll see soon, to deal with certain data structures. +การเรียกซ้ำเป็นรูปแบบการเขียนโปรแกรมที่มีประโยชน์เวลาที่โจทย์สามารถแตกออกเป็นโจทย์ย่อยที่เหมือนกันแต่ง่ายกว่า หรือเวลาที่โจทย์หนึ่งสามารถลดรูปเป็น "การกระทำง่ายๆ + โจทย์เดิมที่เล็กลง" หรืออย่างที่เราจะได้เห็นในบทนี้ ใช้จัดการโครงสร้างข้อมูลบางประเภทได้ดี -When a function solves a task, in the process it can call many other functions. A partial case of this is when a function calls *itself*. That's called *recursion*. +ในขั้นตอนการทำงานของฟังก์ชัน อาจมีการเรียกฟังก์ชันอื่นๆ อีกหลายตัว กรณีพิเศษอย่างหนึ่งก็คือเมื่อฟังก์ชัน *เรียกตัวเอง* นั่นแหละที่เราเรียกว่า *การเรียกซ้ำ (recursion)* -## Two ways of thinking +## สองวิธีคิด -For something simple to start with -- let's write a function `pow(x, n)` that raises `x` to a natural power of `n`. In other words, multiplies `x` by itself `n` times. +เริ่มจากเรื่องง่ายๆ กันก่อน -- มาเขียนฟังก์ชัน `pow(x, n)` ที่ยกกำลัง `x` ด้วยจำนวนเต็มบวก `n` พูดง่ายๆ ก็คือ คูณ `x` ด้วยตัวเอง `n` ครั้ง ```js pow(2, 2) = 4 @@ -20,15 +20,15 @@ pow(2, 3) = 8 pow(2, 4) = 16 ``` -There are two ways to implement it. +มีสองวิธีในการเขียน -1. Iterative thinking: the `for` loop: +1. คิดแบบวนลูป: ใช้ `for` loop ```js run function pow(x, n) { let result = 1; - // multiply result by x n times in the loop + // คูณ result ด้วย x จำนวน n ครั้งในลูป for (let i = 0; i < n; i++) { result *= x; } @@ -39,7 +39,7 @@ There are two ways to implement it. alert( pow(2, 3) ); // 8 ``` -2. Recursive thinking: simplify the task and call self: +2. คิดแบบเรียกซ้ำ: ลดรูปโจทย์แล้วเรียกตัวเอง ```js run function pow(x, n) { @@ -53,9 +53,9 @@ There are two ways to implement it. alert( pow(2, 3) ); // 8 ``` -Please note how the recursive variant is fundamentally different. +สังเกตว่าวิธีเรียกซ้ำนั้นแตกต่างจากวิธีแรกโดยสิ้นเชิง -When `pow(x, n)` is called, the execution splits into two branches: +เมื่อเรียก `pow(x, n)` การทำงานจะแยกออกเป็นสองทาง: ```js if n==1 = x @@ -65,27 +65,27 @@ pow(x, n) = else = x * pow(x, n - 1) ``` -1. If `n == 1`, then everything is trivial. It is called *the base* of recursion, because it immediately produces the obvious result: `pow(x, 1)` equals `x`. -2. Otherwise, we can represent `pow(x, n)` as `x * pow(x, n - 1)`. In maths, one would write xn = x * xn-1. This is called *a recursive step*: we transform the task into a simpler action (multiplication by `x`) and a simpler call of the same task (`pow` with lower `n`). Next steps simplify it further and further until `n` reaches `1`. +1. ถ้า `n == 1` ก็ง่ายเลย เราเรียกกรณีนี้ว่า *ฐานของการเรียกซ้ำ (base of recursion)* เพราะให้ผลลัพธ์ทันที: `pow(x, 1)` เท่ากับ `x` +2. กรณีอื่น เราสามารถเขียน `pow(x, n)` ในรูป `x * pow(x, n - 1)` ได้ ในทางคณิตศาสตร์คือ xn = x * xn-1 ตรงนี้เรียกว่า *ขั้นตอนการเรียกซ้ำ (recursive step)* — เราแปลงโจทย์ให้ง่ายลงโดยเหลือแค่การคูณด้วย `x` บวกกับการเรียก `pow` ที่ `n` ลดลง แล้วก็ลดลงเรื่อยๆ จนกว่า `n` จะถึง `1` -We can also say that `pow` *recursively calls itself* till `n == 1`. +พูดอีกอย่างก็คือ `pow` *เรียกตัวเองซ้ำไปเรื่อยๆ* จนกว่า `n == 1` ![recursive diagram of pow](recursion-pow.svg) -For example, to calculate `pow(2, 4)` the recursive variant does these steps: +ยกตัวอย่าง การคำนวณ `pow(2, 4)` ด้วยวิธีเรียกซ้ำจะมีขั้นตอนดังนี้: 1. `pow(2, 4) = 2 * pow(2, 3)` 2. `pow(2, 3) = 2 * pow(2, 2)` 3. `pow(2, 2) = 2 * pow(2, 1)` 4. `pow(2, 1) = 2` -So, the recursion reduces a function call to a simpler one, and then -- to even more simpler, and so on, until the result becomes obvious. +การเรียกซ้ำจะลดรูปการเรียกฟังก์ชันให้ง่ายลงเรื่อยๆ จนกระทั่งผลลัพธ์ชัดเจน -````smart header="Recursion is usually shorter" -A recursive solution is usually shorter than an iterative one. +````smart header="การเรียกซ้ำมักเขียนได้สั้นกว่า" +โค้ดแบบเรียกซ้ำมักสั้นกว่าแบบใช้ลูป -Here we can rewrite the same using the conditional operator `?` instead of `if` to make `pow(x, n)` more terse and still very readable: +ตรงนี้เราเขียนใหม่ได้โดยใช้ตัวดำเนินการเงื่อนไข `?` แทน `if` ทำให้ `pow(x, n)` กระชับขึ้นแต่ยังอ่านง่าย: ```js run function pow(x, n) { @@ -94,36 +94,36 @@ function pow(x, n) { ``` ```` -The maximal number of nested calls (including the first one) is called *recursion depth*. In our case, it will be exactly `n`. +จำนวนการเรียกซ้อนสูงสุด (รวมการเรียกครั้งแรกด้วย) เรียกว่า *ความลึกของการเรียกซ้ำ (recursion depth)* ในกรณีของเรา จะเท่ากับ `n` พอดี -The maximal recursion depth is limited by JavaScript engine. We can rely on it being 10000, some engines allow more, but 100000 is probably out of limit for the majority of them. There are automatic optimizations that help alleviate this ("tail calls optimizations"), but they are not yet supported everywhere and work only in simple cases. +JavaScript engine จำกัดความลึกของการเรียกซ้ำไว้ โดยทั่วไปเชื่อถือได้ที่ 10000 ชั้น บาง engine รองรับมากกว่านั้น แต่ 100000 ชั้นน่าจะเกินขีดจำกัดของ engine ส่วนใหญ่ มีการปรับแต่งประสิทธิภาพอัตโนมัติที่ช่วยได้ (เรียกว่า "tail calls optimizations") แต่ยังไม่รองรับทุกที่ และใช้ได้เฉพาะกรณีง่ายๆ เท่านั้น -That limits the application of recursion, but it still remains very wide. There are many tasks where recursive way of thinking gives simpler code, easier to maintain. +ข้อจำกัดนี้ทำให้ใช้การเรียกซ้ำได้ไม่ทุกกรณี แต่ก็ยังมีประโยชน์อยู่มาก หลายโจทย์ที่ใช้วิธีคิดแบบเรียกซ้ำจะได้โค้ดที่เรียบง่ายและดูแลรักษาง่ายกว่า -## The execution context and stack +## Execution context และสแต็ก -Now let's examine how recursive calls work. For that we'll look under the hood of functions. +ทีนี้มาดูกันว่าการเรียกซ้ำทำงานอย่างไร เราจะเจาะลึกเข้าไปดูเบื้องหลังของฟังก์ชัน -The information about the process of execution of a running function is stored in its *execution context*. +ข้อมูลเกี่ยวกับการทำงานของฟังก์ชันที่กำลังรันอยู่จะถูกเก็บไว้ใน *execution context* -The [execution context](https://tc39.github.io/ecma262/#sec-execution-contexts) is an internal data structure that contains details about the execution of a function: where the control flow is now, the current variables, the value of `this` (we don't use it here) and few other internal details. +[Execution context](https://tc39.github.io/ecma262/#sec-execution-contexts) เป็นโครงสร้างข้อมูลภายในที่เก็บรายละเอียดการทำงานของฟังก์ชัน เช่น ตอนนี้ทำงานถึงบรรทัดไหนแล้ว ตัวแปรมีค่าอะไรบ้าง `this` ชี้ไปที่ไหน (ในตัวอย่างนี้ยังไม่ใช้) และรายละเอียดภายในอื่นๆ -One function call has exactly one execution context associated with it. +การเรียกฟังก์ชันหนึ่งครั้ง จะมี execution context หนึ่งตัวผูกอยู่เสมอ -When a function makes a nested call, the following happens: +เมื่อฟังก์ชันเรียกฟังก์ชันซ้อน จะเกิดขั้นตอนดังนี้: -- The current function is paused. -- The execution context associated with it is remembered in a special data structure called *execution context stack*. -- The nested call executes. -- After it ends, the old execution context is retrieved from the stack, and the outer function is resumed from where it stopped. +- ฟังก์ชันปัจจุบันถูกพักไว้ก่อน +- Execution context ของฟังก์ชันนั้นจะถูกเก็บไว้ในโครงสร้างข้อมูลพิเศษที่เรียกว่า *execution context stack* +- ฟังก์ชันซ้อนเริ่มทำงาน +- เมื่อทำงานเสร็จ execution context เดิมจะถูกดึงกลับมาจากสแต็ก แล้วฟังก์ชันเดิมก็ทำงานต่อจากจุดที่ค้างไว้ -Let's see what happens during the `pow(2, 3)` call. +มาดูกันว่าเกิดอะไรขึ้นระหว่างการเรียก `pow(2, 3)` ### pow(2, 3) -In the beginning of the call `pow(2, 3)` the execution context will store variables: `x = 2, n = 3`, the execution flow is at line `1` of the function. +ตอนเริ่มเรียก `pow(2, 3)` execution context จะเก็บตัวแปร `x = 2, n = 3` และการทำงานอยู่ที่บรรทัด `1` ของฟังก์ชัน -We can sketch it as: +วาดออกมาเป็นแผนภาพได้แบบนี้:
  • @@ -132,7 +132,7 @@ We can sketch it as:
-That's when the function starts to execute. The condition `n == 1` is falsy, so the flow continues into the second branch of `if`: +นี่คือจุดที่ฟังก์ชันเริ่มทำงาน เงื่อนไข `n == 1` เป็นเท็จ จึงเข้าสู่สาขาที่สองของ `if`: ```js run function pow(x, n) { @@ -149,7 +149,7 @@ alert( pow(2, 3) ); ``` -The variables are same, but the line changes, so the context is now: +ตัวแปรยังเหมือนเดิม แต่บรรทัดเปลี่ยนไป ตอนนี้ context เป็นแบบนี้:
  • @@ -158,19 +158,19 @@ The variables are same, but the line changes, so the context is now:
-To calculate `x * pow(x, n - 1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`. +ในการคำนวณ `x * pow(x, n - 1)` เราต้องเรียก `pow` ซ้อนด้วยอาร์กิวเมนต์ใหม่ `pow(2, 2)` ### pow(2, 2) -To do a nested call, JavaScript remembers the current execution context in the *execution context stack*. +ก่อนเรียกฟังก์ชันซ้อน JavaScript จะจำ execution context ปัจจุบันไว้ใน *execution context stack* -Here we call the same function `pow`, but it absolutely doesn't matter. The process is the same for all functions: +ตรงนี้เราเรียกฟังก์ชัน `pow` ตัวเดิม แต่ไม่ต่างจากการเรียกฟังก์ชันอื่นเลย ขั้นตอนเหมือนกันหมดสำหรับทุกฟังก์ชัน: -1. The current context is "remembered" on top of the stack. -2. The new context is created for the subcall. -3. When the subcall is finished -- the previous context is popped from the stack, and its execution continues. +1. Context ปัจจุบันถูก "จดจำ" ไว้บนสุดของสแต็ก +2. สร้าง context ใหม่สำหรับการเรียกซ้อน +3. เมื่อการเรียกซ้อนเสร็จ -- context เดิมจะถูกดึงออกจากสแต็ก แล้วทำงานต่อ -Here's the context stack when we entered the subcall `pow(2, 2)`: +นี่คือ context stack เมื่อเข้าสู่การเรียก `pow(2, 2)`:
  • @@ -183,21 +183,21 @@ Here's the context stack when we entered the subcall `pow(2, 2)`:
-The new current execution context is on top (and bold), and previous remembered contexts are below. +Execution context ปัจจุบัน (ตัวใหม่) อยู่บนสุด (ตัวหนา) ส่วน context ที่เก็บไว้ก่อนหน้าอยู่ข้างล่าง -When we finish the subcall -- it is easy to resume the previous context, because it keeps both variables and the exact place of the code where it stopped. +เมื่อการเรียกซ้อนเสร็จ เราสามารถกลับไปทำงานต่อใน context เดิมได้ง่ายๆ เพราะ context เก็บทั้งตัวแปรและตำแหน่งที่ค้างไว้ ```smart -Here in the picture we use the word "line", as in our example there's only one subcall in line, but generally a single line of code may contain multiple subcalls, like `pow(…) + pow(…) + somethingElse(…)`. +ในภาพเราใช้คำว่า "บรรทัด" (line) เพราะตัวอย่างของเรามีการเรียกซ้อนแค่ครั้งเดียวต่อบรรทัด แต่โดยทั่วไปบรรทัดเดียวอาจมีการเรียกซ้อนหลายครั้ง เช่น `pow(…) + pow(…) + somethingElse(…)` -So it would be more precise to say that the execution resumes "immediately after the subcall". +พูดให้ถูกต้องกว่าก็คือ การทำงานจะกลับมาทำต่อ "ทันทีหลังจากการเรียกซ้อนเสร็จ" ``` ### pow(2, 1) -The process repeats: a new subcall is made at line `5`, now with arguments `x=2`, `n=1`. +กระบวนการเดิมซ้ำอีกครั้ง: เรียกซ้อนที่บรรทัด `5` ด้วยอาร์กิวเมนต์ `x=2`, `n=1` -A new execution context is created, the previous one is pushed on top of the stack: +สร้าง execution context ใหม่ ส่วนตัวก่อนหน้าถูกดันไปไว้บนสแต็ก:
  • @@ -214,11 +214,11 @@ A new execution context is created, the previous one is pushed on top of the sta
-There are 2 old contexts now and 1 currently running for `pow(2, 1)`. +ตอนนี้มี context เก่าอยู่ 2 ตัว และอีก 1 ตัวที่กำลังทำงานอยู่คือ `pow(2, 1)` -### The exit +### ขาออก (The exit) -During the execution of `pow(2, 1)`, unlike before, the condition `n == 1` is truthy, so the first branch of `if` works: +ระหว่างที่ `pow(2, 1)` ทำงาน คราวนี้ต่างจากก่อนหน้า เงื่อนไข `n == 1` เป็นจริง จึงเข้าสู่สาขาแรกของ `if`: ```js function pow(x, n) { @@ -232,9 +232,9 @@ function pow(x, n) { } ``` -There are no more nested calls, so the function finishes, returning `2`. +ไม่มีการเรียกซ้อนอีกแล้ว ฟังก์ชันจึงทำงานเสร็จและคืนค่า `2` -As the function finishes, its execution context is not needed anymore, so it's removed from the memory. The previous one is restored off the top of the stack: +เมื่อฟังก์ชันทำงานเสร็จ execution context ของมันก็ไม่จำเป็นอีกต่อไป จึงถูกลบออกจากหน่วยความจำ แล้ว context ก่อนหน้าจะถูกดึงกลับมาจากสแต็ก:
    @@ -248,9 +248,9 @@ As the function finishes, its execution context is not needed anymore, so it's r
-The execution of `pow(2, 2)` is resumed. It has the result of the subcall `pow(2, 1)`, so it also can finish the evaluation of `x * pow(x, n - 1)`, returning `4`. +`pow(2, 2)` กลับมาทำงานต่อ ตอนนี้ได้ผลลัพธ์จากการเรียก `pow(2, 1)` แล้ว จึงคำนวณ `x * pow(x, n - 1)` ได้ ซึ่งคืนค่า `4` -Then the previous context is restored: +จากนั้น context ก่อนหน้าถูกดึงกลับมา:
  • @@ -259,15 +259,15 @@ Then the previous context is restored:
-When it finishes, we have a result of `pow(2, 3) = 8`. +เมื่อทำงานเสร็จ เราได้ผลลัพธ์ `pow(2, 3) = 8` -The recursion depth in this case was: **3**. +ความลึกของการเรียกซ้ำในกรณีนี้คือ **3** -As we can see from the illustrations above, recursion depth equals the maximal number of context in the stack. +จากภาพประกอบข้างต้นจะเห็นว่า ความลึกของการเรียกซ้ำเท่ากับจำนวน context สูงสุดในสแต็ก -Note the memory requirements. Contexts take memory. In our case, raising to the power of `n` actually requires the memory for `n` contexts, for all lower values of `n`. +สังเกตเรื่องการใช้หน่วยความจำด้วย แต่ละ context ต้องใช้หน่วยความจำ ในกรณีของเรา การยกกำลัง `n` ต้องใช้หน่วยความจำสำหรับ `n` context ครบทุกค่าของ `n` ที่ลดลงมา -A loop-based algorithm is more memory-saving: +อัลกอริทึมแบบใช้ลูปประหยัดหน่วยความจำกว่า: ```js function pow(x, n) { @@ -281,19 +281,19 @@ function pow(x, n) { } ``` -The iterative `pow` uses a single context changing `i` and `result` in the process. Its memory requirements are small, fixed and do not depend on `n`. +`pow` แบบใช้ลูปใช้ context เดียว โดยเปลี่ยนค่า `i` กับ `result` ไปเรื่อยๆ ใช้หน่วยความจำน้อย คงที่ และไม่ขึ้นกับค่า `n` -**Any recursion can be rewritten as a loop. The loop variant usually can be made more effective.** +**การเรียกซ้ำทุกแบบสามารถเขียนใหม่เป็นลูปได้ และแบบลูปมักจะมีประสิทธิภาพดีกว่า** -...But sometimes the rewrite is non-trivial, especially when a function uses different recursive subcalls depending on conditions and merges their results or when the branching is more intricate. And the optimization may be unneeded and totally not worth the efforts. +...แต่บางทีการเขียนใหม่เป็นลูปก็ไม่ง่าย โดยเฉพาะเมื่อฟังก์ชันเรียกซ้ำหลายทางตามเงื่อนไขต่างๆ แล้วรวมผลลัพธ์กัน หรือเมื่อการแตกกิ่งซับซ้อน อีกทั้งการปรับแต่งประสิทธิภาพอาจไม่จำเป็นและไม่คุ้มค่ากับความพยายาม -Recursion can give a shorter code, easier to understand and support. Optimizations are not required in every place, mostly we need a good code, that's why it's used. +การเรียกซ้ำให้โค้ดที่สั้นกว่า เข้าใจง่ายกว่า และดูแลง่ายกว่า ไม่จำเป็นต้องปรับแต่งประสิทธิภาพทุกจุด สิ่งที่ต้องการจริงๆ คือโค้ดที่ดี นั่นจึงเป็นเหตุผลที่เรานิยมใช้การเรียกซ้ำ -## Recursive traversals +## การท่องข้อมูลแบบเรียกซ้ำ (Recursive traversals) -Another great application of the recursion is a recursive traversal. +การเรียกซ้ำมีประโยชน์อีกอย่างหนึ่งคือการท่องข้อมูลแบบเรียกซ้ำ -Imagine, we have a company. The staff structure can be presented as an object: +สมมติว่าเรามีบริษัทแห่งหนึ่ง โครงสร้างพนักงานสามารถแสดงเป็นออบเจ็กต์ได้แบบนี้: ```js let company = { @@ -322,34 +322,34 @@ let company = { }; ``` -In other words, a company has departments. +พูดง่ายๆ ก็คือ บริษัทมีหลายแผนก -- A department may have an array of staff. For instance, `sales` department has 2 employees: John and Alice. -- Or a department may split into subdepartments, like `development` has two branches: `sites` and `internals`. Each of them has their own staff. -- It is also possible that when a subdepartment grows, it divides into subsubdepartments (or teams). +- แผนกหนึ่งอาจมีอาร์เรย์ของพนักงาน เช่น แผนก `sales` มีพนักงาน 2 คน คือ John กับ Alice +- หรือแผนกหนึ่งอาจแบ่งออกเป็นแผนกย่อย เช่น `development` มี 2 สาขาคือ `sites` กับ `internals` แต่ละสาขาก็มีพนักงานของตัวเอง +- นอกจากนี้ เมื่อแผนกย่อยเติบโตขึ้น ก็อาจแบ่งออกเป็นแผนกย่อยลงไปอีก (หรือเป็นทีม) - For instance, the `sites` department in the future may be split into teams for `siteA` and `siteB`. And they, potentially, can split even more. That's not on the picture, just something to have in mind. + ยกตัวอย่าง แผนก `sites` ในอนาคตอาจแยกเป็นทีม `siteA` กับ `siteB` แล้วก็อาจแยกออกไปอีก ตรงนี้ยังไม่ได้อยู่ในภาพ แค่ให้จินตนาการไว้ -Now let's say we want a function to get the sum of all salaries. How can we do that? +ทีนี้ สมมติเราต้องการฟังก์ชันที่รวมเงินเดือนทั้งหมด จะทำยังไงดี? -An iterative approach is not easy, because the structure is not simple. The first idea may be to make a `for` loop over `company` with nested subloop over 1st level departments. But then we need more nested subloops to iterate over the staff in 2nd level departments like `sites`... And then another subloop inside those for 3rd level departments that might appear in the future? If we put 3-4 nested subloops in the code to traverse a single object, it becomes rather ugly. +ถ้าใช้วิธีวนลูปจะไม่ง่ายเลย เพราะโครงสร้างซับซ้อน ความคิดแรกอาจเป็นการใช้ `for` ลูปวนผ่าน `company` แล้วซ้อนลูปย่อยสำหรับแผนกระดับที่ 1 แต่แล้วก็ต้องซ้อนลูปอีกเพื่อวนพนักงานในแผนกระดับที่ 2 อย่าง `sites`... แล้วก็ต้องซ้อนอีกสำหรับแผนกระดับที่ 3 ที่อาจเกิดขึ้นในอนาคต? ถ้าซ้อนลูป 3-4 ชั้นเพื่อท่องข้อมูลในออบเจ็กต์เดียว โค้ดจะดูรกมาก -Let's try recursion. +ลองใช้การเรียกซ้ำดีกว่า -As we can see, when our function gets a department to sum, there are two possible cases: +จะเห็นว่าเมื่อฟังก์ชันรับแผนกมาเพื่อรวมเงินเดือน จะมีสองกรณี: -1. Either it's a "simple" department with an *array* of people -- then we can sum the salaries in a simple loop. -2. Or it's *an object* with `N` subdepartments -- then we can make `N` recursive calls to get the sum for each of the subdeps and combine the results. +1. เป็นแผนก "ธรรมดา" ที่มี *อาร์เรย์* ของพนักงาน -- ก็รวมเงินเดือนด้วยลูปง่ายๆ ได้เลย +2. เป็น *ออบเจ็กต์* ที่มีแผนกย่อย `N` แผนก -- ก็เรียกซ้ำ `N` ครั้งเพื่อรวมเงินเดือนของแต่ละแผนกย่อย แล้วนำผลลัพธ์มารวมกัน -The 1st case is the base of recursion, the trivial case, when we get an array. +กรณีที่ 1 คือฐานของการเรียกซ้ำ เป็นกรณีง่ายๆ เมื่อได้รับอาร์เรย์ -The 2nd case when we get an object is the recursive step. A complex task is split into subtasks for smaller departments. They may in turn split again, but sooner or later the split will finish at (1). +กรณีที่ 2 ที่ได้รับออบเจ็กต์คือขั้นตอนการเรียกซ้ำ โจทย์ที่ซับซ้อนจะถูกแตกออกเป็นโจทย์ย่อยตามแผนกต่างๆ ซึ่งอาจแตกย่อยออกไปอีก แต่สุดท้ายก็จะจบที่กรณีที่ (1) -The algorithm is probably even easier to read from the code: +อัลกอริทึมนี้อ่านจากโค้ดน่าจะเข้าใจง่ายกว่า: ```js run -let company = { // the same object, compressed for brevity +let company = { // ออบเจ็กต์เดิม ย่อให้กระชับ sales: [{name: 'John', salary: 1000}, {name: 'Alice', salary: 1600 }], development: { sites: [{name: 'Peter', salary: 2000}, {name: 'Alex', salary: 1800 }], @@ -357,15 +357,15 @@ let company = { // the same object, compressed for brevity } }; -// The function to do the job +// ฟังก์ชันทำงาน *!* function sumSalaries(department) { - if (Array.isArray(department)) { // case (1) - return department.reduce((prev, current) => prev + current.salary, 0); // sum the array - } else { // case (2) + if (Array.isArray(department)) { // กรณีที่ (1) + return department.reduce((prev, current) => prev + current.salary, 0); // รวมค่าในอาร์เรย์ + } else { // กรณีที่ (2) let sum = 0; for (let subdep of Object.values(department)) { - sum += sumSalaries(subdep); // recursively call for subdepartments, sum the results + sum += sumSalaries(subdep); // เรียกซ้ำสำหรับแผนกย่อย แล้วรวมผลลัพธ์ } return sum; } @@ -375,62 +375,62 @@ function sumSalaries(department) { alert(sumSalaries(company)); // 7700 ``` -The code is short and easy to understand (hopefully?). That's the power of recursion. It also works for any level of subdepartment nesting. +โค้ดสั้นและเข้าใจง่ายใช่ไหม? นี่แหละพลังของการเรียกซ้ำ โค้ดนี้ใช้ได้กับแผนกย่อยกี่ระดับก็ได้ -Here's the diagram of calls: +นี่คือแผนภาพการเรียก: ![recursive salaries](recursive-salaries.svg) -We can easily see the principle: for an object `{...}` subcalls are made, while arrays `[...]` are the "leaves" of the recursion tree, they give immediate result. +หลักการเห็นได้ชัดเลย: เมื่อเจอออบเจ็กต์ `{...}` ก็เรียกซ้ำต่อ ส่วนอาร์เรย์ `[...]` คือ "ใบ" ของต้นไม้การเรียกซ้ำ ซึ่งให้ผลลัพธ์ทันที -Note that the code uses smart features that we've covered before: +สังเกตว่าโค้ดนี้ใช้ฟีเจอร์ที่เราเรียนมาแล้ว: -- Method `arr.reduce` explained in the chapter to get the sum of the array. -- Loop `for(val of Object.values(obj))` to iterate over object values: `Object.values` returns an array of them. +- เมธอด `arr.reduce` ที่อธิบายไว้ในบท สำหรับรวมค่าในอาร์เรย์ +- ลูป `for(val of Object.values(obj))` สำหรับวนผ่านค่าของออบเจ็กต์ โดย `Object.values` คืนค่าเป็นอาร์เรย์ -## Recursive structures +## โครงสร้างข้อมูลแบบเรียกซ้ำ (Recursive structures) -A recursive (recursively-defined) data structure is a structure that replicates itself in parts. +โครงสร้างข้อมูลแบบเรียกซ้ำ คือโครงสร้างที่นิยามตัวเองซ้ำในส่วนย่อย -We've just seen it in the example of a company structure above. +เราเพิ่งเห็นตัวอย่างจากโครงสร้างบริษัทข้างต้น -A company *department* is: -- Either an array of people. -- Or an object with *departments*. +*แผนก* ของบริษัทอาจเป็น: +- อาร์เรย์ของพนักงาน +- หรือออบเจ็กต์ที่มี *แผนกย่อย* อยู่ภายใน -For web-developers there are much better-known examples: HTML and XML documents. +สำหรับเว็บเดเวลอปเปอร์ มีตัวอย่างที่คุ้นเคยกว่านี้อีก นั่นคือเอกสาร HTML และ XML -In the HTML document, an *HTML-tag* may contain a list of: -- Text pieces. -- HTML-comments. -- Other *HTML-tags* (that in turn may contain text pieces/comments or other tags etc). +ในเอกสาร HTML *แท็ก HTML* หนึ่งตัวอาจมี: +- ข้อความ +- คอมเมนต์ HTML +- *แท็ก HTML* ตัวอื่นๆ (ซึ่งก็อาจมีข้อความ/คอมเมนต์/แท็กอื่นซ้อนลงไปอีก) -That's once again a recursive definition. +นี่ก็เป็นนิยามแบบเรียกซ้ำเช่นกัน -For better understanding, we'll cover one more recursive structure named "Linked list" that might be a better alternative for arrays in some cases. +เพื่อให้เข้าใจมากขึ้น เราจะมาดูโครงสร้างแบบเรียกซ้ำอีกตัวหนึ่งชื่อว่า "Linked list" ซึ่งบางกรณีอาจเป็นทางเลือกที่ดีกว่าอาร์เรย์ ### Linked list -Imagine, we want to store an ordered list of objects. +สมมติเราต้องการเก็บรายการออบเจ็กต์ที่มีลำดับ -The natural choice would be an array: +ทางเลือกที่นึกถึงก่อนก็คืออาร์เรย์: ```js let arr = [obj1, obj2, obj3]; ``` -...But there's a problem with arrays. The "delete element" and "insert element" operations are expensive. For instance, `arr.unshift(obj)` operation has to renumber all elements to make room for a new `obj`, and if the array is big, it takes time. Same with `arr.shift()`. +...แต่อาร์เรย์มีปัญหาอยู่อย่างหนึ่ง การ "ลบสมาชิก" และ "แทรกสมาชิก" นั้นค่อนข้างช้า เช่น `arr.unshift(obj)` ต้องขยับหมายเลขของสมาชิกทุกตัวเพื่อเปิดที่ว่างให้ `obj` ใหม่ ถ้าอาร์เรย์ใหญ่ก็กินเวลามาก `arr.shift()` ก็เช่นกัน -The only structural modifications that do not require mass-renumbering are those that operate with the end of array: `arr.push/pop`. So an array can be quite slow for big queues, when we have to work with the beginning. +การเปลี่ยนแปลงโครงสร้างที่ไม่ต้องขยับหมายเลขทั้งหมดมีแค่ตัวที่ทำงานกับท้ายอาร์เรย์ คือ `arr.push/pop` ดังนั้น อาร์เรย์อาจช้ามากสำหรับคิวขนาดใหญ่ที่ต้องทำงานกับด้านหน้าบ่อยๆ -Alternatively, if we really need fast insertion/deletion, we can choose another data structure called a [linked list](https://en.wikipedia.org/wiki/Linked_list). +ถ้าต้องการแทรก/ลบที่รวดเร็วจริงๆ เราเลือกใช้โครงสร้างข้อมูลอีกแบบที่เรียกว่า [linked list](https://en.wikipedia.org/wiki/Linked_list) -The *linked list element* is recursively defined as an object with: -- `value`. -- `next` property referencing the next *linked list element* or `null` if that's the end. +*สมาชิกของ linked list* ถูกนิยามแบบเรียกซ้ำเป็นออบเจ็กต์ที่มี: +- `value` — ค่าที่เก็บไว้ +- พร็อพเพอร์ตี้ `next` ที่ชี้ไปยัง *สมาชิกตัวถัดไป* ใน linked list หรือ `null` ถ้าเป็นตัวสุดท้าย -For instance: +ตัวอย่าง: ```js let list = { @@ -448,11 +448,11 @@ let list = { }; ``` -Graphical representation of the list: +แสดงเป็นภาพได้แบบนี้: ![linked list](linked-list.svg) -An alternative code for creation: +อีกวิธีหนึ่งในการสร้าง: ```js no-beautify let list = { value: 1 }; @@ -462,9 +462,9 @@ list.next.next.next = { value: 4 }; list.next.next.next.next = null; ``` -Here we can even more clearly see that there are multiple objects, each one has the `value` and `next` pointing to the neighbour. The `list` variable is the first object in the chain, so following `next` pointers from it we can reach any element. +แบบนี้เห็นชัดเจนขึ้นว่ามีออบเจ็กต์หลายตัว แต่ละตัวมี `value` และ `next` ชี้ไปหาตัวถัดไป ตัวแปร `list` คือออบเจ็กต์แรกในสาย เราสามารถตาม `next` ไปเรื่อยๆ จนถึงสมาชิกตัวไหนก็ได้ -The list can be easily split into multiple parts and later joined back: +Linked list แบ่งออกเป็นหลายส่วนได้ง่าย แล้วเชื่อมกลับทีหลังก็ได้: ```js let secondList = list.next.next; @@ -473,15 +473,15 @@ list.next.next = null; ![linked list split](linked-list-split.svg) -To join: +เชื่อมกลับ: ```js list.next.next = secondList; ``` -And surely we can insert or remove items in any place. +แน่นอนว่าเราแทรกหรือลบสมาชิกตรงไหนก็ได้ -For instance, to prepend a new value, we need to update the head of the list: +เช่น ถ้าจะเพิ่มค่าใหม่ไว้ข้างหน้า ก็แค่อัปเดตหัวของ list: ```js let list = { value: 1 }; @@ -490,14 +490,14 @@ list.next.next = { value: 3 }; list.next.next.next = { value: 4 }; *!* -// prepend the new value to the list +// เพิ่มค่าใหม่ไว้ข้างหน้า list list = { value: "new item", next: list }; */!* ``` ![linked list](linked-list-0.svg) -To remove a value from the middle, change `next` of the previous one: +ถ้าจะลบค่าตรงกลาง ก็เปลี่ยน `next` ของตัวก่อนหน้า: ```js list.next = list.next.next; @@ -505,38 +505,38 @@ list.next = list.next.next; ![linked list](linked-list-remove-1.svg) -We made `list.next` jump over `1` to value `2`. The value `1` is now excluded from the chain. If it's not stored anywhere else, it will be automatically removed from the memory. +เราทำให้ `list.next` ข้ามจากค่า `1` ไปที่ค่า `2` ค่า `1` ถูกตัดออกจากสายแล้ว ถ้าไม่มีที่ไหนอ้างอิงถึงอีก ก็จะถูกลบออกจากหน่วยความจำโดยอัตโนมัติ -Unlike arrays, there's no mass-renumbering, we can easily rearrange elements. +ต่างจากอาร์เรย์ตรงที่ไม่ต้องขยับหมายเลขทั้งหมด เราจัดเรียงสมาชิกใหม่ได้ง่ายๆ -Naturally, lists are not always better than arrays. Otherwise everyone would use only lists. +แน่นอนว่า linked list ไม่ได้ดีกว่าอาร์เรย์เสมอไป ไม่อย่างนั้นทุกคนคงใช้แต่ list กันแล้ว -The main drawback is that we can't easily access an element by its number. In an array that's easy: `arr[n]` is a direct reference. But in the list we need to start from the first item and go `next` `N` times to get the Nth element. +ข้อเสียหลักคือเราเข้าถึงสมาชิกตามหมายเลขลำดับไม่ได้ง่ายๆ ถ้าเป็นอาร์เรย์ก็แค่ `arr[n]` ได้เลย แต่ใน linked list ต้องเริ่มจากตัวแรกแล้วตาม `next` ไป `N` ครั้งถึงจะได้สมาชิกตัวที่ N -...But we don't always need such operations. For instance, when we need a queue or even a [deque](https://en.wikipedia.org/wiki/Double-ended_queue) -- the ordered structure that must allow very fast adding/removing elements from both ends, but access to its middle is not needed. +...แต่ไม่ใช่ว่าเราต้องการแบบนั้นเสมอไป เช่น เมื่อต้องการคิว (queue) หรือ [deque](https://en.wikipedia.org/wiki/Double-ended_queue) ซึ่งเป็นโครงสร้างที่ต้องเพิ่ม/ลบสมาชิกจากทั้งสองด้านได้เร็วมาก แต่ไม่ต้องเข้าถึงตรงกลาง -Lists can be enhanced: -- We can add property `prev` in addition to `next` to reference the previous element, to move back easily. -- We can also add a variable named `tail` referencing the last element of the list (and update it when adding/removing elements from the end). -- ...The data structure may vary according to our needs. +Linked list ยังปรับปรุงเพิ่มเติมได้อีก: +- เพิ่มพร็อพเพอร์ตี้ `prev` ควบคู่กับ `next` เพื่อชี้ไปยังสมาชิกก่อนหน้า ให้ย้อนกลับได้ง่าย +- เพิ่มตัวแปรชื่อ `tail` ที่ชี้ไปยังสมาชิกตัวสุดท้ายของ list (และอัปเดตเมื่อเพิ่ม/ลบสมาชิกจากท้าย list) +- ...โครงสร้างข้อมูลปรับเปลี่ยนได้ตามความต้องการ -## Summary +## สรุป -Terms: -- *Recursion* is a programming term that means calling a function from itself. Recursive functions can be used to solve tasks in elegant ways. +คำศัพท์: +- *การเรียกซ้ำ (Recursion)* คือศัพท์ทางโปรแกรมมิ่งที่หมายถึงฟังก์ชันเรียกตัวเอง ฟังก์ชันแบบเรียกซ้ำใช้แก้โจทย์ได้อย่างกระชับ - When a function calls itself, that's called a *recursion step*. The *basis* of recursion is function arguments that make the task so simple that the function does not make further calls. + เมื่อฟังก์ชันเรียกตัวเอง เรียกว่า *ขั้นตอนการเรียกซ้ำ (recursion step)* ส่วน *ฐานของการเรียกซ้ำ (basis)* คือกรณีที่อาร์กิวเมนต์ทำให้โจทย์ง่ายพอจนฟังก์ชันไม่ต้องเรียกต่อ -- A [recursively-defined](https://en.wikipedia.org/wiki/Recursive_data_type) data structure is a data structure that can be defined using itself. +- [โครงสร้างข้อมูลแบบเรียกซ้ำ](https://en.wikipedia.org/wiki/Recursive_data_type) คือโครงสร้างข้อมูลที่นิยามตัวเองได้ - For instance, the linked list can be defined as a data structure consisting of an object referencing a list (or null). + ยกตัวอย่าง linked list สามารถนิยามเป็นโครงสร้างข้อมูลที่ประกอบด้วยออบเจ็กต์ที่อ้างอิงไปยัง list อีกตัว (หรือ null) ```js list = { value, next -> list } ``` - Trees like HTML elements tree or the department tree from this chapter are also naturally recursive: they have branches and every branch can have other branches. + ต้นไม้ (tree) เช่น ต้นไม้ของ HTML elements หรือต้นไม้แผนกในบทนี้ ก็เป็นโครงสร้างแบบเรียกซ้ำโดยธรรมชาติ — แต่ละกิ่งสามารถมีกิ่งย่อยซ้อนลงไปได้อีก - Recursive functions can be used to walk them as we've seen in the `sumSalary` example. + ฟังก์ชันแบบเรียกซ้ำใช้ท่องข้อมูลเหล่านี้ได้ ดังที่เห็นในตัวอย่าง `sumSalary` -Any recursive function can be rewritten into an iterative one. And that's sometimes required to optimize stuff. But for many tasks a recursive solution is fast enough and easier to write and support. +ฟังก์ชันแบบเรียกซ้ำทุกตัวสามารถเขียนใหม่เป็นแบบวนลูปได้ และบางครั้งก็จำเป็นเพื่อปรับแต่งประสิทธิภาพ แต่ในหลายๆ โจทย์ วิธีเรียกซ้ำเร็วพอ เขียนง่ายกว่า และดูแลง่ายกว่า diff --git a/1-js/06-advanced-functions/02-rest-parameters-spread/article.md b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md index 527ada062..375c68d4d 100644 --- a/1-js/06-advanced-functions/02-rest-parameters-spread/article.md +++ b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md @@ -1,20 +1,20 @@ -# ทำความรู้จัก Rest parameters และ spread syntax +# Rest parameters และ spread syntax -ในภาษา JavaScript จะมี built-in ฟังก์ชั่นที่ช่วยทำงานให้เราต่าง ๆ มากมาย +ฟังก์ชัน built-in หลายตัวของ JavaScript รองรับอาร์กิวเมนต์จำนวนไม่จำกัด -ยกตัวอย่าง: +ยกตัวอย่างเช่น: -- `Math.max(arg1, arg2, ..., argN)` -- หาจำนวนที่มีค่ามากที่สุดในกลุ่ม. -- `Object.assign(dest, src1, ..., srcN)` -- ก๊อบปี้ค่าของ `src1..N` ไปที่ `dest`. -- และอื่น ๆ อีกมากมาย +- `Math.max(arg1, arg2, ..., argN)` -- คืนค่าจำนวนที่มากที่สุดจากอาร์กิวเมนต์ทั้งหมด +- `Object.assign(dest, src1, ..., srcN)` -- คัดลอกพร็อพเพอร์ตี้จาก `src1..N` ไปยัง `dest` +- ...และอื่น ๆ อีกมากมาย -ในบทนี้ เราจะมาเรียนรู้ built-in ฟังก์ชั่นอีกรูปแบบนึง ได้แก่ Rest parameters และ spread syntax +ในบทนี้ เราจะมาเรียนรู้วิธีการแบบเดียวกัน รวมถึงวิธีส่งอาร์เรย์เข้าไปเป็นพารามิเตอร์ของฟังก์ชัน ## Rest parameters `...` -ฟังก์ชั่นที่สามารถรับ arguments กี่ตัวก็ได้ ไม่ว่าจะตั้งค่าแบบไหน +เราเรียกฟังก์ชันโดยส่งอาร์กิวเมนต์กี่ตัวก็ได้ ไม่ว่าฟังก์ชันนั้นจะประกาศพารามิเตอร์ไว้กี่ตัวก็ตาม -ตัวอย่าง: +ลองดูตัวอย่าง: ```js run function sum(a, b) { return a + b; @@ -23,11 +23,11 @@ function sum(a, b) { alert( sum(1, 2, 3, 4, 5) ); ``` -ในโค้ดตัวอย่างนี้ จะไม่เกิด error จากการที่มีตัวแปรที่มากไป แต่ ฟังก์ชั่น sum จะถูกคำนวนจาก arguments แค่ 2 ตัวแรกเท่านั้น +โค้ดนี้จะไม่เกิด error แม้จะส่งอาร์กิวเมนต์มากเกินไป แต่ผลลัพธ์จะคำนวณจากอาร์กิวเมนต์ 2 ตัวแรกเท่านั้น -parameters ที่เหลือสามารถเขียนเพิ่มในฟังก์ชั่นได้ผ่านการใช้จุดสามจุด `...` แล้วตามด้วยชื่อของ array นั้น โดยจุดหมายถึง การรวม parameters ที่เหลือใน array ด้วย +เราสามารถรวมพารามิเตอร์ที่เหลือเข้าด้วยกันได้ โดยใช้จุดสามจุด `...` ตามด้วยชื่อของอาร์เรย์ที่จะรองรับค่าเหล่านั้น จุดสามจุดนี้หมายความว่า "รวมพารามิเตอร์ที่เหลือทั้งหมดเข้าไปในอาร์เรย์" -ยกตัวอย่าง การรวม arguments ทั้งหมดใน array `args`: +ยกตัวอย่าง การรวมอาร์กิวเมนต์ทั้งหมดเข้าในอาร์เรย์ `args`: ```js run function sumAll(...args) { // args คือชื่อของ array @@ -43,16 +43,16 @@ alert( sumAll(1, 2) ); // 3 alert( sumAll(1, 2, 3) ); // 6 ``` -เราสามารถใช้ parameters ตัวแรก ๆ เป็นตัวแปร และรวม parameter ที่เหลือใน Rest parameters ได้ +เราเลือกให้พารามิเตอร์ตัวแรก ๆ เป็นตัวแปรแยก แล้วรวมเฉพาะส่วนที่เหลือไว้ใน rest parameters ก็ได้ -ตัวอย่างการให้ 2 parameter แรกเป็นตัวแปร และรวม parameter ที่เหลือใน array ที่ชื่อว่า `titles` +ตัวอย่างนี้ให้ 2 ตัวแรกเป็นตัวแปร แล้วที่เหลือรวมไว้ในอาร์เรย์ `titles` ```js run function showName(firstName, lastName, ...titles) { alert( firstName + ' ' + lastName ); // Julius Caesar - // parameter ที่เหลือจะถูกรวมใน titles array - // ตัวอย่าง titles = ["Consul", "Imperator"] + // พารามิเตอร์ที่เหลือจะรวมอยู่ในอาร์เรย์ titles + // เช่น titles = ["Consul", "Imperator"] alert( titles[0] ); // Consul alert( titles[1] ); // Imperator alert( titles.length ); // 2 @@ -61,8 +61,8 @@ function showName(firstName, lastName, ...titles) { showName("Julius", "Caesar", "Consul", "Imperator"); ``` -````warn header="The rest parameters ต้องอยู่ท้ายสุด" -เพราะ Rest parameters จะรวม arguments ที่เหลืออยู่ทั้งหมด ดังนั้นการเขียนแบบในตัวอย่างด้านล่างจะไม่สมเหตุสมผลและ ก่อให้เกิด error: +````warn header="Rest parameters ต้องอยู่ท้ายสุดเสมอ" +เนื่องจาก rest parameters จะรวมอาร์กิวเมนต์ที่เหลือทั้งหมด การเขียนแบบนี้จึงไม่สมเหตุสมผลและทำให้เกิด error: ```js function f(arg1, ...rest, arg2) { // arg2 หลังจาก ...rest ?! @@ -75,9 +75,9 @@ function f(arg1, ...rest, arg2) { // arg2 หลังจาก ...rest ?! ## ตัวแปร "arguments" -มี object นึงที่ทำหน้าที่เหมือน array เรียกว่า `arguments` ที่ภายในจะมี argument เรียงตาม index +นอกจากนี้ยังมีออบเจ็กต์พิเศษที่มีลักษณะคล้ายอาร์เรย์ ชื่อว่า `arguments` ซึ่งเก็บอาร์กิวเมนต์ทั้งหมดเรียงตาม index -ยกตัวอย่าง +ลองดูตัวอย่าง ```js run function showName() { @@ -85,27 +85,27 @@ function showName() { alert( arguments[0] ); alert( arguments[1] ); - // เราสามารถวนซ้ำกับมันได้ + // วนลูปได้เช่นกัน // for(let arg of arguments) alert(arg); } // แสดงผลเป็น: 2, Julius, Caesar showName("Julius", "Caesar"); -// แสดงผลเป็น: 1, Ilya, undefined (no second argument) +// แสดงผลเป็น: 1, Ilya, undefined (ไม่มีอาร์กิวเมนต์ตัวที่สอง) showName("Ilya"); ``` -ในสมัยก่อนที่จะมี rest parameters การใช้ `arguments` เป็นวิธีที่จะรวมทุก ๆ arguments ของฟังก์ชั่นนั้น ๆ ซึ่งคุณอาจจะเห็นได้จากในโค้ดเก่า ๆ +สมัยก่อนที่จะมี rest parameters การใช้ `arguments` เป็นวิธีเดียวที่จะเข้าถึงอาร์กิวเมนต์ทั้งหมดของฟังก์ชัน ซึ่งยังใช้งานได้อยู่ และอาจเจอได้ในโค้ดเก่า ๆ -แต่ข้อเสียของ `arguments` คือมันมีความเป็น array และยังสามารถวนซ้ำกับมันเพื่ออ่านค่า argument แต่ละตัวได้ แต่มันก็ไม่ใช่ array เพราะมันไม่สามารถเรียกใช้ array methods ได้ เช่นเราไม่สามารถใช้ `arguments.map(...)` ได้ +แต่ข้อเสียของ `arguments` คือ ถึงจะมีลักษณะคล้ายอาร์เรย์และวนลูปได้ แต่จริง ๆ แล้วไม่ใช่อาร์เรย์ จึงใช้เมธอดของอาร์เรย์ไม่ได้ เช่น `arguments.map(...)` จะใช้ไม่ได้ -แม้มันจะมีทุก ๆ arguments แต่เราไม่สามารถเรียกใช้บางส่วนแบบที่ rest parameters ทำได้ +นอกจากนี้ `arguments` จะเก็บอาร์กิวเมนต์ทั้งหมดเสมอ เราไม่สามารถเลือกรับเฉพาะบางส่วนได้แบบที่ rest parameters ทำได้ -ดังนั้นเมื่อเราจะใช้ฟีเจอร์นี้ rest parameters จึงเหมาะสมกว่า +ดังนั้นถ้าต้องการความยืดหยุ่น rest parameters จึงเหมาะกว่า -````smart header="Arrow functions จะไม่มี `\"arguments\"`" -ถ้าเราจะเรียกใช้ object `arguments` จาก arrow function มันจะได้ผลลัพธ์เหมือนฟังก์ชั่นปกติทั่วไป +````smart header="Arrow function ไม่มี `\"arguments\"`" +ถ้าเราเข้าถึงออบเจ็กต์ `arguments` จากภายใน arrow function จะได้ค่า `arguments` ของฟังก์ชันภายนอกที่ครอบอยู่แทน ตัวอย่าง: @@ -118,25 +118,25 @@ function f() { f(1); // 1 ``` -ถ้าเราจำได้ arrow functions จะไม่มี `this` เป็นของตัวเอง และตอนนี้เราก็รู้แล้วว่า arrow functions ก็ไม่มี object `arguments` เหมือนกัน +ดังที่เราเคยเรียนมาแล้ว arrow function ไม่มี `this` เป็นของตัวเอง และตอนนี้เราก็รู้แล้วว่าไม่มีออบเจ็กต์ `arguments` เป็นของตัวเองเช่นกัน ```` ## Spread syntax [#spread-syntax] -จากที่เราได้เห็นการดึง array จากกลุ่มของ parameters แล้ว +เราเพิ่งเห็นวิธีรวมกลุ่มพารามิเตอร์เข้าเป็นอาร์เรย์ไปแล้ว -แต่ในบางครั้งเราต้องการทำสิ่งตรงกันข้าม +แต่บางครั้งเราต้องการทำสิ่งตรงกันข้าม -อย่างเช่น built-in ฟังก์ชั่น [Math.max](mdn:js/Math/max) ที่จะหาจำนวนที่มีค่ามากที่สุดในกลุ่ม: +ยกตัวอย่าง ฟังก์ชัน built-in อย่าง [Math.max](mdn:js/Math/max) จะคืนค่าจำนวนที่มากที่สุดจากรายการที่ส่งเข้าไป: ```js run alert( Math.max(3, 5, 1) ); // 5 ``` -เรามี array ที่มีค่าดังนี้ `[3, 5, 1]`. เราจะเรียกใช้ `Math.max` กับมันได้อย่างไร ? +สมมติเรามีอาร์เรย์ `[3, 5, 1]` แล้วจะส่งเข้า `Math.max` ได้อย่างไร? -การเรียกใช้แบบ "as is" คงจะไม่เวิร์ค เพราะ `Math.max` จะรับค่าเฉพาะ arguments ที่เป็นตัวเลข ไม่ใช่ array: +ส่งเข้าไปตรง ๆ ไม่ได้ เพราะ `Math.max` รับอาร์กิวเมนต์เป็นตัวเลขแยกทีละตัว ไม่ใช่อาร์เรย์: ```js run let arr = [3, 5, 1]; @@ -146,21 +146,21 @@ alert( Math.max(arr) ); // NaN */!* ``` -และเราก็ไม่สามารถเรียกใช้ตัวแปรแต่ละตัวจาก array แบบนี้ได้ในโค้ด `Math.max(arr[0], arr[1], arr[2])`, เพราะเราไม่มีทางรู้ได้เลยว่าใน array จะมีตัวแปรกี่ตัว ในการทำงานจริงคงจะไม่ได้มีแค่ตัวสองตัว หรือบางทีอาจจะไม่มีเลย นั่นแหละคือปัญหา +แน่นอนว่าเราเขียน `Math.max(arr[0], arr[1], arr[2])` แบบระบุทีละตัวไม่ได้ เพราะไม่รู้ว่าอาร์เรย์จะมีสมาชิกกี่ตัว อาจมีเยอะมากหรืออาจไม่มีเลยก็ได้ -*Spread syntax* จะเข้ามาช่วยเหลือ โดยหน้าตามันจะเหมือนกับ rest parameters แถมยังใช้ `...` เหมือนกันอีก แต่การทำงานค่อนข้างแตกต่างกัน +*Spread syntax* จะเข้ามาช่วยตรงนี้ หน้าตาเหมือน rest parameters เพราะใช้ `...` เหมือนกัน แต่ทำงานตรงกันข้าม -เมื่อ `...arr` ถูกใช้ในการเรียกฟังก์ชั่นมันจะกระจาย object `arr` ออกมาเป็นกลุ่มของ arguments. +เมื่อใช้ `...arr` ตอนเรียกฟังก์ชัน จะ "กระจาย" iterable ออบเจ็กต์ `arr` ออกเป็นอาร์กิวเมนต์แยกทีละตัว -ตัวอย่างเช่น `Math.max`: +ลองดูตัวอย่างกับ `Math.max`: ```js run let arr = [3, 5, 1]; -alert( Math.max(...arr) ); // 5 (กระจาย array ออกเป็นกลุ่มของ arguments) +alert( Math.max(...arr) ); // 5 (spread กระจายอาร์เรย์ออกเป็นอาร์กิวเมนต์แยกทีละตัว) ``` -นอกจากนี้เรายังสามารถใช้ Spread syntax ได้มากกว่าหนึ่งตัว: +นอกจากนี้ยังส่ง iterable หลายตัวพร้อมกันได้: ```js run let arr1 = [1, -2, 3, 4]; @@ -169,7 +169,7 @@ let arr2 = [8, 3, -8, 1]; alert( Math.max(...arr1, ...arr2) ); // 8 ``` -หรือเราจะผสม Spread syntax กับตัวแปรธรรมดาก็ย่อมได้: +หรือจะผสม spread syntax กับค่าตัวเลขธรรมดาก็ได้: ```js run @@ -179,7 +179,7 @@ let arr2 = [8, 3, -8, 1]; alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25 ``` -และ Spread syntax ยังสามารถใช้เพื่อรวม array 2 อันเข้าด้วยกัน: +Spread syntax ยังใช้รวมอาร์เรย์เข้าด้วยกันได้อีกด้วย: ```js run let arr = [3, 5, 1]; @@ -192,9 +192,9 @@ let merged = [0, ...arr, 2, ...arr2]; alert(merged); // 0,3,5,1,2,8,9,15 (0, ต่อด้วย arr, ต่อด้วย 2, ต่อด้วย arr2) ``` -ในตัวอย่างข้างต้น เราใช้ array เพื่อสาธิตการใช้ spread syntax แต่อะไรก็ตามที่เราสามารถวนซ้ำกับมันได้ก็สามารถทำแบบนี้ได้เช่นกัน +ตัวอย่างข้างต้นใช้อาร์เรย์ แต่จริง ๆ แล้ว spread syntax ใช้ได้กับ iterable ทุกชนิด -ยกตัวอย่าง เราสามารถใช้ spread syntax เพื่อที่จะแปลง string เป็น array of characters: +ยกตัวอย่างเช่น ใช้ spread syntax แปลงสตริงเป็นอาร์เรย์ของตัวอักษร: ```js run let str = "Hello"; @@ -202,94 +202,94 @@ let str = "Hello"; alert( [...str] ); // H,e,l,l,o ``` -หลักการทำงานของ Spread syntax คือการใช้การวนซ้ำรวม elements แบบที่ `for..of` ทำ +Spread syntax ทำงานเบื้องหลังโดยใช้ iterator เหมือนกับ `for..of` -ดังนั้นสำหรับ string, `for..of` จะคืนค่ามาเป็น characters และ `...str` จะคืนค่ามาเป็น `"H","e","l","l","o"`. ในรูปแบบของ array of string `[...str]`. +สำหรับสตริง `for..of` จะคืนค่าเป็นตัวอักษรทีละตัว ดังนั้น `...str` จึงได้ `"H","e","l","l","o"` แล้วพอส่งเข้า `[...str]` ก็จะได้อาร์เรย์ของตัวอักษรออกมา -สำหรับงานบางประเภท เราสามารถใช้ `Array.from`, เพราะมันสามารถแปลงข้อมูลใด ๆ ที่เราสามารถวนซ้ำกับมันได้ (เช่น string) ให้เป็น array ได้: +งานแบบนี้เราใช้ `Array.from` ก็ได้เช่นกัน เพราะมันแปลง iterable (เช่น สตริง) ให้เป็นอาร์เรย์ได้: ```js run let str = "Hello"; -// Array.from แปลงข้อมูลใด ๆ ที่เราสามารถวนซ้ำกับมันได้ให้เป็น array +// Array.from แปลง iterable ให้เป็นอาร์เรย์ alert( Array.from(str) ); // H,e,l,l,o ``` -ผลลัพธ์ที่ได้จะเหมือนกับ `[...str]`. +ผลลัพธ์ที่ได้เหมือนกับ `[...str]` -แต่ทั้งนี้ทั้ง ก็ยังมีความแตกต่างระหว่าง `Array.from(obj)` และ `[...obj]`: +แต่ `Array.from(obj)` กับ `[...obj]` มีความต่างเล็กน้อย: -- `Array.from` สามารถทำงานกับข้อมูลที่เป็น array-likes และข้อมูลที่เราสามารถวนซ้ำกับมันได้ -- Spread syntax ทำงานได้กับแค่ข้อมูลที่เราสามารถวนซ้ำกับมันได้ +- `Array.from` ใช้ได้ทั้งกับ array-like และ iterable +- Spread syntax ใช้ได้กับ iterable เท่านั้น -ดังนั้น สำหรับการเปลี่ยนบางสิ่งให้กลายเป็น array, `Array.from` ดูจะเหมาะสมมากกว่า +ดังนั้นถ้าต้องการแปลงอะไรสักอย่างให้เป็นอาร์เรย์ `Array.from` จึงครอบคลุมกว่า -## การก๊อบปี้ array/object +## การคัดลอก array/object -จำได้มั้ยว่าเราเคยพูดถึง `Object.assign()` [in the past](info:object-copy#cloning-and-merging-object-assign)? +จำได้ไหมว่าเราเคยพูดถึง `Object.assign()` [ไปก่อนหน้านี้](info:object-copy#cloning-and-merging-object-assign)? -เราสามารถทำแบบ `Object.assign()` ได้ ผ่านการใช้ spread syntax. +เราใช้ spread syntax ทำแบบเดียวกันได้เลย ```js run let arr = [1, 2, 3]; *!* -let arrCopy = [...arr]; // กระจาย array ออกมาเป็นกลุ่มของ parameters - // จากนั้นนำผลลัพธ์ไปสร้าง array ตัวใหม่ +let arrCopy = [...arr]; // กระจายอาร์เรย์ออกเป็นรายการค่า + // แล้วนำไปสร้างอาร์เรย์ตัวใหม่ */!* -// แล้ว array ตัวใหม่ กับ array ตัวเดิมมีค่าเหมือนกันมั้ย ? +// อาร์เรย์ทั้งสองมีค่าเหมือนกันไหม? alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true -// แล้ว array 2 ตัวนั้นถูกอ้างอิงจากที่เดียวกันหรือป่าว ? -alert(arr === arrCopy); // false (ไม่ได้ถูกอ้างอิงจากที่เดียวกัน) +// แต่อ้างอิงตัวเดียวกันไหม? +alert(arr === arrCopy); // false (คนละตัวกัน) -// การเปลี่ยน array ต้นแบบ จะไม่เปลี่ยนค่า array ตัวใหม่: +// แก้ไขต้นฉบับจะไม่กระทบสำเนา: arr.push(4); alert(arr); // 1, 2, 3, 4 alert(arrCopy); // 1, 2, 3 ``` -จำไว้ว่า มันเป็นไปได้ที่จะทำเหมือนกัน ตอนที่เราจะก๊อปปี้ object: +ใช้วิธีเดียวกันนี้คัดลอกออบเจ็กต์ก็ได้เช่นกัน: ```js run let obj = { a: 1, b: 2, c: 3 }; *!* -let objCopy = { ...obj }; // กระจาย object ออกมาเป็นกลุ่มของ parameters - // จากนั้นนำผลลัพธ์ไปสร้าง object ตัวใหม่ +let objCopy = { ...obj }; // กระจายออบเจ็กต์ออกเป็นพร็อพเพอร์ตี้แยกทีละตัว + // แล้วนำไปสร้างออบเจ็กต์ตัวใหม่ */!* -// แล้ว object ตัวใหม่ กับ object ตัวเดิมมีค่าเหมือนกันมั้ย ? +// ออบเจ็กต์ทั้งสองมีค่าเหมือนกันไหม? alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true -// แล้ว object 2 ตัวนั้นถูกอ้างอิงจากที่เดียวกันหรือป่าว ? -alert(obj === objCopy); // false (ไม่ได้ถูกอ้างอิงจากที่เดียวกัน) +// แต่อ้างอิงตัวเดียวกันไหม? +alert(obj === objCopy); // false (คนละตัวกัน) -// การเปลี่ยน object ต้นแบบ จะไม่เปลี่ยนค่า object ตัวใหม่: +// แก้ไขต้นฉบับจะไม่กระทบสำเนา: obj.d = 4; alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4} alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3} ``` -ด้วยวิธีการนี้จะทำให้การเขียนก๊อปปี้ object เขียนได้สั้นกว่า `let objCopy = Object.assign({}, obj)` หรือสำหรับ array คือ `let arrCopy = Object.assign([], arr)` ดังนั้นเราจึงใช้พยายามใช้มันบ่อย ๆ เท่าที่จะทำได้ +วิธีนี้เขียนสั้นกว่า `let objCopy = Object.assign({}, obj)` หรือ `let arrCopy = Object.assign([], arr)` มาก จึงนิยมใช้กันมากกว่า ## สรุป -เมื่อเราเห็น `"..."` ในโค้ด, มันอาจจะเป็น rest parameters หรือ spread syntax ก็ได้ +พอเห็น `"..."` ในโค้ด อาจเป็นได้ทั้ง rest parameters หรือ spread syntax -แต่มันก็มีวิธีง่าย ๆ ในการแยกแยะความแตกต่างระหว่างสองสิ่งนี้ +แยกได้ง่าย ๆ ดังนี้: -- เมื่อ `...` ปรากฏอยู่ที่ด้านท้ายของ parameters ในฟังก์ชั่น, มันคือ "rest parameters" ซึ่งจะรวม argument ที่เหลือใน array -- เมื่อ `...` ปรากฏอยู่ ตอนที่เรียกใช้ฟังก์ชั่น, มันคือ "spread syntax" ซึ่งจะกระจาย array ออกมาเป็นกลุ่มของ parameter +- ถ้า `...` อยู่ท้ายพารามิเตอร์ของฟังก์ชัน นั่นคือ "rest parameters" ซึ่งจะรวมอาร์กิวเมนต์ที่เหลือเข้าเป็นอาร์เรย์ +- ถ้า `...` อยู่ตอนเรียกฟังก์ชัน นั่นคือ "spread syntax" ซึ่งจะกระจายอาร์เรย์ออกเป็นอาร์กิวเมนต์แยกทีละตัว -Patterns การใช้งาน: +รูปแบบการใช้งาน: -- Rest parameters มักใช้กับฟังก์ชั่นที่สามารถรับ arguments กี่ตัวก็ได้ -- Spread syntax จะใช้กับการเรียกใช้ array ในฟังก์ชั่น ที่ในฟังก์ชั่นนั้นอาจจะมีหลาย arguments +- Rest parameters ใช้สร้างฟังก์ชันที่รับอาร์กิวเมนต์ได้ไม่จำกัดจำนวน +- Spread syntax ใช้ส่งอาร์เรย์เข้าไปในฟังก์ชันที่ต้องการอาร์กิวเมนต์แยกทีละตัว -ทั้งสองอย่างนี้ช่วยให้เราทำงานกับ array ของ parameters ได้ง่ายยิ่งขึ้น +ทั้งสองอย่างนี้ช่วยให้เราสลับไปมาระหว่างรายการค่ากับอาร์เรย์ได้สะดวก -ตัว arguments ในฟังก์ชั่นยังคงสามารถเรียกใช้ผ่าน "old-style" `arguments`: array-like iterable object ได้เหมือนเดิม +นอกจากนี้ ยังสามารถเข้าถึงอาร์กิวเมนต์ทั้งหมดของฟังก์ชันได้ผ่าน `arguments` แบบเดิม ซึ่งเป็น array-like iterable ออบเจ็กต์ diff --git a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md index 7cbd85ab7..597e35a5e 100644 --- a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md +++ b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md @@ -1,5 +1,5 @@ -The answer is: **Pete**. +คำตอบคือ: **Pete** -A function gets outer variables as they are now, it uses the most recent values. +ฟังก์ชันจะดึงค่าตัวแปรภายนอก ณ ตอนที่เรียกใช้ โดยใช้ค่าล่าสุดเสมอ -Old variable values are not saved anywhere. When a function wants a variable, it takes the current value from its own Lexical Environment or the outer one. +ค่าเก่าจะไม่ถูกเก็บไว้ที่ไหน เมื่อฟังก์ชันต้องการตัวแปร จะดึงค่าปัจจุบันจาก Lexical Environment ของตัวเองหรือชั้นนอก diff --git a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md index 819189773..defb1a3e1 100644 --- a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md +++ b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Does a function pickup latest changes? +# ฟังก์ชันจะเห็นค่าล่าสุดไหม? -The function sayHi uses an external variable name. When the function runs, which value is it going to use? +ฟังก์ชัน sayHi ใช้ตัวแปรภายนอก name เมื่อฟังก์ชันทำงาน จะใช้ค่าไหน? ```js let name = "John"; @@ -15,9 +15,9 @@ function sayHi() { name = "Pete"; -sayHi(); // what will it show: "John" or "Pete"? +sayHi(); // จะแสดงอะไร: "John" หรือ "Pete"? ``` -Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request. +สถานการณ์แบบนี้พบได้บ่อยทั้งในฝั่ง browser และ server ฟังก์ชันอาจถูกตั้งเวลาให้ทำงานทีหลัง เช่น หลังจากผู้ใช้กดปุ่มหรือหลังจากรับข้อมูลจากเครือข่าย -So, the question is: does it pick up the latest changes? +คำถามคือ: ฟังก์ชันจะใช้ค่าล่าสุดไหม? diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js index a26578ae1..dd01c66f9 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js +++ b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js @@ -3,8 +3,8 @@ function makeArmy() { let shooters = []; for(let i = 0; i < 10; i++) { - let shooter = function() { // shooter function - alert( i ); // should show its number + let shooter = function() { // ฟังก์ชัน shooter + alert( i ); // ควรแสดงหมายเลขของมัน }; shooters.push(shooter); } diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js index 7c7aaa1e3..2cb3826b7 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js +++ b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js @@ -3,8 +3,8 @@ function makeArmy() { let i = 0; while (i < 10) { - let shooter = function() { // shooter function - alert( i ); // should show its number + let shooter = function() { // ฟังก์ชัน shooter + alert( i ); // ควรแสดงหมายเลขของมัน }; shooters.push(shooter); i++; @@ -16,7 +16,7 @@ function makeArmy() { /* let army = makeArmy(); -army[0](); // the shooter number 0 shows 10 -army[5](); // and number 5 also outputs 10... -// ... all shooters show 10 instead of their 0, 1, 2, 3... +army[0](); // shooter หมายเลข 0 แสดง 10 +army[5](); // หมายเลข 5 ก็แสดง 10 เช่นกัน... +// ... shooters ทุกตัวแสดง 10 แทนที่จะเป็น 0, 1, 2, 3... */ diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md index 9d99aa717..e455b5a2d 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md +++ b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md @@ -1,14 +1,14 @@ -Let's examine what exactly happens inside `makeArmy`, and the solution will become obvious. +มาดูกันว่าเกิดอะไรขึ้นภายใน `makeArmy` แล้วจะเข้าใจคำตอบเอง -1. It creates an empty array `shooters`: +1. สร้างอาร์เรย์เปล่า `shooters`: ```js let shooters = []; ``` -2. Fills it with functions via `shooters.push(function)` in the loop. +2. เติมฟังก์ชันเข้าไปด้วย `shooters.push(function)` ในลูป - Every element is a function, so the resulting array looks like this: + สมาชิกทุกตัวเป็นฟังก์ชัน อาร์เรย์ที่ได้จึงหน้าตาแบบนี้: ```js no-beautify shooters = [ @@ -25,105 +25,105 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco ]; ``` -3. The array is returned from the function. - - Then, later, the call to any member, e.g. `army[5]()` will get the element `army[5]` from the array (which is a function) and calls it. - - Now why do all such functions show the same value, `10`? - - That's because there's no local variable `i` inside `shooter` functions. When such a function is called, it takes `i` from its outer lexical environment. - - Then, what will be the value of `i`? - - If we look at the source: - +3. อาร์เรย์ถูก return ออกจากฟังก์ชัน + + จากนั้น เมื่อเรียกสมาชิกตัวใดก็ตาม เช่น `army[5]()` จะดึงเอลิเมนต์ `army[5]` จากอาร์เรย์ (ซึ่งเป็นฟังก์ชัน) แล้วเรียกใช้ + + ทำไมฟังก์ชันทุกตัวถึงแสดงค่าเดียวกันคือ `10`? + + เพราะไม่มีตัวแปร `i` ภายในฟังก์ชัน `shooter` เมื่อเรียกฟังก์ชัน จึงดึงค่า `i` จาก Lexical Environment ชั้นนอก + + แล้วค่าของ `i` จะเป็นเท่าไหร่? + + ลองดูจากซอร์สโค้ด: + ```js function makeArmy() { ... let i = 0; while (i < 10) { - let shooter = function() { // shooter function - alert( i ); // should show its number + let shooter = function() { // ฟังก์ชัน shooter + alert( i ); // ควรแสดงหมายเลขของมัน }; - shooters.push(shooter); // add function to the array + shooters.push(shooter); // เพิ่มฟังก์ชันเข้าอาร์เรย์ i++; } ... } ``` - - We can see that all `shooter` functions are created in the lexical environment of `makeArmy()` function. But when `army[5]()` is called, `makeArmy` has already finished its job, and the final value of `i` is `10` (`while` stops at `i=10`). - - As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`. - + + จะเห็นว่าฟังก์ชัน `shooter` ทุกตัวถูกสร้างใน Lexical Environment ของฟังก์ชัน `makeArmy()` แต่เมื่อเรียก `army[5]()` ตอนนั้น `makeArmy` ทำงานเสร็จแล้ว และค่าสุดท้ายของ `i` คือ `10` (`while` หยุดที่ `i=10`) + + ผลก็คือ ฟังก์ชัน `shooter` ทุกตัวดึงค่าเดียวกันจาก Lexical Environment ชั้นนอก นั่นคือค่าสุดท้าย `i=10` + ![](lexenv-makearmy-empty.svg) - - As you can see above, on each iteration of a `while {...}` block, a new lexical environment is created. So, to fix this, we can copy the value of `i` into a variable within the `while {...}` block, like this: - + + จากรูปด้านบนจะเห็นว่า ในแต่ละรอบของลูป `while {...}` จะสร้าง Lexical Environment ใหม่ ดังนั้นวิธีแก้คือ คัดลอกค่า `i` ไว้ในตัวแปรภายในบล็อก `while {...}` แบบนี้: + ```js run function makeArmy() { let shooters = []; - + let i = 0; while (i < 10) { *!* let j = i; */!* - let shooter = function() { // shooter function - alert( *!*j*/!* ); // should show its number + let shooter = function() { // ฟังก์ชัน shooter + alert( *!*j*/!* ); // ควรแสดงหมายเลขของมัน }; shooters.push(shooter); i++; } - + return shooters; } - + let army = makeArmy(); - - // Now the code works correctly + + // ตอนนี้โค้ดทำงานถูกต้องแล้ว army[0](); // 0 army[5](); // 5 ``` - - Here `let j = i` declares an "iteration-local" variable `j` and copies `i` into it. Primitives are copied "by value", so we actually get an independent copy of `i`, belonging to the current loop iteration. - - The shooters work correctly, because the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds to the current loop iteration: - + + ตรงนี้ `let j = i` ประกาศตัวแปร `j` ที่เป็นของแต่ละรอบลูป แล้วคัดลอกค่า `i` ไป ค่า primitive ถูกคัดลอก "ตามค่า" ดังนั้นจึงได้สำเนาอิสระของ `i` ที่เป็นของรอบลูปปัจจุบัน + + shooters ทำงานถูกต้อง เพราะค่าของ `i` ตอนนี้อยู่ใกล้ขึ้น ไม่ได้อยู่ใน Lexical Environment ของ `makeArmy()` แต่อยู่ใน Lexical Environment ของรอบลูปปัจจุบัน: + ![](lexenv-makearmy-while-fixed.svg) - - Such a problem could also be avoided if we used `for` in the beginning, like this: - + + ปัญหานี้จะหายไปเลยถ้าใช้ `for` ตั้งแต่แรก แบบนี้: + ```js run demo function makeArmy() { - + let shooters = []; - + *!* for(let i = 0; i < 10; i++) { */!* - let shooter = function() { // shooter function - alert( i ); // should show its number + let shooter = function() { // ฟังก์ชัน shooter + alert( i ); // ควรแสดงหมายเลขของมัน }; shooters.push(shooter); } - + return shooters; } - + let army = makeArmy(); - + army[0](); // 0 army[5](); // 5 ``` - - That's essentially the same, because `for` on each iteration generates a new lexical environment, with its own variable `i`. So `shooter` generated in every iteration references its own `i`, from that very iteration. - + + หลักการเดียวกัน เพราะ `for` จะสร้าง Lexical Environment ใหม่ในแต่ละรอบ พร้อมตัวแปร `i` ของตัวเอง ดังนั้น `shooter` ที่สร้างในแต่ละรอบจะอ้างอิง `i` ของรอบนั้นโดยเฉพาะ + ![](lexenv-makearmy-for-fixed.svg) -Now, as you've put so much effort into reading this, and the final recipe is so simple - just use `for`, you may wonder -- was it worth that? +ตอนนี้ที่อ่านมาตั้งมาก แล้วสรุปก็แค่ใช้ `for` อาจรู้สึกว่าคุ้มไหมที่อ่าน? -Well, if you could easily answer the question, you wouldn't read the solution. So, hopefully this task must have helped you to understand things a bit better. +ถ้าตอบได้ทันทีก็คงไม่ต้องอ่านคำเฉลย หวังว่าโจทย์ข้อนี้จะช่วยให้เข้าใจเรื่องนี้ได้ดีขึ้น -Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios, where such problems are real. +นอกจากนี้ ในการทำงานจริงก็มีกรณีที่ต้องใช้ `while` แทน `for` และปัญหาแบบนี้ก็เกิดขึ้นจริง diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/task.md b/1-js/06-advanced-functions/03-closure/10-make-army/task.md index f50c7dc20..364203ea0 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/task.md +++ b/1-js/06-advanced-functions/03-closure/10-make-army/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Army of functions +# กองทัพของฟังก์ชัน -The following code creates an array of `shooters`. +โค้ดด้านล่างสร้างอาร์เรย์ของ `shooters` -Every function is meant to output its number. But something is wrong... +ฟังก์ชันแต่ละตัวควรจะแสดงหมายเลขของมัน แต่มีบางอย่างผิดพลาด... ```js run function makeArmy() { @@ -14,28 +14,28 @@ function makeArmy() { let i = 0; while (i < 10) { - let shooter = function() { // create a shooter function, - alert( i ); // that should show its number + let shooter = function() { // สร้างฟังก์ชัน shooter + alert( i ); // ที่ควรแสดงหมายเลขของมัน }; - shooters.push(shooter); // and add it to the array + shooters.push(shooter); // แล้วเพิ่มเข้าไปในอาร์เรย์ i++; } - // ...and return the array of shooters + // ...แล้วคืนค่าอาร์เรย์ของ shooters return shooters; } let army = makeArmy(); *!* -// all shooters show 10 instead of their numbers 0, 1, 2, 3... -army[0](); // 10 from the shooter number 0 -army[1](); // 10 from the shooter number 1 -army[2](); // 10 ...and so on. +// shooters ทุกตัวแสดง 10 แทนที่จะเป็นหมายเลข 0, 1, 2, 3... +army[0](); // 10 จาก shooter หมายเลข 0 +army[1](); // 10 จาก shooter หมายเลข 1 +army[2](); // 10 ...เป็นแบบนี้ไปเรื่อยๆ */!* ``` -Why do all of the shooters show the same value? +ทำไม shooters ทุกตัวถึงแสดงค่าเดียวกัน? -Fix the code so that they work as intended. +จงแก้โค้ดให้ทำงานตามที่ต้องการ diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md index 0a522132f..798ed1ab4 100644 --- a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md @@ -1,9 +1,9 @@ -The answer is: **Pete**. +คำตอบคือ: **Pete** -The `work()` function in the code below gets `name` from the place of its origin through the outer lexical environment reference: +ฟังก์ชัน `work()` ในโค้ดด้านล่างได้ค่า `name` จากตำแหน่งที่มันถูกสร้าง ผ่านการอ้างอิง Lexical Environment ชั้นนอก: ![](lexenv-nested-work.svg) -So, the result is `"Pete"` here. +ดังนั้นผลลัพธ์คือ `"Pete"` -But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case the result would be `"John"`. +แต่ถ้าไม่มี `let name` ใน `makeWorker()` การค้นหาจะไล่ออกไปข้างนอกและใช้ตัวแปร global ตามที่เห็นในห่วงโซ่ด้านบน ในกรณีนั้นผลลัพธ์จะเป็น `"John"` diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md index d12a385c8..d63147b31 100644 --- a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Which variables are available? +# ตัวแปรไหนเข้าถึงได้บ้าง? -The function `makeWorker` below makes another function and returns it. That new function can be called from somewhere else. +ฟังก์ชัน `makeWorker` ด้านล่างสร้างฟังก์ชันอีกตัวหนึ่งแล้ว return ออกมา ฟังก์ชันใหม่นี้สามารถเรียกจากที่อื่นได้ -Will it have access to the outer variables from its creation place, or the invocation place, or both? +คำถามคือ มันจะเข้าถึงตัวแปรภายนอกจากตอนที่สร้าง หรือจากตอนที่เรียกใช้ หรือทั้งคู่? ```js function makeWorker() { @@ -19,11 +19,11 @@ function makeWorker() { let name = "John"; -// create a function +// สร้างฟังก์ชัน let work = makeWorker(); -// call it -work(); // what will it show? +// เรียกใช้ +work(); // จะแสดงอะไร? ``` -Which value it will show? "Pete" or "John"? +จะแสดง "Pete" หรือ "John"? diff --git a/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md b/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md index 25ecbea4c..544e225f4 100644 --- a/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md +++ b/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md @@ -1,5 +1,5 @@ -The answer: **0,1.** +คำตอบคือ: **0,1** -Functions `counter` and `counter2` are created by different invocations of `makeCounter`. +ฟังก์ชัน `counter` กับ `counter2` ถูกสร้างจากการเรียก `makeCounter` คนละครั้ง -So they have independent outer Lexical Environments, each one has its own `count`. +ดังนั้นจึงมี Lexical Environment ชั้นนอกเป็นของตัวเอง แต่ละตัวมี `count` ของตัวเอง diff --git a/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md b/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md index e8c17dd31..e4caf578e 100644 --- a/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md +++ b/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Are counters independent? +# ตัวนับแต่ละตัวเป็นอิสระจากกันไหม? -Here we make two counters: `counter` and `counter2` using the same `makeCounter` function. +เราสร้างตัวนับ 2 ตัว คือ `counter` กับ `counter2` จากฟังก์ชัน `makeCounter` ตัวเดียวกัน -Are they independent? What is the second counter going to show? `0,1` or `2,3` or something else? +แต่ละตัวเป็นอิสระจากกันไหม? ตัวนับตัวที่สองจะแสดงค่าอะไร? `0,1` หรือ `2,3` หรืออย่างอื่น? ```js function makeCounter() { diff --git a/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md index cd4e641e4..da3e466ad 100644 --- a/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md +++ b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md @@ -1,7 +1,7 @@ -Surely it will work just fine. +ทำงานได้ปกติ -Both nested functions are created within the same outer Lexical Environment, so they share access to the same `count` variable: +ฟังก์ชันซ้อนทั้งสองตัวถูกสร้างภายใน Lexical Environment ชั้นนอกเดียวกัน จึงเข้าถึงตัวแปร `count` ตัวเดียวกันได้: ```js run function Counter() { @@ -10,7 +10,7 @@ function Counter() { this.up = function() { return ++count; }; - + this.down = function() { return --count; }; diff --git a/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md index d770b0ffc..d2ebe51f5 100644 --- a/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md +++ b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Counter object +# ออบเจ็กต์ตัวนับ -Here a counter object is made with the help of the constructor function. +ตัวนับด้านล่างสร้างจากคอนสตรักเตอร์ฟังก์ชัน -Will it work? What will it show? +มันจะทำงานได้ไหม? จะแสดงอะไร? ```js function Counter() { diff --git a/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md index e2e7a91b3..0e4ce33c3 100644 --- a/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md +++ b/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md @@ -1,3 +1,3 @@ -The result is **an error**. +ผลลัพธ์คือ **error** -The function `sayHi` is declared inside the `if`, so it only lives inside it. There is no `sayHi` outside. \ No newline at end of file +ฟังก์ชัน `sayHi` ถูกประกาศภายในบล็อก `if` จึงมีอยู่แค่ภายในบล็อกนั้น ข้างนอกไม่มี `sayHi` \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md index 4e386eec5..ac1c4a7d8 100644 --- a/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md +++ b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md @@ -1,9 +1,9 @@ importance: 5 --- -# Function in if +# ฟังก์ชันใน if -Look at the code. What will be the result of the call at the last line? +ดูโค้ดนี้ ผลลัพธ์ของการเรียกบรรทัดสุดท้ายจะเป็นอะไร? ```js run let phrase = "Hello"; diff --git a/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md b/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md index a6679cd20..cd5bcbc24 100644 --- a/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md +++ b/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md @@ -1,12 +1,12 @@ -For the second parentheses to work, the first ones must return a function. +วงเล็บชั้นที่สองจะทำงานได้ วงเล็บชั้นแรกต้องคืนค่าเป็นฟังก์ชัน -Like this: +แบบนี้: ```js run function sum(a) { return function(b) { - return a + b; // takes "a" from the outer lexical environment + return a + b; // ดึงค่า "a" จาก Lexical Environment ชั้นนอก }; } diff --git a/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md b/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md index b45758562..31b702549 100644 --- a/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md +++ b/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md @@ -2,13 +2,13 @@ importance: 4 --- -# Sum with closures +# บวกเลขด้วยคลอเชอร์ -Write function `sum` that works like this: `sum(a)(b) = a+b`. +เขียนฟังก์ชัน `sum` ที่ทำงานแบบนี้: `sum(a)(b) = a+b` -Yes, exactly this way, using double parentheses (not a mistype). +ใช่แล้ว เรียกด้วยวงเล็บสองชั้นแบบนี้เลย (ไม่ได้พิมพ์ผิด) -For instance: +ยกตัวอย่าง: ```js sum(1)(2) = 3 diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md index b16b35290..89c253f22 100644 --- a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md @@ -1,6 +1,6 @@ -The result is: **error**. +ผลลัพธ์คือ: **error** -Try running it: +ลองรันดู: ```js run let x = 1; @@ -15,20 +15,20 @@ function func() { func(); ``` -In this example we can observe the peculiar difference between a "non-existing" and "uninitialized" variable. +ตัวอย่างนี้แสดงให้เห็นความแตกต่างระหว่างตัวแปรที่ "ไม่มีอยู่" กับ "ยังไม่ได้ initialize" -As you may have read in the article [](info:closure), a variable starts in the "uninitialized" state from the moment when the execution enters a code block (or a function). And it stays uninitalized until the corresponding `let` statement. +อย่างที่อาจอ่านมาแล้วในบทความ [](info:closure) ตัวแปรจะเริ่มต้นในสถานะ "uninitialized" ตั้งแต่เมื่อการทำงานเข้าสู่บล็อกโค้ด (หรือฟังก์ชัน) และจะอยู่ในสถานะนี้จนกว่าจะถึงคำสั่ง `let` ที่ประกาศมัน -In other words, a variable technically exists, but can't be used before `let`. +พูดง่ายๆ ก็คือ ตัวแปรมีอยู่ในทางเทคนิค แต่ยังใช้ไม่ได้จนกว่าจะถึงบรรทัด `let` -The code above demonstrates it. +โค้ดด้านบนแสดงให้เห็นจุดนี้ ```js function func() { *!* - // the local variable x is known to the engine from the beginning of the function, - // but "uninitialized" (unusable) until let ("dead zone") - // hence the error + // engine รู้ว่ามีตัวแปร x ตั้งแต่เริ่มฟังก์ชัน + // แต่อยู่ในสถานะ "uninitialized" (ใช้ไม่ได้) จนกว่าจะถึง let ("dead zone") + // จึงเกิด error */!* console.log(x); // ReferenceError: Cannot access 'x' before initialization @@ -37,4 +37,4 @@ function func() { } ``` -This zone of temporary unusability of a variable (from the beginning of the code block till `let`) is sometimes called the "dead zone". +โซนที่ตัวแปรใช้ไม่ได้ชั่วคราวนี้ (ตั้งแต่ต้นบล็อกจนถึง `let`) บางทีเรียกว่า "dead zone" diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/task.md b/1-js/06-advanced-functions/03-closure/7-let-scope/task.md index fb7445e66..351f8d306 100644 --- a/1-js/06-advanced-functions/03-closure/7-let-scope/task.md +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/task.md @@ -2,9 +2,9 @@ importance: 4 --- -# Is variable visible? +# ตัวแปรมองเห็นไหม? -What will be the result of this code? +โค้ดนี้จะให้ผลลัพธ์อะไร? ```js let x = 1; @@ -18,4 +18,4 @@ function func() { func(); ``` -P.S. There's a pitfall in this task. The solution is not obvious. +ป.ล. โจทย์ข้อนี้มีกับดัก คำตอบอาจไม่ตรงตามที่คิดไว้ diff --git a/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js b/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js index 74989df28..bba19d1db 100644 --- a/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js +++ b/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js @@ -2,9 +2,9 @@ let arr = [1, 2, 3, 4, 5, 6, 7]; function inBetween(a, b) { - // ...your code... + // ...โค้ดของคุณ... } function inArray(arr) { - // ...your code... + // ...โค้ดของคุณ... } diff --git a/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md b/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md index d1c39f949..09ec5df79 100644 --- a/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md +++ b/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md @@ -2,24 +2,24 @@ importance: 5 --- -# Filter through function +# กรองข้อมูลผ่านฟังก์ชัน -We have a built-in method `arr.filter(f)` for arrays. It filters all elements through the function `f`. If it returns `true`, then that element is returned in the resulting array. +เรามีเมธอด `arr.filter(f)` สำหรับอาร์เรย์ ซึ่งกรองสมาชิกทั้งหมดผ่านฟังก์ชัน `f` ถ้าคืนค่า `true` สมาชิกนั้นจะถูกรวมไว้ในอาร์เรย์ผลลัพธ์ -Make a set of "ready to use" filters: +จงสร้างชุดฟังก์ชันกรอง "สำเร็จรูป" ดังนี้: -- `inBetween(a, b)` -- between `a` and `b` or equal to them (inclusively). -- `inArray([...])` -- in the given array. +- `inBetween(a, b)` -- กรองค่าที่อยู่ระหว่าง `a` ถึง `b` รวมค่าขอบด้วย +- `inArray([...])` -- กรองค่าที่อยู่ในอาร์เรย์ที่กำหนด -The usage must be like this: +ใช้งานแบบนี้: -- `arr.filter(inBetween(3,6))` -- selects only values between 3 and 6. -- `arr.filter(inArray([1,2,3]))` -- selects only elements matching with one of the members of `[1,2,3]`. +- `arr.filter(inBetween(3,6))` -- เลือกเฉพาะค่าที่อยู่ระหว่าง 3 ถึง 6 +- `arr.filter(inArray([1,2,3]))` -- เลือกเฉพาะสมาชิกที่ตรงกับค่าใน `[1,2,3]` -For instance: +ยกตัวอย่าง: ```js -/* .. your code for inBetween and inArray */ +/* .. โค้ดของ inBetween และ inArray */ let arr = [1, 2, 3, 4, 5, 6, 7]; alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6 diff --git a/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js index 23b433834..68c182f82 100644 --- a/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js @@ -1,5 +1,5 @@ function byField(fieldName){ - // Your code goes here. + // เขียนโค้ดตรงนี้ } diff --git a/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md b/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md index 08fb5cc34..752a9a1da 100644 --- a/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Sort by field +# เรียงลำดับตามฟิลด์ -We've got an array of objects to sort: +เรามีอาร์เรย์ของออบเจ็กต์ที่ต้องการเรียงลำดับ: ```js let users = [ @@ -14,23 +14,23 @@ let users = [ ]; ``` -The usual way to do that would be: +วิธีปกติที่ทำกันคือ: ```js -// by name (Ann, John, Pete) +// เรียงตามชื่อ (Ann, John, Pete) users.sort((a, b) => a.name > b.name ? 1 : -1); -// by age (Pete, Ann, John) +// เรียงตามอายุ (Pete, Ann, John) users.sort((a, b) => a.age > b.age ? 1 : -1); ``` -Can we make it even less verbose, like this? +จะทำให้กระชับกว่านี้ได้ไหม แบบนี้? ```js users.sort(byField('name')); users.sort(byField('age')); ``` -So, instead of writing a function, just put `byField(fieldName)`. +คือแทนที่จะเขียนฟังก์ชันเอง แค่ใส่ `byField(fieldName)` ไปเลย -Write the function `byField` that can be used for that. +จงเขียนฟังก์ชัน `byField` ที่ใช้แบบนี้ได้ diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index cb43a7968..dc9a5a69f 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -1,35 +1,35 @@ -# Variable scope, closure +# สโคปของตัวแปรและคลอเชอร์ (Closure) -JavaScript is a very function-oriented language. It gives us a lot of freedom. A function can be created at any moment, passed as an argument to another function, and then called from a totally different place of code later. +JavaScript เป็นภาษาที่เน้นการใช้งานฟังก์ชันเป็นหลัก เราสร้างฟังก์ชันได้ทุกเมื่อ ส่งผ่านเป็นอาร์กิวเมนต์ให้ฟังก์ชันอื่น แล้วค่อยเรียกใช้ทีหลังจากที่อื่นในโค้ดก็ได้ -We already know that a function can access variables outside of it ("outer" variables). +จากที่เราเรียนมาแล้ว ฟังก์ชันสามารถเข้าถึงตัวแปรภายนอก ("outer" variables) ได้ -But what happens if outer variables change since a function is created? Will the function get newer values or the old ones? +แต่ถ้าตัวแปรภายนอกเปลี่ยนค่าไปหลังจากสร้างฟังก์ชันแล้วล่ะ? ฟังก์ชันจะได้ค่าใหม่หรือค่าเก่า? -And what if a function is passed along as an argument and called from another place of code, will it get access to outer variables at the new place? +แล้วถ้าส่งฟังก์ชันไปเป็นอาร์กิวเมนต์ แล้วเรียกใช้จากที่อื่นในโค้ด จะยังเข้าถึงตัวแปรภายนอกจากที่ใหม่ได้ไหม? -Let's expand our knowledge to understand these scenarios and more complex ones. +มาขยายความรู้เพื่อทำความเข้าใจสถานการณ์เหล่านี้ รวมถึงกรณีที่ซับซ้อนกว่ากัน -```smart header="We'll talk about `let/const` variables here" -In JavaScript, there are 3 ways to declare a variable: `let`, `const` (the modern ones), and `var` (the remnant of the past). +```smart header="บทความนี้จะพูดถึงตัวแปร `let/const`" +ใน JavaScript มีวิธีประกาศตัวแปรอยู่ 3 แบบ คือ `let`, `const` (แบบใหม่) และ `var` (ตกค้างจากยุคเก่า) -- In this article we'll use `let` variables in examples. -- Variables, declared with `const`, behave the same, so this article is about `const` too. -- The old `var` has some notable differences, they will be covered in the article . +- ในบทความนี้เราจะใช้ `let` ในตัวอย่างเป็นหลัก +- ตัวแปรที่ประกาศด้วย `const` ก็ทำงานเหมือนกัน ดังนั้นบทความนี้ครอบคลุม `const` ด้วยเช่นกัน +- `var` แบบเก่ามีความแตกต่างที่สำคัญบางประการ จะอธิบายเพิ่มในบทความ ``` -## Code blocks +## บล็อกของโค้ด (Code blocks) -If a variable is declared inside a code block `{...}`, it's only visible inside that block. +ถ้าประกาศตัวแปรภายในบล็อกโค้ด `{...}` ตัวแปรนั้นจะมองเห็นได้แค่ภายในบล็อกนั้นเท่านั้น -For example: +ยกตัวอย่าง: ```js run { - // do some job with local variables that should not be seen outside + // ทำงานบางอย่างกับตัวแปรภายในที่ไม่ควรเห็นจากข้างนอก - let message = "Hello"; // only visible in this block + let message = "Hello"; // มองเห็นได้แค่ในบล็อกนี้ alert(message); // Hello } @@ -37,31 +37,31 @@ For example: alert(message); // Error: message is not defined ``` -We can use this to isolate a piece of code that does its own task, with variables that only belong to it: +เราใช้วิธีนี้เพื่อแยกส่วนของโค้ดที่ทำงานเฉพาะของมันออกมา โดยมีตัวแปรที่ใช้เฉพาะภายในบล็อกนั้น: ```js run { - // show message + // แสดงข้อความ let message = "Hello"; alert(message); } { - // show another message + // แสดงข้อความอีกอัน let message = "Goodbye"; alert(message); } ``` -````smart header="There'd be an error without blocks" -Please note, without separate blocks there would be an error, if we use `let` with the existing variable name: +````smart header="ถ้าไม่มีบล็อกจะเกิด error" +โปรดสังเกตว่า ถ้าไม่แยกเป็นบล็อก จะเกิด error เมื่อใช้ `let` กับชื่อตัวแปรที่มีอยู่แล้ว: ```js run -// show message +// แสดงข้อความ let message = "Hello"; alert(message); -// show another message +// แสดงข้อความอีกอัน *!* let message = "Goodbye"; // Error: variable already declared */!* @@ -69,7 +69,7 @@ alert(message); ``` ```` -For `if`, `for`, `while` and so on, variables declared in `{...}` are also only visible inside: +สำหรับ `if`, `for`, `while` และอื่นๆ ตัวแปรที่ประกาศใน `{...}` ก็มองเห็นได้แค่ข้างในเช่นกัน: ```js run if (true) { @@ -81,35 +81,35 @@ if (true) { alert(phrase); // Error, no such variable! ``` -Here, after `if` finishes, the `alert` below won't see the `phrase`, hence the error. +ในที่นี้ หลังจาก `if` ทำงานเสร็จ `alert` ด้านล่างจะมองไม่เห็น `phrase` จึงเกิด error -That's great, as it allows us to create block-local variables, specific to an `if` branch. +นี่เป็นสิ่งที่ดี เพราะช่วยให้เราสร้างตัวแปรที่ใช้เฉพาะภายในบล็อก เช่น ตัวแปรที่ใช้เฉพาะในสาขาของ `if` -The similar thing holds true for `for` and `while` loops: +หลักการเดียวกันนี้ใช้ได้กับลูป `for` และ `while` ด้วย: ```js run for (let i = 0; i < 3; i++) { - // the variable i is only visible inside this for - alert(i); // 0, then 1, then 2 + // ตัวแปร i มองเห็นได้แค่ภายใน for นี้ + alert(i); // 0, จากนั้น 1, จากนั้น 2 } alert(i); // Error, no such variable ``` -Visually, `let i` is outside of `{...}`. But the `for` construct is special here: the variable, declared inside it, is considered a part of the block. +ถึงแม้ `let i` จะดูเหมือนอยู่นอก `{...}` แต่โครงสร้าง `for` เป็นกรณีพิเศษ ตัวแปรที่ประกาศข้างในถือว่าเป็นส่วนหนึ่งของบล็อก -## Nested functions +## ฟังก์ชันซ้อน (Nested functions) -A function is called "nested" when it is created inside another function. +ฟังก์ชันที่สร้างขึ้นภายในฟังก์ชันอื่น เราเรียกว่า "ฟังก์ชันซ้อน" (nested function) -It is easily possible to do this with JavaScript. +ใน JavaScript ทำแบบนี้ได้ง่ายมาก -We can use it to organize our code, like this: +เราใช้เพื่อจัดระเบียบโค้ดได้ เช่นแบบนี้: ```js function sayHiBye(firstName, lastName) { - // helper nested function to use below + // ฟังก์ชันซ้อนที่เป็นตัวช่วย function getFullName() { return firstName + " " + lastName; } @@ -120,11 +120,11 @@ function sayHiBye(firstName, lastName) { } ``` -Here the *nested* function `getFullName()` is made for convenience. It can access the outer variables and so can return the full name. Nested functions are quite common in JavaScript. +ตรงนี้ฟังก์ชันซ้อน `getFullName()` ถูกสร้างขึ้นเพื่อความสะดวก มันเข้าถึงตัวแปรภายนอกได้ จึงคืนค่าชื่อเต็มได้ ฟังก์ชันซ้อนเป็นสิ่งที่พบได้บ่อยมากใน JavaScript -What's much more interesting, a nested function can be returned: either as a property of a new object or as a result by itself. It can then be used somewhere else. No matter where, it still has access to the same outer variables. +สิ่งที่น่าสนใจกว่านั้นคือ ฟังก์ชันซ้อนสามารถถูก return ออกมาได้ ไม่ว่าจะเป็นพร็อพเพอร์ตี้ของออบเจ็กต์ใหม่ หรือเป็นผลลัพธ์โดยตรง จากนั้นจะนำไปใช้ที่อื่นก็ได้ ไม่ว่าจะเรียกจากที่ไหน มันยังเข้าถึงตัวแปรภายนอกเดิมได้เสมอ -Below, `makeCounter` creates the "counter" function that returns the next number on each invocation: +ตัวอย่างด้านล่าง `makeCounter` สร้างฟังก์ชัน "ตัวนับ" ที่คืนค่าตัวเลขถัดไปทุกครั้งที่เรียก: ```js run function makeCounter() { @@ -142,87 +142,87 @@ alert( counter() ); // 1 alert( counter() ); // 2 ``` -Despite being simple, slightly modified variants of that code have practical uses, for instance, as a [random number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) to generate random values for automated tests. +แม้จะเป็นโค้ดง่ายๆ แต่ถ้าดัดแปลงนิดหน่อยก็นำไปใช้งานจริงได้ เช่น เป็น[ตัวสร้างเลขสุ่ม](https://en.wikipedia.org/wiki/Pseudorandom_number_generator)สำหรับสร้างค่าสุ่มในการทดสอบอัตโนมัติ -How does this work? If we create multiple counters, will they be independent? What's going on with the variables here? +โค้ดนี้ทำงานยังไง? ถ้าสร้างตัวนับหลายตัว แต่ละตัวจะเป็นอิสระจากกันไหม? เกิดอะไรขึ้นกับตัวแปรในนี้? -Understanding such things is great for the overall knowledge of JavaScript and beneficial for more complex scenarios. So let's go a bit in-depth. +การเข้าใจเรื่องพวกนี้เป็นพื้นฐานสำคัญของ JavaScript และจะมีประโยชน์มากเมื่อเจอสถานการณ์ที่ซับซ้อนขึ้น ดังนั้นมาเจาะลึกกันเลย ## Lexical Environment -```warn header="Here be dragons!" -The in-depth technical explanation lies ahead. +```warn header="เนื้อหาเชิงลึกอยู่ข้างหน้า!" +เราจะอธิบายรายละเอียดเชิงเทคนิคกันต่อไป -As far as I'd like to avoid low-level language details, any understanding without them would be lacking and incomplete, so get ready. +แม้จะอยากหลีกเลี่ยงรายละเอียดภาษาระดับต่ำ แต่ถ้าไม่เข้าใจตรงนี้ ความรู้จะไม่ครบถ้วน ดังนั้นเตรียมตัวให้พร้อม ``` -For clarity, the explanation is split into multiple steps. +เพื่อให้อธิบายได้ชัดเจน จะแบ่งเป็นหลายขั้นตอน -### Step 1. Variables +### ขั้นตอนที่ 1: ตัวแปร -In JavaScript, every running function, code block `{...}`, and the script as a whole have an internal (hidden) associated object known as the *Lexical Environment*. +ใน JavaScript ทุกฟังก์ชันที่กำลังทำงาน ทุกบล็อกโค้ด `{...}` และทั้งสคริปต์ จะมีออบเจ็กต์ภายใน (ซ่อนอยู่) ที่เรียกว่า *Lexical Environment* -The Lexical Environment object consists of two parts: +ออบเจ็กต์ Lexical Environment ประกอบด้วย 2 ส่วน: -1. *Environment Record* -- an object that stores all local variables as its properties (and some other information like the value of `this`). -2. A reference to the *outer lexical environment*, the one associated with the outer code. +1. *Environment Record* -- ออบเจ็กต์ที่เก็บตัวแปรภายในทั้งหมดเป็นพร็อพเพอร์ตี้ (รวมถึงข้อมูลอื่นๆ เช่น ค่าของ `this`) +2. การอ้างอิงไปยัง *Lexical Environment ภายนอก* ที่เชื่อมกับโค้ดชั้นนอก -**A "variable" is just a property of the special internal object, `Environment Record`. "To get or change a variable" means "to get or change a property of that object".** +**"ตัวแปร" ก็คือพร็อพเพอร์ตี้หนึ่งของออบเจ็กต์ภายในพิเศษที่ชื่อ `Environment Record` นั่นเอง "การอ่านหรือเปลี่ยนค่าตัวแปร" ก็คือ "การอ่านหรือเปลี่ยนพร็อพเพอร์ตี้ของออบเจ็กต์ตัวนั้น"** -In this simple code without functions, there is only one Lexical Environment: +ในโค้ดง่ายๆ ที่ไม่มีฟังก์ชัน จะมี Lexical Environment แค่ตัวเดียว: ![lexical environment](lexical-environment-global.svg) -This is the so-called *global* Lexical Environment, associated with the whole script. +นี่คือ Lexical Environment แบบ *global* ที่เชื่อมกับทั้งสคริปต์ -On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer reference, that's why the arrow points to `null`. +ในรูปด้านบน สี่เหลี่ยมคือ Environment Record (ที่เก็บตัวแปร) และลูกศรคือการอ้างอิงไปยังภายนอก Lexical Environment แบบ global ไม่มีการอ้างอิงภายนอก ลูกศรจึงชี้ไปที่ `null` -As the code starts executing and goes on, the Lexical Environment changes. +เมื่อโค้ดเริ่มทำงานและดำเนินต่อไป Lexical Environment จะเปลี่ยนแปลงไปเรื่อยๆ -Here's a little bit longer code: +ลองดูโค้ดที่ยาวขึ้นหน่อย: ![lexical environment](closure-variable-phrase.svg) -Rectangles on the right-hand side demonstrate how the global Lexical Environment changes during the execution: +สี่เหลี่ยมด้านขวาแสดงให้เห็นว่า Lexical Environment แบบ global เปลี่ยนแปลงอย่างไรระหว่างการทำงาน: -1. When the script starts, the Lexical Environment is pre-populated with all declared variables. - - Initially, they are in the "Uninitialized" state. That's a special internal state, it means that the engine knows about the variable, but it cannot be referenced until it has been declared with `let`. It's almost the same as if the variable didn't exist. -2. Then `let phrase` definition appears. There's no assignment yet, so its value is `undefined`. We can use the variable from this point forward. -3. `phrase` is assigned a value. -4. `phrase` changes the value. +1. เมื่อสคริปต์เริ่มทำงาน Lexical Environment จะถูกเติมด้วยตัวแปรที่ประกาศไว้ทั้งหมด + - ตอนแรกตัวแปรจะอยู่ในสถานะ "Uninitialized" ซึ่งเป็นสถานะภายในพิเศษ หมายความว่า engine รู้ว่ามีตัวแปรนี้อยู่ แต่ยังอ้างอิงไม่ได้จนกว่าจะประกาศด้วย `let` เหมือนกับว่าตัวแปรยังไม่มีอยู่ +2. จากนั้น `let phrase` ปรากฏขึ้น ยังไม่มีการกำหนดค่า จึงเป็น `undefined` ตั้งแต่จุดนี้เป็นต้นไปเราใช้ตัวแปรนี้ได้ +3. `phrase` ถูกกำหนดค่า +4. `phrase` เปลี่ยนค่า -Everything looks simple for now, right? +ดูง่ายใช่ไหม? -- A variable is a property of a special internal object, associated with the currently executing block/function/script. -- Working with variables is actually working with the properties of that object. +- ตัวแปรคือพร็อพเพอร์ตี้ของออบเจ็กต์ภายในพิเศษ ที่เชื่อมกับบล็อก/ฟังก์ชัน/สคริปต์ที่กำลังทำงานอยู่ +- การทำงานกับตัวแปรก็คือการทำงานกับพร็อพเพอร์ตี้ของออบเจ็กต์ตัวนั้น -```smart header="Lexical Environment is a specification object" -"Lexical Environment" is a specification object: it only exists "theoretically" in the [language specification](https://tc39.es/ecma262/#sec-lexical-environments) to describe how things work. We can't get this object in our code and manipulate it directly. +```smart header="Lexical Environment เป็นออบเจ็กต์ตามสเปก" +"Lexical Environment" เป็นออบเจ็กต์ตามสเปกของภาษา (specification object) มีอยู่แค่ "ในทางทฤษฎี" ตาม[สเปกของภาษา](https://tc39.es/ecma262/#sec-lexical-environments)เพื่ออธิบายว่าสิ่งต่างๆ ทำงานอย่างไร เราไม่สามารถเข้าถึงออบเจ็กต์นี้ในโค้ดและจัดการโดยตรงได้ -JavaScript engines also may optimize it, discard variables that are unused to save memory and perform other internal tricks, as long as the visible behavior remains as described. +JavaScript engine อาจปรับแต่งการทำงาน เช่น ตัดตัวแปรที่ไม่ได้ใช้ออกเพื่อประหยัดหน่วยความจำ หรือทำเทคนิคอื่นๆ ภายใน ตราบใดที่พฤติกรรมที่มองเห็นยังคงเป็นไปตามที่อธิบายไว้ ``` -### Step 2. Function Declarations +### ขั้นตอนที่ 2: Function Declaration -A function is also a value, like a variable. +ฟังก์ชันก็เป็นค่าตัวหนึ่ง เหมือนกับตัวแปร -**The difference is that a Function Declaration is instantly fully initialized.** +**ข้อแตกต่างคือ Function Declaration จะถูกเตรียมพร้อมใช้งานทันที** -When a Lexical Environment is created, a Function Declaration immediately becomes a ready-to-use function (unlike `let`, that is unusable till the declaration). +เมื่อสร้าง Lexical Environment ขึ้นมา Function Declaration จะกลายเป็นฟังก์ชันที่พร้อมใช้ทันที (ต่างจาก `let` ที่ใช้ไม่ได้จนกว่าจะถึงบรรทัดที่ประกาศ) -That's why we can use a function, declared as Function Declaration, even before the declaration itself. +นั่นเป็นเหตุผลว่าทำไมเราถึงเรียกใช้ฟังก์ชันที่ประกาศแบบ Function Declaration ได้ก่อนที่จะถึงบรรทัดประกาศ -For example, here's the initial state of the global Lexical Environment when we add a function: +ตัวอย่างเช่น สถานะเริ่มต้นของ Lexical Environment แบบ global เมื่อเราเพิ่มฟังก์ชันเข้ามา: ![](closure-function-declaration.svg) -Naturally, this behavior only applies to Function Declarations, not Function Expressions where we assign a function to a variable, such as `let say = function(name)...`. +แน่นอนว่าพฤติกรรมนี้ใช้ได้กับ Function Declaration เท่านั้น ไม่ใช่ Function Expression ที่เราใส่ฟังก์ชันให้กับตัวแปร เช่น `let say = function(name)...` -### Step 3. Inner and outer Lexical Environment +### ขั้นตอนที่ 3: Lexical Environment ชั้นในและชั้นนอก -When a function runs, at the beginning of the call, a new Lexical Environment is created automatically to store local variables and parameters of the call. +เมื่อฟังก์ชันทำงาน ณ ตอนเริ่มต้นการเรียก จะมี Lexical Environment ใหม่ถูกสร้างขึ้นโดยอัตโนมัติเพื่อเก็บตัวแปรภายในฟังก์ชันและพารามิเตอร์ -For instance, for `say("John")`, it looks like this (the execution is at the line, labelled with an arrow): +ยกตัวอย่าง เมื่อเรียก `say("John")` จะเป็นแบบนี้ (การทำงานอยู่ที่บรรทัดที่มีลูกศรกำกับ):