เทคนิคลัด JavaScript ที่คุณควรรู้! (ตอนที่ 1)
กฎเกณฑ์บนโลกนี้บางอย่างก็มีสูตรตายตัวแบบสูตรคำนวน ส.ส. ปาร์ตี้ลิสต์ ที่แม้จะมีสูตรเดียวแต่คำนวณนานประหนึ่งแก้อินทิเกรตสามชั้น
สำหรับบางสิ่งก็ไม่มีกฎตายตัวในการแก้ปัญหา JavaScript ก็เช่นกัน หลากหลายวิธีที่ใช้โค้ดย่อมนำไปสู่การแก้ปัญหาเดียวกันได้ แต่บางครั้งเราก็มีสูตรลัดนะ StackOverflow แอนด์ Copy-Paste ไงละปัดโถ่!
ตัดเศษทิ้งด้วย ~~
สมมติเรามีตัวเลข 21.16
แล้วต้องการตัดเศษทิ้งเหลือ 21
วิธีง่ายสุดคือการใส่ ~~
นำหน้า เช่น ~~21.16
จะได้ค่าเป็น 21
แล้วหละ! แม้จะง่ายแต่ก็ดูขำ ๆ อย่าทะลึ่งไปเขียนซะทุกที่ มันอ่านไม่รู้เรื่อง ไอ้หอยขม!
ตัวเครื่องหมาย ~
เป็นหนึ่งในกลุ่มเครื่องหมายที่เราเรียกว่า Bitwise Operator
โดยเงื่อนไขของการใช้ ~~
จะต้องเป็นตัวเลขที่อยู่ระหว่าง และ ไม่เช่นนั้นจะเกิด overflow และให้ผลลัพธ์ที่ไม่ถูกต้อง
วิธีทางเลือกที่ดีกว่าคือการใช้ฟังก์ชัน Math.trunc
เช่น Math.truc(21.16)
ที่จะได้ผลลัพธ์เป็น 21 เช่นกัน
กำจัดตัวซ้ำจากอาร์เรย์ด้วย Set
สมมติเรามีอาร์เรย์ที่ของข้างในมีค่าซ้ำกันอยู่ เช่น [1, 1, 2, 3, 2, 4, 5]
จะกำจัดให้เหลือเฉพาะค่าไม่ซ้ำแบบนี้ [1, 2, 3, 4, 5]
ได้อย่างไร?
ยาวไปไม่อ่าน (TL;DR)
สมมติอาร์เรย์ที่มีค่าซ้ำอยู่ในตัวแปรชื่อ arr
ให้เราใช้สูตรนี้คือจบ
1;[...new Set(arr)]
เช่น ต้องการลบค่าซ้ำจาก [1, 1, 2, 3, 2, 4, 5]
สามารถทำได้ดังนี้
1;[...new Set([1, 1, 2, 3, 2, 4, 5])] // [1, 2, 3, 4, 5]
คำอธิบาย
ในทางคณิตศาสตร์ เซต หมายถึงกลุ่มของสมาชิกที่สามารถนิยามสมาชิกได้อย่างชัดเจน โดยสมาชิกภายในเซตใด ๆ ย่อมไม่ซ้ำกัน
เพราะความที่เซตนั้นมีสมาชิกไม่ซ้ำ เราจึงจัดการโยนอาร์เรย์เข้าไปในคอนสตรัคเตอร์ของเซต เพื่อกำจัดค่าซ้ำออกไป จากนั้นจึงทำการกระจายออกด้วย spread operator เพื่อให้กลับมาเป็นอาร์เรย์อีกครั้ง
range อย่างง่ายด้วย Array
โจทย์ข้อนี้เราจะลองสร้างฟังก์ชัน range กัน เพื่อผลิตอาร์เรย์ของตัวเลขจากค่าหนึ่งไปสู่ค่าหนึ่ง เช่น range(1, 5)
ให้ผลลัพธ์เป็น [1, 2, 3, 4, 5]
ยาวไปไม่อ่าน (TL;DR)
1function range(from, to) {2 return Array.from({ length: to - from + 1 }, (_v, i) => i + from)3}45// range(1, 5) => [1, 2, 3, 4, 5]
คำอธิบาย
เรามาเริ่มสูตรลัพธ์ในการสร้างอาร์เรย์จากเลข 0 ถึง n เอาไปสองสูตร ดังนี้
1// สูตรแรก2;[...Array(n).keys()]34// สูตรที่สอง5Array.from({ length: n }, (_v, i) => i)
ตัวอย่างเช่น ต้องการสร้างอาร์เรย์ [0, 1, 2, 3, 4] ที่เริ่มต้นจาก 0 และมีทั้งหมด 5 จำนวน จากทั้งสองสูตรสามารถแก้ปัญหาข้างต้นได้ ดังนี้
1// สูตรแรก2;[...Array(5).keys()]34// สูตรสอง5Array.from({ length: 5 }, (_v, i) => i)
คราวนี้เราจะขยายความสามารถจากเดิมที่เลขเริ่มจาก 0 เป็นเริ่มจากค่า from จนถึงค่า to จากสองสูตรข้างต้นผนวกความสามารถใหม่เข้าไปได้เป็น
1// สูตรแรก2;[...Array(to - from + 1).keys()].map((v) => v + x)34// สูตรสอง5Array.from({ length: to - from + 1 }, (_v, i) => i + from)
ตัวอย่างเช่น ต้องการสร้างอาร์เรย์จากเลข 10 ถึง 20 สามารถใช้สองสูตรได้ ดังนี้
1// 11 มาจาก 20 - 11 + 12;[...Array(11).keys()].map((v) => v + 10)34// หรือ5// Array.from({ length: 11 }, (_v, i) => i + 10)
กลับมาที่โจทย์ของเราที่ต้องการสร้างฟังก์ชัน range
ให้ระบุค่าเริ่มต้นและค่าสิ้นสุดได้ อาศัยความสัมพันธ์จากสูตรที่สองข้างต้น จึงเกิดเป็นฟังก์ชัน range
หน้าตาแบบนี้
1function range(from, to) {2 return Array.from({ length: to - from + 1 }, (_v, i) => i + from)3}45// range(1, 5) => [1, 2, 3, 4, 5]
เอาหละถึงเวลาอธิบายความหมายแล้ว ในที่นี้จะอธิบายความหมายเฉพาะสูตรที่สองนะครับ
สำหรับ Array เมธอด from
สามารถใช้เพื่อสร้างอาร์เรย์ใหม่ได้ โดยค่าแรกที่ส่งไปนั้นจะต้องเป็นออบเจ็กต์ประเภท array-like / iterable
ความที่อาร์เรย์ก็เป็นออบเจ็กต์ เราจึงสามารถเสกสร้างออบเจ็กต์ให้เหมือนอาร์เรย์ได้ เช่น ['A', 'B', 'C']
นั้นเป็นอาร์เรย์ความยาวสามช่อง ช่องที่ 0 หรือ [0] มีค่าเป็น A ช่องที่ [1] มีค่าเป็น B และช่องสุดท้ายคือ [2] มีค่าเป็น C เมื่อเราแปลงความสัมพันธ์นี้เป็นออบเจ็กต์จึงได้ว่า ['A', 'B', 'C']
คล้ายกับออบเจ็กต์นี้
1{2 0: 'A',3 1: 'B',4 2: 'C'5 length: 36}
และนี่หละคือสื่งที่เราเรียกว่าเป็นออบเจ็กต์ประเภท array-like
การที่เราใช้ฟังก์ชัน from
ด้วยการระบุ length
เข้าไปจึงเป็นการจำลอง array-like ที่มีค่าจำนวนตามที่ระบุ และเหตุที่เราไม่ได้แจกแจงว่าแต่ละช่องมีค่าเป็นอะไร ทุกช่องจึงมีค่าเป็น undefined
แทน
1Array.from({ length: 3 }) // [undefined, undefined, undefined]
ค่าที่สองของ from
เราสามารถส่ง map function
อันเป็นฟังก์ชันที่จะถูกเรียกทุกครั้งที่สร้างค่าแต่ละช่องในอาร์เรย์ ฟังก์ชันนี้จะรับค่าของแต่ละช่องเข้ามา เช่นจากตัวอย่างข้างต้นค่าของแต่ละช่องจะเป็น undefined นั่นเอง
ตามธรรมชาติของฟังก์ชันที่ชื่อว่า map
เราใช้เพื่อแปลงค่าของอาร์เรย์ในแต่ละช่องให้เป็นอีกค่าหนึ่ง แต่เนื่องจากทุกช่องของเราเป็น undefined เราจึงต้องอาศัยพารามิเตอร์ตัวที่สองของ map
คือค่า index มาช่วย
1// _v เป็นค่าแต่ละค่าในแต่ละช่อง นั่นคือ undefined2// i คือค่า index มีค่าเป็น 0, 1, 2 ตามลำดับ3Array.from({ length: 3 }, (_v, i) => i) // [0, 1, 2]
และนี่คือหลักการณ์ที่อยู่เบื้องหลังการสร้างฟังก์ชัน range
ของเราครับ
แกะ Query String ด้วย URLSearchParams
หากเรามี Query String ใน URL ของเราดังนี้ '?search=prayut&limit=44'
เมื่อเราต้องการทราบว่า search
นั้นมีค่าเป็นอะไรจะสามารถแกะค่าข้อมูลนี้ออกมาได้อย่างไร?
ถ้าเราใช้งาน lib อย่าง query-string ชีวิตก็จะง่ายขึ้น นั่นเพราะเราสามารถดึงแต่ละค่าที่ต้องการออกมาได้อย่างง่ายได้ แต่หากเราไม่ได้แคร์เบราเซอร์เจ้าปัญหาอย่าง IE ทางเลือกของเราก็มีอีกทางคือการใช้ URLSearchParams
URLSearchParams มีคอนสตรัคเตอร์ให้เราส่งข้อความของ query string ได้ ออบเจ็กต์จาก URLSearchParams จะมีเมธอดต่าง ๆ ให้เราสามารถจัดการเกี่ยวกับก้อน query string นี้ เช่น เมธอด get
ที่ใช้สำหรับหาค่าของพารามิเตอร์ที่เราระบุ
1const queryString = '?search=prayut&limit=44'2const params = new URLSearchParams(queryString)34params.get('search') // prayut
เจอชื่อนี้ ตัดจบเลยแล้วกัน ตัวใครตัวมันน้อ~
สารบัญ
- ตัดเศษทิ้งด้วย ~~
- กำจัดตัวซ้ำจากอาร์เรย์ด้วย Set
- range อย่างง่ายด้วย Array
- แกะ Query String ด้วย URLSearchParams