Array & Callback

การจัดการ array คือหัวใจของการเขียน JS สมัยใหม่ — โยนฟังก์ชันเข้าไปแล้วให้ array จัดการวนซ้ำให้เอง แทนการเขียน for ทุกครั้ง

Array vs Object

ArrayObject
ใช้เครื่องหมาย[ ]{ }
เข้าถึงด้วยindex ที่เป็นตัวเลขชื่อ key
let user = { name: 'A' };
user.name        // A
user["name"]     // A

// ซ้อนกันเป็นต้นไม้ได้
const shop = [
    { name: "แซลมอน", type: [ {grade:"A", price:10}, {grade:"ธรรมดา", price:1} ] },
    { name: ["หมูทะ", "หมูจุ่ม"], price: 1 }
];

Destructuring

// array
const name = ["a", "b"];
const [a, b] = name;                    // a="a", b="b"

const vehicles = ['mustang', 'f-150', 'expedition'];
const [car, , suv] = vehicles;          // car='mustang', suv='expedition' — เว้นตำแหน่งได้

// object
const vehicleOne = { brand:'Ford', model:'Mustang', type:'car', year:2021, color:'red' };
const { type, color, brand, model } = vehicleOne;

// destructure ที่พารามิเตอร์ของฟังก์ชันได้เลย
function myVehicle({ type, color, brand, model }) {
    return 'My ' + type + ' is a ' + color + ' ' + brand + ' ' + model;
}
myVehicle(vehicleOne);

// รับหลายค่าที่ return มาเป็น array
function calculate(a, b) {
    return [a+b, a-b, a*b, a/b];
}
const [add, subtract, multiply, divide] = calculate(4, 7);

push / pop / shift / unshift

Banana Orange Apple unshift() → เพิ่มหน้า shift() ← เอาตัวแรกออก push() → เพิ่มท้าย pop() ← เอาตัวท้ายออก index 0 index 2 push/pop ทำงานที่ท้าย array (เหมือน stack) · shift/unshift ทำงานที่หัว

4 เมธอดพื้นฐานที่ข้อสอบถามบ่อยที่สุด

const fruits = ["Banana", "Orange", "Apple", "Mango"];

fruits.push("Kiwi");                // เพิ่มท้าย  → ...,Mango,Kiwi
fruits.push("Kiwi", "Lemon");       // เพิ่มหลายตัวได้
fruits.pop();                       // เอาตัวท้ายออก
fruits.shift();                     // เอาตัวแรกออก → Orange,Apple,Mango
fruits.unshift("Lemon","Pineapple");// เพิ่มที่ตัวแรก

splice / slice / delete

splice(index ที่จะลบ, จำนวนที่จะลบ)
splice(index ที่จะลบ, จำนวนที่จะลบ, ค่าที่จะแทรกเข้าไปแทน)
slice(index เริ่ม, index สุดท้าย - 1)      // ตัดออกมาเป็น array ใหม่

// ลบตามค่าที่ค้นเจอ
const array = [2, 5, 9];
const index = array.indexOf(5);
if (index > -1) {
    array.splice(index, 1);         // ลบ 1 ตัวที่ตำแหน่งนั้น
}

delete fruits[2];                   // ลบค่าแต่ทิ้งช่องว่างไว้ (ไม่แนะนำ)

join / concat

const fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.join();          // "Banana,Orange,Apple,Mango"
fruits.join(" and ");   // "Banana and Orange and Apple and Mango"

const arr1 = ["Cecilie", "Lone"];
const arr2 = ["Emil", "Tobias"];
arr1.concat(arr2);      // ["Cecilie","Lone","Emil","Tobias"]
เมธอดรับอะไรคืนอะไร
.indexOf(ค่า)ค่าที่จะค้นindex ถ้าเจอ, -1 ถ้าไม่เจอ
.find(fn)ฟังก์ชันเงื่อนไขค่าตัวแรกที่ตรง, undefined ถ้าไม่เจอ
.findIndex(fn)ฟังก์ชันเงื่อนไขindex ของตัวแรกที่ตรง
const a = ["a", "b"];
a.find(value => value === "b");     // "b" — array เดิมไม่เปลี่ยน

