อ็อบเจ็กต์ `Array`
บทความนี้อธิบายเกี่ยวกับอ็อบเจ็กต์ Array
ฉันจะอธิบายการใช้งานจริงของอาเรย์ทีละขั้นตอนในรูปแบบที่เข้าใจง่าย
YouTube Video
อ็อบเจ็กต์ Array
อ็อบเจ็กต์ Array ของ JavaScript เป็นหนึ่งในโครงสร้างที่สำคัญที่สุดที่เป็นพื้นฐานของกระบวนการจัดการข้อมูลทุกประเภท ตั้งแต่การใช้งานอาเรย์ขั้นพื้นฐานจนถึงฟังก์ชันขั้นสูงที่ช่วยในการแปลงข้อมูลอย่างมีประสิทธิภาพ มีฟีเจอร์มากมายที่คุณควรรู้
พื้นฐานของอาเรย์
ใน JavaScript อาเรย์เป็นโครงสร้างข้อมูลพื้นฐานที่ใช้จัดการกับค่าหลายค่าในคราวเดียว ที่นี่เราจะแนะนำวิธีสร้างอาเรย์ และการอ่านและเขียนสมาชิกของอาเรย์ด้วยตัวอย่างที่เข้าใจง่าย
1// Create arrays in different ways
2const arr1 = [1, 2, 3]; // array literal
3const arr2 = new Array(4, 5, 6); // Array constructor
4const arr3 = Array.of(7, 8, 9); // Array.of
5
6console.log("arr1 created with literal. :", arr1);
7console.log("arr2 created with constructor:", arr2);
8console.log("arr3 created with Array.of. :", arr3);
9// arr1 created with literal. : [ 1, 2, 3 ]
10// arr2 created with constructor: [ 4, 5, 6 ]
11// arr3 created with Array.of. : [ 7, 8, 9 ]
12
13// Access and modify elements
14let first = arr1[0]; // read element
15console.log("First element of arr1:", first);
16// First element of arr1: 1
17
18arr1[1] = 20; // modify element
19console.log("arr1 after modifying index 1:", arr1);
20// arr1 after modifying index 1: [ 1, 20, 3 ]
21
22const len = arr1.length; // get length
23console.log("Length of arr1:", len);
24// Length of arr1: 3
- โค้ดนี้แสดงวิธีสร้างอาเรย์ 3 วิธี วิธีอ่านและอัปเดตสมาชิกด้วยดัชนี และวิธีตรวจสอบความยาวด้วยพร็อพเพอร์ตี้
length - ลิเทอรัลของอาเรย์เป็นรูปแบบที่พบได้บ่อยที่สุดและอ่านเข้าใจง่ายที่สุด และถูกใช้บ่อยครั้งในสถานการณ์ประจำวัน
การเพิ่มและลบสมาชิก (ที่ส่วนท้ายหรือจุดเริ่มต้นของอาเรย์)
อาเรย์ช่วยให้คุณเพิ่มหรือลบสมาชิกที่ส่วนท้ายหรือส่วนแรกได้อย่างง่ายดาย การดำเนินการเหล่านี้มีประโยชน์อย่างยิ่งเมื่อใช้งานกับโครงสร้างข้อมูลประเภทสแตกหรือคิว
1// Push and pop (stack-like)
2const stack = [];
3console.log("Initial stack:", stack);
4
5stack.push(1); // push 1
6console.log("After push(1):", stack);
7
8stack.push(2); // push 2
9console.log("After push(2):", stack);
10
11const last = stack.pop(); // pop -> 2
12console.log("Popped value:", last);
13console.log("Stack after pop():", stack);
14
15// Unshift and shift (queue-like)
16const queue = [];
17console.log("Initial queue:", queue);
18
19queue.push('a'); // add to end
20console.log("After push('a'):", queue);
21
22queue.unshift('start'); // add to front
23console.log("After unshift('start'):", queue);
24
25const firstItem = queue.shift(); // remove from front
26console.log("Shifted value:", firstItem);
27console.log("Queue after shift():", queue);
28
29// Initial stack: []
30// After push(1): [ 1 ]
31// After push(2): [ 1, 2 ]
32// Popped value: 2
33// Stack after pop(): [ 1 ]
34
35// Initial queue: []
36// After push('a'): [ 'a' ]
37// After unshift('start'): [ 'start', 'a' ]
38// Shifted value: start
39// Queue after shift(): [ 'a' ]
pushและpopทำงานกับ ส่วนท้าย ของอาเรย์ เหมาะสำหรับการใช้งานสแตกunshiftและshiftทำงานกับ ส่วนต้น ของอาเรย์ แต่อย่าลืมว่าการดำเนินการที่จุดเริ่มต้นจะต้องเปลี่ยนดัชนีทั้งหมดของสมาชิกภายใน ส่งผลให้ใช้ทรัพยากรมาก
การจัดการสมาชิกตรงกลางของอาเรย์ (splice และ slice)
เมื่อต้องการจัดการสมาชิกตรงกลางของอาเรย์ เลือกใช้งานระหว่าง splice กับ slice ตามความต้องการว่าจะเปลี่ยนอาเรย์ต้นฉบับหรือไม่ ถ้าคุณแค่ต้องการดึงบางส่วนของอาเรย์ ให้ใช้ slice แต่ถ้าคุณต้องการแก้ไขอาเรย์โดยตรง เช่น การแทรกหรือการลบสมาชิก ให้ใช้ splice
1// slice (non-destructive)
2const nums = [0, 1, 2, 3, 4];
3console.log("Original nums:", nums);
4
5const part = nums.slice(1, 4); // returns [1, 2, 3]
6console.log("Result of nums.slice(1, 4):", part);
7console.log("nums after slice (unchanged):", nums);
8
9// splice (destructive)
10const arr = [10, 20, 30, 40];
11console.log("\nOriginal arr:", arr);
12
13// remove 1 item at index 2, insert 25 and 27
14arr.splice(2, 1, 25, 27);
15console.log("After arr.splice(2, 1, 25, 27):", arr);
16
17// Original nums: [ 0, 1, 2, 3, 4 ]
18// Result of nums.slice(1, 4): [ 1, 2, 3 ]
19// nums after slice (unchanged): [ 0, 1, 2, 3, 4 ]
20
21// Original arr: [ 10, 20, 30, 40 ]
22// After arr.splice(2, 1, 25, 27): [ 10, 20, 25, 27, 40 ]
sliceจะคัดลอกเฉพาะสมาชิกตามที่กำหนดและไม่เปลี่ยนอาเรย์ต้นฉบับspliceเพิ่มหรือลบสมาชิกและเปลี่ยนอาเรย์ต้นฉบับ ดังนั้นควรระวังเรื่องผลกระทบที่ตามมา
การวนซ้ำ (for / for...of / forEach)
มีหลายวิธีในการวนซ้ำอาเรย์ คุณสามารถเลือกใช้ได้ตามวัตถุประสงค์และสไตล์การเขียนโค้ด นี่คือตัวอย่างโครงสร้างลูปสามแบบที่พบบ่อย
1const items = ['apple', 'banana', 'cherry'];
2console.log("Items:", items);
3
4// classic for
5console.log("\n--- Classic for loop ---");
6for (let i = 0; i < items.length; i++) {
7 console.log(`Index: ${i}, Value: ${items[i]}`);
8}
9
10// for...of (recommended for values)
11console.log("\n--- for...of loop ---");
12for (const item of items) {
13 console.log(`Value: ${item}`);
14}
15
16// forEach (functional style)
17console.log("\n--- forEach loop ---");
18items.forEach((item, index) => {
19 console.log(`Index: ${index}, Value: ${item}`);
20});- ลูป
forมีความยืดหยุ่นสูงสุด สามารถใช้งานกับดัชนี และควบคุมการวนซ้ำได้ละเอียด เช่น การใช้break for...ofเป็นวิธีที่กระชับสำหรับการเข้าถึงสมาชิก เหมาะสำหรับการอ่านโค้ดforEachช่วยให้คุณเขียนโค้ดแบบ functional และเหมาะกับการทำงานแบบมีผลข้างเคียง เช่น การแสดงผลหรืออัปเดตข้อมูล แต่อย่างไรก็ตาม ไม่สามารถใช้breakหรือcontinueได้ และ ไม่เหมาะกับการทำงานแบบ asynchronous ร่วมกับawait
map / filter / reduce — ฟังก์ชันขั้นสูง
map filter และ reduce คือฟังก์ชันขั้นสูงที่นิยมใช้เมื่อต้องการแปลง กรอง หรือรวมข้อมูลของอาร์เรย์ เนื่องจากคุณสามารถแสดงขั้นตอนที่ซ้ำกันได้อย่างชัดเจน โค้ดของคุณจึงเรียบง่ายและเข้าใจง่าย
1const numbers = [1, 2, 3, 4, 5];
2console.log("Original numbers:", numbers);
3
4// map: transform each item
5const doubled = numbers.map(n => n * 2);
6console.log("\nResult of map (n * 2):", doubled);
7
8// filter: select items
9const evens = numbers.filter(n => n % 2 === 0);
10console.log("Result of filter (even numbers):", evens);
11
12// reduce: accumulate to single value
13const sum = numbers.reduce((acc, n) => acc + n, 0);
14console.log("Result of reduce (sum):", sum);
15
16// Original numbers: [ 1, 2, 3, 4, 5 ]
17// Result of map (n * 2): [ 2, 4, 6, 8, 10 ]
18// Result of filter (even numbers): [ 2, 4 ]
19// Result of reduce (sum): 15
- วิธีเหล่านี้ช่วยให้คุณเน้นสิ่งที่ต้องการทำแบบ declarative เพิ่มความชัดเจนและลดผลข้างเคียงในโค้ด
find / findIndex / some / every
ต่อไปนี้คือภาพรวมของเมธอดการค้นหาและตรวจสอบเงื่อนไข สิ่งเหล่านี้มีประโยชน์ในการค้นหาสมาชิกที่ตรงตามเงื่อนไข หรือการตรวจสอบเป็น boolean
1const users = [
2 { id: 1, name: 'Alice' },
3 { id: 2, name: 'Bob' },
4 { id: 3, name: 'Carol' }
5];
6
7console.log("Users:", users);
8
9// Find the first user whose name is 'Bob'
10const bob = users.find(user => user.name === 'Bob');
11console.log("\nResult of find (name === 'Bob'):", bob);
12
13// Find index of the user whose id is 3
14const indexOfId3 = users.findIndex(user => user.id === 3);
15console.log("Result of findIndex (id === 3):", indexOfId3);
16
17// Check if there exists a user with id = 2
18const hasId2 = users.some(user => user.id === 2);
19console.log("Result of some (id === 2):", hasId2);
20
21// Check if all users have a numeric id
22const allHaveNumericId = users.every(user => typeof user.id === 'number');
23console.log("Result of every (id is number):", allHaveNumericId);
24// Result of find (name === 'Bob'): { id: 2, name: 'Bob' }
25// Result of findIndex (id === 3): 2
26// Result of some (id === 2): true
27// Result of every (id is number): true
findคืนค่าสมาชิกแรกที่ตรงตามเงื่อนไขfindIndexคืนค่าดัชนีของสมาชิกที่ตรงตามเงื่อนไขsomeคืนค่าtrueหากมีอย่างน้อย 1 สมาชิกที่ตรงตามเงื่อนไขeveryคืนค่าtrueหากสมาชิกทั้งหมดตรงตามเงื่อนไข
เมธอดเหล่านี้มีประโยชน์มากในการจัดการอาเรย์ หากใช้ให้เหมาะสมจะทำให้โค้ดของคุณสั้นและชัดเจน
การเรียงลำดับและฟังก์ชันเปรียบเทียบ
อาเรย์ถูกเรียงลำดับด้วย sort แต่ค่าปริยายจะ เปรียบเทียบสมาชิกแบบสตริง ซึ่งอาจก่อให้เกิดผลลัพธ์ไม่คาดคิดเมื่อใช้กับตัวเลข
1const nums = [10, 2, 33, 4];
2console.log("Original nums:", nums);
3
4// Default sort: compares elements as strings (not suitable for numbers)
5nums.sort();
6console.log("\nAfter default sort (string comparison):", nums);
7
8// Numeric ascending sort using a compare function
9nums.sort((a, b) => a - b);
10console.log("After numeric sort (a - b):", nums);
11
12// Sort objects by a property
13const people = [{ age: 30 }, { age: 20 }, { age: 25 }];
14console.log("\nOriginal people:", people);
15
16people.sort((a, b) => a.age - b.age);
17console.log("After sorting people by age:", people);
18
19// After default sort (string comparison): [ 10, 2, 33, 4 ]
20// After numeric sort (a - b): [ 2, 4, 10, 33 ]
21// Original people: [ { age: 30 }, { age: 20 }, { age: 25 } ]
22// After sorting people by age: [ { age: 20 }, { age: 25 }, { age: 30 } ]
- เมื่อต้องการเรียงลำดับตัวเลขหรืออ็อบเจ็กต์ ควรกำหนด ฟังก์ชันเปรียบเทียบ ทุกครั้ง
- ในฟังก์ชันเปรียบเทียบ หากส่งคืนค่าติดลบจะให้
aอยู่ก่อนbส่งคืนค่าบวกจะให้bอยู่ก่อนaและส่งคืน 0 จะไม่เปลี่ยนลำดับ
การคัดลอกอาเรย์และความไม่เปลี่ยนแปลง
เมื่อคัดลอกอาร์เรย์ สิ่งสำคัญคือต้องเข้าใจความแตกต่างระหว่าง 'การคัดลอกแบบอ้างอิง' กับ 'การคัดลอกแบบตื้น' โดยเฉพาะอย่างยิ่ง ถ้ามีอ็อบเจ็กต์อยู่ในอาเรย์ การคัดลอกแบบตื้นจะทำให้อ็อบเจ็กต์ข้างในถูกร่วมใช้งาน
การคัดลอกแบบอ้างอิง
เมื่อคุณกำหนดอาร์เรย์ให้กับตัวแปรอื่น ตัวอาร์เรย์จะไม่ถูกก็อปปี้ใหม่ แต่จะก็อปปี้เฉพาะ 'การอ้างอิง' ที่ชี้ไปยังอาร์เรย์เดิมเท่านั้น
1const a = [1, 2, 3];
2console.log("Original a:", a);
3
4// Reference copy; modifying b also affects a
5const b = a;
6console.log("\nReference copy b = a:", b);
7// Reference copy b = a: [ 1, 2, 3 ]
8
9b[0] = 100;
10console.log("After modifying b[0] = 100:");
11console.log("a:", a); // a: [ 100, 2, 3 ] (affected)
12console.log("b:", b); // b: [ 100, 2, 3 ]
- สำหรับการคัดลอกแบบอ้างอิง หากคุณแก้ไขค่าของอาร์เรย์โดยใช้ตัวแปรที่ได้คัดลอก การเปลี่ยนแปลงนั้นจะเกิดขึ้นกับตัวแปรต้นฉบับที่อ้างอิงอาร์เรย์เดียวกันด้วย
การคัดลอกแบบตื้น
การใช้ slice() หรือ spread syntax จะสร้าง 'การคัดลอกแบบตื้น' เพราะจะคัดลอกเฉพาะค่า โดยอาร์เรย์ต้นฉบับและอาร์เรย์ที่คัดลอกจะถือเป็นออบเจ็กต์แยกกัน
1const a = [1, 2, 3];
2console.log("Original a:", a);
3
4// Shallow copy (new array)
5const b = a.slice();
6console.log("\nShallow copy using slice():", b);
7// Shallow copy using slice(): [ 100, 2, 3 ]
8
9const c = [...a];
10console.log("Shallow copy using spread [...a]:", c);
11// Shallow copy using spread [...a]: [ 100, 2, 3 ]
12
13// Modifying c or d does NOT affect a
14b[1] = 200;
15c[2] = 300;
16console.log("\nAfter modifying b[1] = 200 and c[2] = 300:");
17console.log("a:", a); // [ 100, 2, 3 ]
18console.log("b:", b); // [ 100, 200, 3 ]
19console.log("c:", c); // [ 100, 2, 300 ]
- โค้ดนี้แสดงให้เห็นว่า การคัดลอกแบบตื้นโดยใช้
slice()หรือ spread syntax จะไม่ส่งผลต่ออาร์เรย์ต้นฉบับ
การคัดลอกแบบตื้นและความไม่เปลี่ยนแปลง
แม้ว่าจะคัดลอกอาร์เรย์โดยใช้ 'การคัดลอกแบบตื้น' ก็อาจเกิดการแชร์ข้อมูลโดยไม่ตั้งใจได้หากอาร์เรย์นั้นมีอ็อบเจ็กต์ภายใน
1// Shallow copy doesn't clone inner objects
2const nested = [{ x: 1 }, { x: 2 }];
3console.log("\nOriginal nested:", nested);
4// Original nested: [ { x: 1 }, { x: 2 } ]
5
6const shallow = nested.slice();
7console.log("Shallow copy of nested:", shallow);
8// Shallow copy of nested: [ { x: 1 }, { x: 2 } ]
9
10// Changing inner object affects both arrays
11shallow[0].x = 99;
12console.log("\nAfter shallow[0].x = 99:");
13console.log("nested:", nested);
14console.log("shallow:", shallow);
15// nested: [ { x: 99 }, { x: 2 } ]
16// shallow: [ { x: 99 }, { x: 2 } ]
- โค้ดนี้แสดงให้เห็นว่า เมื่อคัดลอกแบบตื้น อ็อบเจ็กต์ภายในจะถูกแชร์ ดังนั้นการแก้ไขอ็อบเจ็กต์ภายในนั้นจะมีผลทั้งกับอาร์เรย์ต้นฉบับและอาร์เรย์ที่คัดลอก
- หากต้องการข้อมูลที่แยกจากกันจริง ๆ ต้องใช้ 'deep copy' อย่างเช่น
structuredClone()หรือแปลงผ่าน JSON
เมธอดยูทิลิตี้ที่มีประโยชน์
ต่อไปนี้คือเมธอดเสริมที่มักใช้งานกับอาเรย์ หากเลือกใช้ให้เหมาะสมกับจุดประสงค์ จะทำให้โค้ดสั้นและอ่านง่ายขึ้น
includes
เมธอด includes ใช้ตรวจสอบว่าค่าเฉพาะมีอยู่ในอาร์เรย์หรือไม่
1// includes: check if array contains a value
2const letters = ['a', 'b', 'c'];
3const hasB = letters.includes('b');
4console.log("letters:", letters); // [ 'a', 'b', 'c' ]
5console.log("letters.includes('b'):", hasB); // true
- ในโค้ดนี้จะใช้เมธอด
includesเพื่อตรวจสอบอย่างกระชับว่าค่าที่ต้องการมีอยู่ในอาร์เรย์หรือไม่
concat
เมธอด concat จะคืนค่าอาร์เรย์ใหม่โดยเพิ่มอาร์เรย์หรือค่าที่กำหนดไว้ต่อท้าย โดยที่อาร์เรย์ต้นฉบับไม่ถูกเปลี่ยนแปลง
1// concat: merge arrays without mutation
2const a1 = [1, 2];
3const a2 = [3, 4];
4const combined = a1.concat(a2);
5console.log("a1.concat(a2):", combined); // [ 1, 2, 3, 4 ]
6console.log("a1(unchanged):", a1); // [ 1, 2 ]
- โค้ดนี้แสดงให้เห็นว่า
concatจะไม่ทำลายอาร์เรย์ต้นฉบับ คุณจึงสามารถสร้างอาร์เรย์ใหม่โดยที่อาร์เรย์เดิมยังคงเดิม
flat
โดยใช้เมธอด flat คุณสามารถแปลงอาร์เรย์ซ้อนกันให้แบนราบได้
1// flat and flatMap: flatten arrays or map + flatten in one step
2const nested = [1, [2, [3]]];
3console.log("nested:", nested); // nested: [ 1, [ 2, [ 3 ] ] ]
4console.log("nested.flat():", nested.flat()); // default depth = 1
5// nested.flat(): [ 1, 2, [ 3 ] ]
- โค้ดนี้แสดงผลการแปลงอาร์เรย์ให้แบนราบลงหนึ่งระดับ
- เนื่องจาก
flatสามารถกำหนดระดับความลึกได้ คุณจึงสามารถจัดการการซ้อนกันของอาร์เรย์ได้ตามต้องการ
flatMap
เมธอด flatMap จะเปลี่ยนแปลงแต่ละค่าแล้วแปลงผลลัพธ์ให้เป็นอาร์เรย์หนึ่งมิติแบบอัตโนมัติ
1// flat and flatMap: flatten arrays or map + flatten in one step
2const words = ['hello world', 'hi'];
3console.log("words:", words); // [ 'hello world', 'hi' ]
4
5const splitWords = words.flatMap(w => w.split(' '));
6console.log("words.flatMap(w => w.split(' ')):", splitWords);
7// words.flatMap(w => w.split(' ')): [ 'hello', 'world', 'hi' ]
- โค้ดนี้แสดงตัวอย่างที่แต่ละสตริงในอาร์เรย์ถูกแยกตามช่องว่างแล้วนำผลลัพธ์มารวมและทำให้แบนราบเป็นอาร์เรย์เดียว
join
เมธอด join จะสร้างสตริงโดยนำแต่ละสมาชิกในอาร์เรย์มาเชื่อมกันด้วยตัวคั่นที่กำหนด
1// join: combine elements into a string with a separator
2const csv = ['a', 'b', 'c'].join(',');
3console.log("['a', 'b', 'c'].join(','):", csv); // a,b,c
- ในโค้ดนี้จะใช้เมธอด
joinเพื่อแปลงอาร์เรย์เป็นสตริงที่คั่นด้วยเครื่องหมายจุลภาค
ข้อควรระวังที่พบบ่อย
การจัดการอาเรย์อาจดูง่ายในตอนแรก แต่มีหลายประเด็นที่อาจทำให้เกิดพฤติกรรมที่ไม่ตั้งใจได้ง่าย ข้อควรระวังมักถูกมองข้ามระหว่างใช้งานอาเรย์ประจำวัน หากจดจำประเด็นเหล่านี้จะเพิ่มความน่าเชื่อถือของโค้ดอย่างมาก
sort()ของArrayจะเรียงลำดับโดย เปรียบเทียบค่าตัวอักษร เป็นค่าปริยาย เมื่อต้องการเรียงลำดับตัวเลข ให้กำหนดฟังก์ชันเปรียบเทียบทุกครั้ง- การคัดลอกอาเรย์ (เช่นผ่าน
sliceหรือ spread syntax) จะสร้าง สำเนาแบบตื้น หากอาเรย์ของคุณมีอ็อบเจ็กต์ ให้ระวังว่าข้อมูลต้นฉบับอาจถูกเปลี่ยนโดยไม่ได้ตั้งใจ spliceเป็น เมธอดที่เปลี่ยนอาเรย์ต้นฉบับ ส่วนsliceเป็น เมธอดที่ไม่เปลี่ยนอาเรย์ต้นฉบับ จึงควรใช้อย่างเหมาะสมกับความต้องการforEachไม่เหมาะกับลูปที่ต้องใช้ asynchronous หรือawaitหากต้องการประมวลผลแบบ asynchronous ตามลำดับ ควรใช้for...ofแทน
ตัวอย่างการใช้งานจริง
ด้านล่างนี้คือตัวอย่างการผสมเมธอดของอาเรย์เพื่อ 'หาผลรวมอายุจากข้อมูลผู้ใช้ คัดเลือกผู้ที่อายุ 30 ขึ้นไป และสร้างรายชื่อ'
1const users = [
2 { name: 'Alice', age: 28 },
3 { name: 'Bob', age: 34 },
4 { name: 'Carol', age: 41 },
5 { name: 'Dave', age: 19 }
6];
7
8console.log("Users:", users);
9
10// sum ages
11const totalAge = users.reduce((acc, u) => acc + u.age, 0);
12console.log("\nTotal age of all users:", totalAge);
13// Total age of all users: 122
14
15// filter and map names of users 30 and older
16const namesOver30 = users
17 .filter(u => u.age >= 30)
18 .map(u => u.name);
19
20console.log("Users aged 30 or older (names):", namesOver30);
21// Users aged 30 or older (names): [ 'Bob', 'Carol' ]
- โดยการเชื่อมต่อ
reduce,filter, และmapสามารถเขียนโค้ดการรวม ตรวจสอบเงื่อนไข และแปลงข้อมูลอย่างง่าย - 'data processing pipeline' แบบนี้อ่านโค้ดได้ง่ายและมีผลข้างเคียงน้อย เหมาะกับการใช้งานจริง
สรุป
อาเรย์ของ JavaScript นั้น สามารถใช้พื้นฐานได้กว้างขวาง และด้วยฟังก์ชันขั้นสูง โค้ดของคุณจะกระชับและเข้าใจง่ายยิ่งขึ้น มีหลายประเด็นที่ต้องเข้าใจ แต่หากใช้แต่ละวิธีอย่างเหมาะสม การประมวลผลข้อมูลจะราบรื่นมากขึ้น
คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย