diff --git a/1-js/02-first-steps/11-logical-operators/article.md b/1-js/02-first-steps/11-logical-operators/article.md index 078c6df40..8e2418a51 100644 --- a/1-js/02-first-steps/11-logical-operators/article.md +++ b/1-js/02-first-steps/11-logical-operators/article.md @@ -1,45 +1,45 @@ -## ตัวดำเนินการตรรกะ +# ตัวดำเนินการตรรกะ -JavaScript มีตัวดำเนินการตรรกะ 4 ตัว ได้แก่ `||` (OR), `&&` (AND), `!` (NOT), และ `??` (Nullish Coalescing) ที่นี่เราจะครอบคลุม 3 ตัวแรก ตัวดำเนินการ `??` อยู่ในบทความถัดไป +ใน JavaScript มีตัวดำเนินการตรรกะ 4 ตัว ได้แก่ `||` (OR), `&&` (AND), `!` (NOT) และ `??` (Nullish Coalescing) ในที่นี้จะอธิบายเฉพาะ 3 ตัวแรก ส่วนตัว `??` จะอยู่ในบทความถัดไป -แม้ว่าจะเรียกว่า "ตรรกะ" แต่ก็สามารถนำไปใช้กับค่าทุกประเภท ไม่ใช่แค่แบบบูลีน ผลลัพธ์ของพวกมันก็สามารถเป็นประเภทใดก็ได้ +แม้จะเรียกว่า "ตรรกะ" แต่ก็สามารถใช้กับค่าประเภทอื่นๆ นอกเหนือจาก boolean ได้ และผลลัพธ์ที่ออกมาก็เป็นประเภทอะไรก็ได้เช่นกัน มาดูรายละเอียดกัน ## || (OR) -ตัวดำเนินการ "OR" แสดงด้วยเครื่องหมายเส้นแนวตั้งสองอัน: +เครื่องหมาย "OR" จะแทนด้วยสัญลักษณ์เส้นตั้งคู่ `||` แบบนี้: ```js result = a || b; ``` -ในการเขียนโปรแกรมแบบดั้งเดิม OR ตรรกะใช้สำหรับจัดการค่าบูลีนเท่านั้น ถ้าอาร์กิวเมนต์ใดๆ เป็น `true` มันจะส่งคืน `true` มิฉะนั้นจะส่งคืน `false` +โดยทั่วไปแล้ว OR ตรรกะมีไว้ใช้กับค่า boolean เท่านั้น ถ้ามี argument ตัวใดเป็น `true` ก็จะคืนค่า `true` ถ้าไม่มีเลยจะคืนค่า `false` -ใน JavaScript ตัวดำเนินการมีความซับซ้อนและมีประสิทธิภาพมากกว่าเล็กน้อย แต่ก่อนอื่น มาดูสิ่งที่เกิดขึ้นกับค่าบูลีนกัน +แต่ใน JavaScript นั้นตัวดำเนินการ OR มีความซับซ้อนและมีพลังมากกว่านั้นหน่อย แต่ก่อนอื่นมาดูว่าเกิดอะไรขึ้นกับค่า boolean ก่อน -มีสี่ชุดค่าตรรกะที่เป็นไปได้: +จะมีเคสที่เป็นไปได้ 4 แบบ: ```js run alert( true || true ); // true alert( false || true ); // true alert( true || false ); // true -alert( false || false ); // false +alert( false || false ); // false ``` -อย่างที่เราเห็น ผลลัพธ์คือ `true` เสมอ ยกเว้นกรณีที่ทั้งสองตัวถูกดำเนินการเป็น `false` +จะเห็นว่าผลลัพธ์จะเป็น `true` เสมอ ยกเว้นกรณีที่ operand ทั้งสองข้างเป็น `false` -ถ้าตัวดำเนินการไม่ใช่แบบบูลีน มันจะถูกแปลงเป็นแบบบูลีนเพื่อการประเมิน +ถ้า operand ไม่ใช่ boolean จะถูกแปลงเป็น boolean ก่อนแล้วค่อยประเมิน -ตัวอย่างเช่น ตัวเลข `1` ถูกตีความเป็น `true` ตัวเลข `0` เป็น `false`: +เช่น ตัวเลข `1` จะถือเป็น `true` ส่วนตัวเลข `0` จะเป็น `false`: ```js run -if (1 || 0) { // ทำงานเหมือนกับ if( true || false ) - alert( 'truthy!' ); +if (1 || 0) { // จะทำงานเหมือน if (true || false) + alert('truthy!'); } ``` -ส่วนใหญ่ OR `||` ใช้ในคำสั่ง `if` เพื่อทดสอบว่าเงื่อนไขใด *ก็ตาม* ในเงื่อนไขที่กำหนดเป็น `true` +ส่วนใหญ่แล้ว OR `||` จะใช้ในคำสั่ง `if` เพื่อตรวจว่ามีเงื่อนไขใดเป็น `true` บ้าง ตัวอย่างเช่น: @@ -49,42 +49,42 @@ let hour = 9; *!* if (hour < 10 || hour > 18) { */!* - alert( 'The office is closed.' ); + alert('ออฟฟิศปิดแล้ว'); } ``` -เราสามารถใส่เงื่อนไขเพิ่มเติมได้: +เราสามารถเพิ่มเงื่อนไขได้อีก: ```js run let hour = 12; let isWeekend = true; if (hour < 10 || hour > 18 || isWeekend) { - alert( 'The office is closed.' ); // วันหยุดสุดสัปดาห์ + alert('ออฟฟิศปิดแล้ว'); // เพราะเป็นวันหยุดสุดสัปดาห์ } ``` -## || "OR" หาค่า truthy ตัวแรก [#or-finds-the-first-truthy-value] +## OR "||" จะหาค่า truthy ตัวแรก -ตรรกะที่อธิบายไว้ข้างต้นค่อนข้างคลาสสิก ตอนนี้เรามาดู "ฟีเจอร์พิเศษ" ของ JavaScript กัน +ตรรกะที่อธิบายไว้ข้างต้นค่อนข้างเป็นแบบดั้งเดิม ทีนี้มาดูความสามารถ "พิเศษ" ของ JavaScript กัน -อัลกอริทึมที่ขยายแล้วทำงานดังนี้ +อัลกอริทึมที่ขยายออกไปจะทำงานดังนี้ -กำหนดค่า OR หลายค่า: +เมื่อมีหลายค่ามา OR กัน: ```js result = value1 || value2 || value3; ``` -ตัวดำเนินการ OR `||` ทำสิ่งต่อไปนี้: +ตัวดำเนินการ OR `||` จะทำดังนี้: -- ประเมินตัวดำเนินการจากซ้ายไปขวา -- สำหรับตัวดำเนินการแต่ละตัวแปลงเป็นแบบบูลีน ถ้าผลลัพธ์เป็น `true` ให้หยุดและส่งคืนค่าเดิมของตัวดำเนินการนั้น -- ถ้าประเมินตัวดำเนินการทั้งหมดแล้ว (ทั้งหมดเป็น `false`) จะส่งคืนตัวดำเนินการสุดท้าย +- ประเมิน operand จากซ้ายไปขวา +- สำหรับแต่ละ operand จะแปลงเป็น boolean ถ้าผลลัพธ์เป็น `true` จะหยุดและคืนค่าเดิมของ operand นั้นทันที +- ถ้าประเมิน operand ทั้งหมดแล้ว (คือทุกตัวเป็น `false`) จะคืนค่า operand ตัวสุดท้าย -ค่าจะถูกส่งคืนในรูปแบบเดิมโดยไม่เปลี่ยนแปลง +จะคืนค่าในรูปแบบเดิม โดยไม่มีการแปลงใดๆ -กล่าวอีกนัยหนึ่ง OR `||` ช่วงหนึ่งจะส่งคืนค่า truthy ตัวแรก หรือตัวสุดท้ายถ้าไม่พบค่า truthy +อีกนัยหนึ่ง การเชื่อม OR `||` หลายตัวจะคืนค่า truthy ตัวแรกเสมอ หรือถ้าไม่เจอค่า truthy เลยจะคืนตัวสุดท้าย ตัวอย่างเช่น: @@ -94,57 +94,57 @@ alert( 1 || 0 ); // 1 (1 เป็น truthy) alert( null || 1 ); // 1 (1 เป็นค่า truthy ตัวแรก) alert( null || 0 || 1 ); // 1 (ค่า truthy ตัวแรก) -alert( undefined || null || 0 ); // 0 (ทั้งหมดเป็น falsy ส่งคืนค่าสุดท้าย) +alert( undefined || null || 0 ); // 0 (ทั้งหมดเป็น falsy จึงคืนค่าตัวสุดท้าย) ``` -สิ่งนี้นำไปสู่การใช้งานที่น่าสนใจเมื่อเทียบกับ "OR แบบคลาสสิก แบบบูลีนเท่านั้น" +พฤติกรรมนี้ทำให้มีวิธีใช้ OR ที่น่าสนใจเมื่อเทียบกับการใช้ OR แบบบูลีนอย่างเดียวทั่วไป -1. แสดงค่า truthy ตัวแรกจากรายการตัวแปรหรือนิพจน์ +1. **การหาค่า truthy ตัวแรกจาก list ของตัวแปรหรือนิพจน์ต่างๆ** -ยกตัวอย่างเช่น เราต้องการเลือกแสดงชื่อของผู้ใช้โดยใช้ตัวแปร `firstName`, `lastName`, `nickName` ซึ่งตัวแปรเหล่านี้อาจจะไม่มีข้อมูล (undefined) หรือมีค่าที่ไม่ใช่จริง (falsy) + เช่น สมมติเรามีตัวแปร `firstName`, `lastName` และ `nickName` ซึ่งล้วนแต่เป็นออปชันที่อาจจะเป็น undefined หรือมีค่า falsy ได้ -เราสามารถใช้ตัวดำเนินการ OR `||` เพื่อเลือกค่าแรกที่มีข้อมูลและแสดงผล (หรือแสดง `"คนไร้นาม"` ถ้าไม่มีข้อมูลเลย) + เราสามารถใช้ OR `||` เพื่อเลือกเอาค่าที่เซ็ตข้อมูลแล้วมาแสดง (หรือถ้าไม่มีจะแสดง `"Anonymous"`): -```js run -let firstName = ""; -let lastName = ""; -let nickName = "SuperCoder"; - -*!* -alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder -*/!* -``` - -ถ้าตัวแปรทั้งหมดเป็น falsy โปรแกรมจะแสดง `"Anonymous"` + ```js run + let firstName = ""; + let lastName = ""; + let nickName = "SuperCoder"; -2. การประเมินแบบลัดวงจร (Short-circuit evaluation) + *!* + alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder + */!* + ``` -อีกหนึ่งฟีเจอร์ของตัวดำเนินการ OR `||` คือการประเมินแบบลัดวงจร (short-circuit) + ถ้าทุกตัวแปรเป็น falsy ก็จะแสดง `"Anonymous"` -หมายความว่า `||` จะประมวลผลอาร์กิวเมนต์ของตัวเองจนกว่าจะเจอค่า truthy ตัวแรก จากนั้นจะส่งคืนค่านั้นทันที โดยไม่แตะอาร์กิวเมนต์อื่นเลย +2. **การประเมินแบบตัดตอนสั้น (short-circuit)** -ความสำคัญของฟีเจอร์นี้จะชัดเจนขึ้นหากตัวถูกดำเนินการไม่ใช่แค่ค่า แต่เป็นนิพจน์ที่มีผลข้างเคียง เช่น การกำหนดตัวแปรหรือการเรียกใช้ฟังก์ชัน + อีกความสามารถของตัวดำเนินการ OR `||` คือเรียกว่า "short-circuit evaluation" + + มันหมายความว่า `||` จะประมวลผลอาร์กิวเมนต์จนกว่าจะเจอค่า truthy ตัวแรก แล้วจะคืนค่านั้นทันทีเลย โดยไม่แตะอาร์กิวเมนต์ตัวถัดไปเลย -ในตัวอย่างด้านล่าง มีเพียงข้อความที่สองเท่านั้นที่ถูกพิมพ์: + ความสำคัญของความสามารถนี้จะเห็นได้ชัดเจน ถ้า operand ไม่ใช่แค่ค่าธรรมดา แต่เป็นนิพจน์ที่มี side-effect อย่างเช่นการกำหนดค่าตัวแปรหรือเรียกใช้ฟังก์ชัน - ```js run no-beautify - *!*true*/!* || alert("ไม่ถูกพิมพ์"); - *!*false*/!* || alert("ถูกพิมพ์"); - ``` + ในตัวอย่างด้านล่าง จะพิมพ์แค่ข้อความที่สองเท่านั้น: + + ```js run no-beautify + *!*true*/!* || alert("จะไม่พิมพ์"); + *!*false*/!* || alert("จะพิมพ์"); + ``` -ในบรรทัดแรก ตัวดำเนินการ OR `||` จะหยุดการประเมินทันทีที่เห็น `true` ดังนั้น `alert` จึงไม่ทำงาน + ในบรรทัดแรก ตัว OR `||` จะหยุดประเมินทันทีที่เจอ `true` ดังนั้น `alert` จึงไม่ถูกเรียก -บางครั้ง ผู้ใช้ฟีเจอร์นี้เพื่อสั่งให้รันคำสั่งเฉพาะในกรณีที่เงื่อนไขในส่วนซ้ายเป็น falsy + บางครั้งก็มีคนใช้ความสามารถนี้เพื่อรันคำสั่งก็ต่อเมื่อเงื่อนไขทางซ้ายมือเป็น falsy ด้วย ## && (AND) -ตัวดำเนินการ AND แสดงด้วยเครื่องหมาย ampersand สองตัว `&&`: +เครื่องหมาย AND ใช้สัญลักษณ์ & สองตัวติดกัน `&&` แบบนี้: ```js result = a && b; ``` -ในการเขียนโปรแกรมแบบดั้งเดิม AND จะส่งคืน `true` ถ้าทั้งสองตัวถูกดำเนินการเป็น truthy มิฉะนั้นจะส่งคืน `false` : +โดยปกติในการเขียนโปรแกรม AND จะคืนค่า `true` ถ้า operand ทั้งสองเป็น truthy และคืน `false` ในกรณีอื่นๆ: ```js run alert( true && true ); // true @@ -153,115 +153,113 @@ alert( true && false ); // false alert( false && false ); // false ``` -ตัวอย่างกับ `if`: +ตัวอย่างการใช้กับ `if`: ```js run let hour = 12; let minute = 30; if (hour == 12 && minute == 30) { - alert( 'เวลานี้คือ 12:30' ); -} + alert( 'ขณะนี้เวลา 12:30 น.' ); +} ``` -เช่นเดียวกับ OR ค่าใดๆ ก็สามารถใช้เป็นตัวถูกดำเนินการของ AND: +เหมือนกับ OR นั่นคือ operand ของ AND จะเป็นค่าอะไรก็ได้: ```js run if (1 && 0) { // ประเมินเป็น true && false - alert( "ไม่ทำงาน เพราะผลลัพธ์เป็น falsy" ); + alert( "จะไม่ทำงาน เพราะผลลัพธ์เป็น falsy" ); } ``` +## AND "&&" จะหาค่า falsy ตัวแรก -## AND "&&" หาค่า falsy ตัวแรก - -กำหนดค่า AND หลายค่า: +ถ้ามีหลายค่ามา AND กัน: ```js result = value1 && value2 && value3; ``` -ตัวดำเนินการ AND `&&` ทำสิ่งต่อไปนี้: +ตัวดำเนินการ AND `&&` จะทำงานดังนี้: -- ประเมินตัวดำเนินการจากซ้ายไปขวา -- สำหรับตัวดำเนินการแต่ละตัวแปลงเป็นแบบบูลีน ถ้าผลลัพธ์เป็น `false` ให้หยุดและส่งคืนค่าเดิมของตัวดำเนินการนั้น -- ถ้าประเมินตัวดำเนินการทั้งหมดแล้ว (ทั้งหมดเป็น truthy) จะส่งคืนตัวดำเนินการสุดท้าย +- ประเมิน operand จากซ้ายไปขวา +- สำหรับแต่ละ operand จะแปลงเป็น boolean ถ้าผลลัพธ์เป็น `false` จะหยุดและคืนค่าเดิมของ operand นั้นทันที +- ถ้าประเมิน operand ทั้งหมดแล้ว (คือทุกตัวเป็น truthy) จะคืนค่า operand ตัวสุดท้าย -กล่าวอีกนัยหนึ่ง AND จะส่งคืนค่า falsy ตัวแรก หรือตัวสุดท้ายถ้าไม่พบค่า falsy +อีกนัยหนึ่ง AND จะคืนค่า falsy ตัวแรกที่เจอ หรือถ้าไม่เจอเลยจะคืนค่าตัวสุดท้าย -กฎข้างต้นคล้ายกับ OR ความแตกต่างคือ AND ส่งคืนค่า *falsy* ตัวแรก ในขณะที่ OR ส่งคืนค่า *truthy* ตัวแรก +กฎข้างต้นคล้ายกับ OR ต่างกันตรงที่ AND คืนค่า *falsy* ตัวแรก ส่วน OR คืนค่า *truthy* ตัวแรก ตัวอย่าง: ```js run -// ถ้าตัวดำเนินการตัวแรกเป็น truthy -// AND จะส่งคืนตัวดำเนินการที่สอง: +// ถ้า operand ตัวแรกเป็น truthy +// AND จะคืนค่า operand ตัวที่สอง: alert( 1 && 0 ); // 0 alert( 1 && 5 ); // 5 -// ถ้าตัวดำเนินการตัวแรกเป็น falsy -// AND จะส่งคืนตัวนั้น ตัวดำเนินการตัวที่สองจะถูกละเว้น +// ถ้า operand ตัวแรกเป็น falsy +// AND จะคืนค่านั้นเลย โดยไม่สนใจ operand ตัวที่สอง alert( null && 5 ); // null -alert( 0 && "no matter what" ); // 0 +alert( 0 && "ไม่สำคัญ" ); // 0 ``` -เราสามารถส่งค่าหลายค่าติดต่อกันได้ ดูว่าค่า falsy ตัวแรกถูกส่งคืนอย่างไร: +เราสามารถส่งหลายค่าต่อกันได้ ลองดูว่าค่า falsy ตัวแรกจะถูกคืนออกมา: ```js run alert( 1 && 2 && null && 3 ); // null ``` -เมื่อค่าทั้งหมดเป็น truthy ค่าสุดท้ายจะถูกส่งคืน: +ถ้าค่าทั้งหมดเป็น truthy ค่าตัวสุดท้ายจะถูกคืน: ```js run -alert( 1 && 2 && 3 ); // 3, the last one +alert( 1 && 2 && 3 ); // 3, ตัวสุดท้าย ``` -````smart header="ลำดับความสำคัญของ AND `&&` สูงกว่า OR `||`" -ลำดับความสำคัญของตัวดำเนินการ AND `&&` สูงกว่า OR `||`. +````smart header="AND `&&` มีลำดับความสำคัญสูงกว่า OR `||`" +ตัวดำเนินการ AND `&&` จะมี precedence สูงกว่า OR `||` -ดังนั้นโค้ด `a && b || c && d` จริงๆแล้วเหมือนกับว่านิพจน์ `&&` อยู่ในวงเล็บ: `(a && b) || (c && d)`. +ดังนั้นโค้ด `a && b || c && d` ก็เหมือนกับการเขียนนิพจน์ `&&` อยู่ในวงเล็บ: `(a && b) || (c && d)` ```` -````warn header="อย่าแทนที่ `if` ด้วย `||` หรือ `&&`" -บางครั้ง ผู้ใช้ตัวดำเนินการ AND `&&` เป็น "วิธีที่สั้นกว่าในการเขียน `if`". +````warn header="อย่าใช้ `||` หรือ `&&` แทนคำสั่ง `if`" +บางครั้งมีคนใช้ตัวดำเนินการ AND `&&` เพื่อ "เขียน `if` ให้สั้นลง" -ตัวอย่างเช่น: +เช่น: ```js run let x = 1; -(x > 0) && alert( 'Greater than zero!' ); - +(x > 0) && alert( 'มากกว่าศูนย์!' ); +``` -การกระทำทางด้านขวาของ `&&` จะดำเนินการเฉพาะในกรณีที่การประเมินไปถึงเท่านั้น นั่นคือ ในกรณีที่ `(x > 0)` เป็นจริง +การกระทำทางขวาของ `&&` จะรันก็ต่อเมื่อการประเมินทำไปถึงจุดนั้น ซึ่งก็คือเมื่อ `(x > 0)` เป็นจริง -ดังนั้น โดยทั่วไป `&&` สามารถใช้แทน `if` ได้ แต่ไม่ใช่เสมอไป ดังนี้: +ดังนั้นมันจะเหมือนกับการเขียนแบบนี้: -js run +```js run let x = 1; -if (x > 0) alert( 'Greater than zero!' ); - +if (x > 0) alert( 'มากกว่าศูนย์!' ); +``` -แม้ว่าตัวแปร `&&` จะสั้นกว่า `if` แต่ `if` ก็ชัดเจนกว่าและอ่านง่ายกว่าเล็กน้อย ดังนั้นเราขอแนะนำให้ใช้แต่ละโครงสร้างตามวัตถุประสงค์: ใช้ `if` ถ้าเราต้องการ `if` และใช้ `&&` ถ้าเราต้องการ AND +ถึงแม้การใช้ `&&` จะดูสั้นกว่า แต่ `if` จะชัดเจนและอ่านเข้าใจง่ายกว่า แนะนำให้ใช้แต่ละแบบตามวัตถุประสงค์ที่มันถูกสร้างมา ใช้ `if` ถ้าต้องการเงื่อนไข if และใช้ `&&` ถ้าต้องการตรรกะ AND ```` - ## ! (NOT) -ตัวดำเนินการ NOT แบบบูลีนแสดงด้วยเครื่องหมายอัศเจรีย์ `!` +ตัวดำเนินการตรรกะ NOT ใช้สัญลักษณ์อัศเจรีย์ `!` -ไวยากรณ์ค่อนข้างง่าย: +รูปแบบการใช้งานค่อนข้างง่าย: ```js result = !value; ``` -ตัวดำเนินการรับอาร์กิวเมนต์ตัวเดียวและทำสิ่งต่อไปนี้: +โดยตัวดำเนินการจะรับ operand เดียว และทำงานดังนี้: -1. แปลงตัวถูกดำเนินการเป็นประเภทบูลีน: `true/false` -2. ส่งคืนค่าผกผัน +1. แปลง operand ให้เป็นประเภท boolean: `true/false` +2. คืนค่าตรงข้าม (invert) ตัวอย่างเช่น: @@ -270,20 +268,20 @@ alert( !true ); // false alert( !0 ); // true ``` -บางครั้งใช้ NOT `!!` ซ้อนกันเพื่อแปลงค่าเป็นประเภทบูลีน: +การใช้ NOT สองตัวติดกัน `!!` บางครั้งใช้เพื่อแปลงค่าให้เป็นประเภท boolean: ```js run alert( !!"non-empty string" ); // true -alert( !!null ); // false +alert( !!null ); // false ``` -นั่นคือ NOT ตัวแรกแปลงค่าเป็นบูลีนและส่งคืนค่าผกผัน และ NOT ตัวที่สองทำให้ผกผันอีกครั้ง สุดท้าย เรามีการแปลงค่าเป็นบูลีนอย่างง่าย +นั่นคือ NOT ตัวแรกจะแปลงค่าเป็น boolean และคืนค่าตรงข้าม จากนั้น NOT ตัวที่สองก็จะ invert อีกครั้ง ในท้ายที่สุดเราจะได้การแปลงค่าเป็น boolean อย่างง่ายๆ -มีวิธีที่ยาวกว่าเล็กน้อยในการทำสิ่งเดียวกัน -- ฟังก์ชัน `Boolean` ที่มีมาให้: +มีอีกวิธีที่อ่านเข้าใจง่ายกว่าในการทำแบบเดียวกัน คือใช้ฟังก์ชันในตัว `Boolean`: ```js run alert( Boolean("non-empty string") ); // true alert( Boolean(null) ); // false ``` -ลำดับความสำคัญของ NOT `!` สูงที่สุดในบรรดาตัวดำเนินการตรรกะทั้งหมด ดังนั้นมันจะทำงานก่อนเสมอ ก่อน `&&` หรือ `||` +ตัวดำเนินการ NOT `!` จะมีลำดับความสำคัญ (precedence) สูงสุดในบรรดาตัวดำเนินการตรรกะทั้งหมด ดังนั้นมันจะถูกประมวลผลก่อนเสมอ ก่อนที่จะไปประมวลผล `&&` หรือ `||` \ No newline at end of file