Callback function

JS โยนฟังก์ชันเป็น parameter ให้ฟังก์ชันอื่นได้

// แบบ sequence — เรียกตรง ๆ ไม่ใช่ callback
function myCalculator(num1, num2) {
    let sum = num1 + num2;
    myDisplayer(sum);
}
myCalculator(5, 5);

// แบบ callback — โยนฟังก์ชันเข้าไปเป็นพารามิเตอร์
function myDisplayer(some) {
    document.getElementById("demo").innerHTML = some;
}
function myCalculator(num1, num2, myCallback) {
    let sum = num1 + num2;
    myCallback(sum);
}
myCalculator(5, 5, myDisplayer);
สังเกตว่าตอนส่งเข้าไปเขียน myDisplayer เฉย ๆ ไม่ใส่วงเล็บ — ถ้าใส่วงเล็บจะกลายเป็นการเรียกใช้แล้วส่ง "ผลลัพธ์" เข้าไปแทน

map — แปลงค่าทุกตัว

const numbers = [4, 9, 16, 25];
const newArr = numbers.map(Math.sqrt);        // [2, 3, 4, 5]

const numbers2 = [65, 44, 12, 4];
const newArr2 = numbers2.map((num) => { return num + 10; });
ฟังก์ชันที่โยนเข้า map ต้อง return ค่า และ map ได้ array ใหม่ — array เดิมไม่เปลี่ยน

filter — คัดกรอง

const words = ['spray', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter((word) => word.length > 6);
// ['exuberant', 'destruction', 'present']
ฟังก์ชันที่โยนเข้า filter ต้องเป็นเงื่อนไข (คืน true/false)

reduce — ยุบ array ให้เหลือค่าเดียว

array.reduce((prev, curr) => { ... }, ค่าเริ่มต้น)
let price = [42, 32, 20] · reduce หาผลรวม เริ่มที่ 0 prev = 0 +42 prev = 42 +32 prev = 74 +20 ผลลัพธ์ 94 ถ้าเปลี่ยนค่าเริ่มต้นเป็น -1 ผลลัพธ์จะเป็น 93 price.reduce(function (prev, curr) { return prev + curr; }, 0); พารามิเตอร์: (ค่าก่อนหน้า, ค่าปัจจุบัน, ตำแหน่งปัจจุบัน) และค่าเริ่มต้นอยู่หลังฟังก์ชัน

reduce สะสมค่าไปเรื่อย ๆ จนจบ array

let price = [42, 32, 20];

// ผลรวม
let sum = price.reduce(function (prev, curr) {
    return prev + curr;
}, 0);                       // 94   (ถ้าเริ่มที่ -1 จะได้ 93)

// หาค่ามากสุด
let max = price.reduce(function (prev, curr) {
    return prev < curr ? curr : prev;
});                          // 42

// reduce ใช้แทน filter ได้
let filtered = price.reduce(function (prev, curr) {
    if (curr > 20) { return [...prev, curr]; }
    return [...prev];
}, []);                      // [42, 32]

// รวมราคาสินค้าในตะกร้า
const cart = [{name:"แซลมอน", price:10}, {name:"หมูทะ", price:1}];
const total = cart.reduce((value, data) => value + data.price, 0);   // 11

sort — กับดักที่ต้องระวัง

const numbers = [2, 10, 1, 100, 20];
numbers.sort();          // [1, 10, 100, 2, 20]  ← ผิด! เรียงแบบ string

ค่าปริยายของ sort คือแปลงเป็น string ก่อนแล้วเรียงตามตัวอักษร

// วิธีที่ถูก — ใส่ฟังก์ชันเปรียบเทียบ
numbers.sort((a, b) => a - b);    // น้อยไปมาก [1,2,10,20,100]
numbers.sort((a, b) => b - a);    // มากไปน้อย
ผลของ a - bความหมาย
ค่าลบa มาก่อน b
ค่าบวกb มาก่อน a
0เท่ากัน คงลำดับเดิม
JavaScript engine V8 ใช้อัลกอริทึม Timsort ในการ sort ซึ่งเป็นการผสมระหว่าง Merge Sort + Insertion Sort