Guest Adventurer 🥉 Lv.1
🪙 50
🔷 10
← กลับคลังเนื้อหา
Article · programming

JavaScript Objects และการจัดการข้อมูล

JavaScript

EP13 เรียนรู้ JavaScript Object ตั้งแต่การสร้างและอ่านคุณสมบัติ การเพิ่มและลบข้อมูล การใช้ Destructuring และ Spread Syntax ไปจนถึง Optional Chaining และการจัดการข้อมูลซ้อนอย่างปลอดภัย

EP13: JavaScript Objects และการจัดการข้อมูล

ในบทก่อนหน้า เราเรียนรู้วิธีจัดการ Array ด้วย map(), filter(), reduce() และเมธอดอื่น ๆ แล้ว แต่ข้อมูลจริงมักไม่ได้มีเพียงรายการตัวเลขหรือข้อความ เช่น ข้อมูลสมาชิกหนึ่งคนอาจประกอบด้วยชื่อ อีเมล อายุ และสถานะ ส่วนสินค้าอาจมีรหัส ชื่อ ราคา และจำนวนในสต็อก JavaScript Object จึงเป็นโครงสร้างสำคัญที่ช่วยรวมข้อมูลหลายคุณสมบัติไว้ภายใต้ตัวแปรเดียว
เป้าหมายของบทนี้คุณจะสามารถสร้าง Object อ่านและแก้ไขคุณสมบัติ เลือกใช้ Dot Notation และ Bracket Notation แยกข้อมูลด้วย Destructuring คัดลอกข้อมูลด้วย Spread Syntax และเข้าถึงข้อมูลที่อาจไม่มีอยู่ได้อย่างปลอดภัย

1. Object คืออะไร

Object คือโครงสร้างข้อมูลที่เก็บข้อมูลในรูปแบบคู่ของคุณสมบัติและค่า หรือ Property และ Value คุณสมบัติแต่ละรายการมีชื่อกำกับ ทำให้เราระบุความหมายของข้อมูลได้ชัดเจนกว่าการอ้างอิงตำแหน่งด้วย index เหมือน Array โดยทั่วไป Object มักใช้แทนสิ่งของหรือแนวคิด เช่น ผู้ใช้ สินค้า คำสั่งซื้อ หรือข้อมูลการตั้งค่าระบบ
const user = {
    id: 101,
    name: 'มิน',
    age: 18,
    active: true
};

console.log(user);
console.log(user.name);
console.log(user.age);
ตัวแปร user เก็บ Object ที่มีคุณสมบัติ id, name, age และ active เราสามารถเข้าถึงแต่ละค่าได้โดยระบุชื่อคุณสมบัติ เช่น user.name จะได้ข้อความ มิน และ user.active จะได้ค่า true

2. การอ่านคุณสมบัติด้วย Dot Notation

Dot Notation เป็นวิธีอ่านคุณสมบัติที่ใช้เครื่องหมายจุดตามด้วยชื่อคุณสมบัติ เหมาะกับชื่อที่เป็นตัวระบุ JavaScript ได้ตามปกติ เช่น object.name หรือ product.price รูปแบบนี้อ่านง่ายและเป็นวิธีที่ใช้บ่อยที่สุดในโค้ดทั่วไป
const product = {
    name: 'คีย์บอร์ด',
    price: 890,
    inStock: true
};

console.log(product.name);
console.log(product.price);
console.log(product.inStock);
เมื่อทราบชื่อคุณสมบัติแน่นอน Dot Notation ช่วยให้โค้ดกระชับและอ่านเข้าใจง่าย อย่างไรก็ตาม หากชื่อคุณสมบัติอยู่ในตัวแปรหรือมีช่องว่าง เราควรใช้ Bracket Notation แทน

3. Bracket Notation สำหรับชื่อคุณสมบัติแบบไดนามิก

Bracket Notation ใช้วงเล็บเหลี่ยมครอบชื่อคุณสมบัติ โดยสามารถใส่ข้อความหรือค่าจากตัวแปรได้ เหมาะกับกรณีที่ชื่อคุณสมบัติเปลี่ยนไปตามการเลือกของผู้ใช้ หรือมาจากข้อมูลที่ได้รับจากระบบภายนอก
const product = {
    name: 'คีย์บอร์ด',
    price: 890,
    'stock-count': 15,
    category: 'อุปกรณ์คอมพิวเตอร์'
};

const selectedField = 'price';

console.log(product['stock-count']);
console.log(product[selectedField]);
console.log(product['category']);
คุณสมบัติ stock-count มีเครื่องหมายขีดกลาง จึงไม่สามารถเข้าถึงด้วยรูปแบบ product.stock-count ได้ตามที่ต้องการ เพราะ JavaScript จะตีความเครื่องหมายขีดกลางเป็นตัวดำเนินการลบ การใช้ product['stock-count'] จึงเป็นรูปแบบที่ถูกต้อง ส่วน product[selectedField] จะอ่านค่าของคุณสมบัติที่ชื่ออยู่ในตัวแปร selectedField
รูปแบบเหมาะกับกรณีตัวอย่าง
Dot Notationทราบชื่อคุณสมบัติแน่นอนuser.name
Bracket Notationชื่อคุณสมบัติอยู่ในตัวแปรuser[field]
Bracket Notationชื่อคุณสมบัติมีช่องว่างหรือเครื่องหมายพิเศษitem['stock-count']

4. เพิ่ม แก้ไข และลบคุณสมบัติ

Object ที่ประกาศด้วย const ไม่สามารถนำตัวแปรไปชี้ยัง Object ตัวใหม่ได้ แต่คุณสมบัติของ Object ยังสามารถเปลี่ยนแปลงได้ หากไม่ได้กำหนดข้อจำกัดอื่นไว้ เราสามารถเพิ่มคุณสมบัติใหม่ แก้ไขค่าเดิม หรือลบคุณสมบัติด้วย delete ได้
const user = {
    name: 'มิน',
    age: 18
};

// เพิ่มคุณสมบัติ
user.email = '[email protected]';

// แก้ไขค่าเดิม
user.age = 19;

// เพิ่มคุณสมบัติด้วย Bracket Notation
user['member-level'] = 'gold';

// ลบคุณสมบัติ
 delete user.email;

console.log(user);
หลังทำงานเสร็จ Object จะมี name, age และ member-level แต่ไม่มี email แล้ว คำสั่ง delete ใช้ลบคุณสมบัติออกจาก Object โดยตรง จึงควรใช้อย่างระมัดระวังหาก Object นั้นถูกใช้งานร่วมกับส่วนอื่นของโปรแกรม
const ไม่ได้ทำให้ Object เป็นค่าคงที่ทั้งหมดconst ป้องกันการกำหนดตัวแปรให้ชี้ไปยังค่าใหม่ แต่ไม่ได้ป้องกันการเปลี่ยนแปลงคุณสมบัติของ Object หากต้องการป้องกันการเปลี่ยนแปลง ต้องใช้แนวทางเพิ่มเติมตามลักษณะงาน เช่น Object.freeze() ซึ่งป้องกันการแก้ไขระดับบนสุด แต่ไม่ได้ทำให้ Object ที่ซ้อนอยู่ถูกแช่แข็งโดยอัตโนมัติ

5. Destructuring ดึงคุณสมบัติออกมาใช้งาน

Object Destructuring ช่วยดึงคุณสมบัติจาก Object มาเก็บไว้ในตัวแปรโดยไม่ต้องเขียนชื่อ Object ซ้ำหลายครั้ง เหมาะกับการเตรียมข้อมูลก่อนใช้งานในฟังก์ชัน การอ่านค่าจากผลลัพธ์ API และการกำหนดค่าเริ่มต้นให้ข้อมูลที่อาจไม่มีอยู่
const user = {
    id: 101,
    name: 'มิน',
    age: 18,
    city: 'อุตรดิตถ์'
};

const { name, age } = user;

console.log(name);
console.log(age);
รูปแบบ const { name, age } = user จะดึงค่าจากคุณสมบัติชื่อ name และ age มาเก็บในตัวแปรชื่อเดียวกันกับคุณสมบัติ หากต้องการตั้งชื่อตัวแปรใหม่ สามารถใช้รูปแบบ name: displayName ได้
const user = {
    name: 'มิน',
    age: 18
};

const {
    name: displayName,
    city = 'ไม่ระบุเมือง',
    age: userAge = 0
} = user;

console.log(displayName);
console.log(city);
console.log(userAge);
displayName เป็นตัวแปรใหม่ที่รับค่าจาก user.name ส่วน city ใช้ค่าเริ่มต้นเมื่อคุณสมบัติไม่มีอยู่หรือมีค่าเป็น undefined การกำหนดค่าเริ่มต้นลักษณะนี้ไม่ได้แทนค่าที่เป็น null ดังนั้นควรพิจารณาความหมายของข้อมูลก่อนเลือกใช้

6. Spread Syntax คัดลอกและรวม Object

Spread Syntax ใช้เครื่องหมายจุดสามจุด ... เพื่อกระจายคุณสมบัติที่ enumerable ของ Object ออกมาเป็นส่วนหนึ่งของ Object ใหม่ วิธีนี้เหมาะกับการสร้างข้อมูลที่คล้ายต้นฉบับแต่มีการเปลี่ยนแปลงบางคุณสมบัติ รวมถึงการรวม Object หลายตัวเข้าด้วยกัน
const originalUser = {
    id: 101,
    name: 'มิน',
    role: 'member'
};

const updatedUser = {
    ...originalUser,
    role: 'admin',
    active: true
};

console.log(originalUser);
console.log(updatedUser);
updatedUser จะมี id และ name จาก Object เดิม แต่ role เปลี่ยนเป็น admin และมี active เพิ่มเข้ามา เมื่อคุณสมบัติชื่อเดียวกันปรากฏหลายครั้ง ค่าที่อยู่ด้านหลังจะเขียนทับค่าที่อยู่ด้านหน้า จึงควรตรวจสอบลำดับการรวม Object ให้ถูกต้อง

6.1 อัปเดตข้อมูลโดยไม่แก้ Object ต้นฉบับ

const product = {
    id: 1,
    name: 'สมุด',
    price: 25,
    inStock: true
};

const discountedProduct = {
    ...product,
    price: 20
};

console.log(product.price);
console.log(discountedProduct.price);
product.price ยังคงเป็น 25 ส่วน discountedProduct.price เป็น 20 เพราะเราได้สร้าง Object ใหม่ วิธีนี้ช่วยลดผลกระทบจากการแก้ไขข้อมูลร่วมกัน และเป็นแนวทางที่นิยมใช้เมื่อต้องจัดการ State ในแอปพลิเคชัน

7. Shallow Copy กับข้อมูลที่ซ้อนกัน

การคัดลอกด้วย Spread Syntax เป็นการคัดลอกเพียงระดับบนสุด หรือ Shallow Copy หากคุณสมบัติหนึ่งเก็บ Object หรือ Array อยู่ภายใน ค่าที่ซ้อนอยู่อาจยังอ้างถึงข้อมูลชุดเดียวกัน การเปลี่ยนคุณสมบัติด้านในจึงอาจส่งผลต่อ Object ต้นฉบับได้
const original = {
    name: 'มิน',
    address: {
        city: 'อุตรดิตถ์'
    }
};

const copy = { ...original };

copy.name = 'ฟ้า';
copy.address.city = 'เชียงใหม่';

console.log(original.name);
console.log(original.address.city);
console.log(copy.name);
console.log(copy.address.city);
original.name ยังคงเป็น มิน เพราะคุณสมบัติ name ถูกคัดลอกมาอยู่ใน Object ใหม่ แต่ original.address.city เปลี่ยนเป็น เชียงใหม่ เพราะ address ยังคงอ้างถึง Object ชั้นในตัวเดียวกันกับต้นฉบับ

7.1 คัดลอกข้อมูลซ้อนระดับที่ต้องการ

const original = {
    name: 'มิน',
    address: {
        city: 'อุตรดิตถ์',
        postalCode: '53000'
    }
};

const copy = {
    ...original,
    address: {
        ...original.address,
        city: 'เชียงใหม่'
    }
};

console.log(original.address.city);
console.log(copy.address.city);
ตัวอย่างนี้สร้าง Object ใหม่ทั้งระดับบนและระดับ address จึงสามารถเปลี่ยนเมืองใน copy ได้โดยไม่เปลี่ยนเมืองของ original หากโครงสร้างมีหลายระดับ ควรคัดลอกเฉพาะส่วนที่ต้องแก้ไขให้ครบตามความลึกของข้อมูล
เมื่อจำเป็นต้องคัดลอกข้อมูลลึกทั้งหมดstructuredClone() สามารถใช้คัดลอกข้อมูลหลายระดับสำหรับชนิดข้อมูลที่รองรับได้ แต่ไม่ได้ใช้ได้กับทุกชนิดหรือทุกสภาพแวดล้อม และมีข้อจำกัดกับข้อมูลบางประเภท ควรเลือกวิธีคัดลอกให้เหมาะกับโครงสร้างข้อมูลจริง

8. Object.keys(), Object.values() และ Object.entries()

JavaScript มีเมธอดมาตรฐานสำหรับแปลงคุณสมบัติของ Object ให้อยู่ในรูปแบบ Array โดย Object.keys() คืนชื่อคุณสมบัติ Object.values() คืนค่า และ Object.entries() คืนคู่ของชื่อกับค่า เครื่องมือเหล่านี้มีประโยชน์เมื่อต้องวนอ่านข้อมูลหรือสร้างรายงานจาก Object
const scores = {
    math: 90,
    science: 85,
    english: 92
};

console.log(Object.keys(scores));
console.log(Object.values(scores));
console.log(Object.entries(scores));

for (const [subject, score] of Object.entries(scores)) {
    console.log(`${subject}: ${score}`);
}
Object.keys(scores) ได้ชื่อวิชา Object.values(scores) ได้คะแนน และ Object.entries(scores) ได้ Array ของคู่ [ชื่อวิชา, คะแนน] ซึ่งสามารถแยกค่าในลูปด้วย Destructuring ได้โดยตรง

9. Optional Chaining ป้องกันการเข้าถึงข้อมูลที่ไม่มีอยู่

ในข้อมูลจาก API หรือฐานข้อมูล คุณสมบัติบางอย่างอาจไม่มีอยู่ หรือ Object ชั้นในอาจเป็น null หรือ undefined หากเข้าถึงคุณสมบัติต่อโดยตรง อาจเกิด TypeError เครื่องหมาย ?. หรือ Optional Chaining ช่วยหยุดการเข้าถึงเมื่อส่วนก่อนหน้าเป็น null หรือ undefined และส่งกลับ undefined แทน
const userA = {
    name: 'มิน',
    profile: {
        city: 'อุตรดิตถ์'
    }
};

const userB = {
    name: 'ฟ้า',
    profile: null
};

console.log(userA.profile?.city);
console.log(userB.profile?.city);
console.log(userB.contact?.email);
userA.profile?.city คืนค่า อุตรดิตถ์ ส่วน userB.profile?.city และ userB.contact?.email คืนค่า undefined โดยไม่ทำให้เกิดข้อผิดพลาดจากการอ่านคุณสมบัติของ null หรือ undefined

10. Nullish Coalescing กำหนดค่าเริ่มต้นให้ข้อมูล

เครื่องหมาย ?? หรือ Nullish Coalescing ใช้เลือกค่าทางขวาเมื่อค่าทางซ้ายเป็น null หรือ undefined เท่านั้น แตกต่างจาก || ที่ใช้ค่าทางขวาเมื่อค่าทางซ้ายเป็น Falsy เช่น 0, false หรือข้อความว่าง ซึ่งบางกรณีเป็นข้อมูลที่ถูกต้องและควรรักษาไว้
const settings = {
    volume: 0,
    enabled: false,
    theme: ''
};

const volumeA = settings.volume ?? 50;
const volumeB = settings.volume || 50;
const enabled = settings.enabled ?? true;
const theme = settings.theme ?? 'light';

console.log(volumeA);
console.log(volumeB);
console.log(enabled);
console.log(theme);
volumeA เป็น 0 เพราะ 0 ไม่ใช่ null หรือ undefined แต่ volumeB เป็น 50 เพราะ 0 เป็น Falsy ค่า enabled ยังคงเป็น false และ theme ยังคงเป็นข้อความว่าง ตัวอย่างนี้แสดงเหตุผลที่ควรเลือก ?? เมื่อค่าที่เป็น 0, false หรือข้อความว่างยังมีความหมาย

11. Object กับ JSON แตกต่างกันอย่างไร

Object เป็นโครงสร้างข้อมูลที่ใช้ทำงานภายใน JavaScript ส่วน JSON เป็นรูปแบบข้อความมาตรฐานสำหรับแลกเปลี่ยนข้อมูลระหว่างระบบ JSON.stringify() แปลงข้อมูลที่รองรับจาก JavaScript เป็นข้อความ JSON และ JSON.parse() แปลงข้อความ JSON ที่ถูกต้องกลับเป็นค่า JavaScript
const product = {
    id: 101,
    name: 'สมุด',
    price: 25
};

const jsonText = JSON.stringify(product);
console.log(jsonText);

const parsedProduct = JSON.parse(jsonText);
console.log(parsedProduct.name);
console.log(parsedProduct.price);
jsonText เป็นข้อความที่แทนข้อมูลสินค้า ส่วน parsedProduct เป็น Object ที่สร้างจากข้อความนั้น ในงานจริงควรจัดการกรณีข้อความ JSON ไม่ถูกต้อง เพราะ JSON.parse() จะโยน SyntaxError เมื่อไม่สามารถแปลงข้อความได้
function parseJsonSafely(text) {
    try {
        return {
            success: true,
            data: JSON.parse(text)
        };
    } catch (error) {
        return {
            success: false,
            message: 'ข้อมูล JSON ไม่ถูกต้อง'
        };
    }
}

console.log(parseJsonSafely('{"name":"มิน"}'));
console.log(parseJsonSafely('{invalid json}'));
ฟังก์ชัน parseJsonSafely() ใช้ try...catch เพื่อจัดการข้อผิดพลาด โดยคืน Object ที่มี success ระบุสถานะและ data หรือ message ตามผลลัพธ์ ในระบบจริงควรบันทึกรายละเอียดข้อผิดพลาดอย่างเหมาะสมโดยไม่เปิดเผยข้อมูลอ่อนไหวให้ผู้ใช้เห็น

12. ตัวอย่างประยุกต์: อัปเดตโปรไฟล์สมาชิก

ลองนำแนวคิดที่เรียนรู้มารวมกันในตัวอย่างโปรไฟล์สมาชิก เราจะดึงข้อมูลบางส่วนด้วย Destructuring ใช้ Optional Chaining อ่านที่อยู่ ใช้ Nullish Coalescing กำหนดค่าทดแทน และใช้ Spread Syntax สร้าง Object ใหม่เพื่ออัปเดตข้อมูลโดยไม่แก้ไขต้นฉบับ
const member = {
    id: 501,
    name: 'มิน',
    email: '[email protected]',
    profile: {
        city: 'อุตรดิตถ์',
        phone: null
    },
    preferences: {
        newsletter: false
    }
};

const { id, name, email } = member;
const city = member.profile?.city ?? 'ไม่ระบุเมือง';
const phone = member.profile?.phone ?? 'ไม่ระบุเบอร์โทร';
const newsletterEnabled = member.preferences?.newsletter ?? true;

const updatedMember = {
    ...member,
    profile: {
        ...member.profile,
        city: 'เชียงใหม่'
    }
};

console.log({ id, name, email, city, phone, newsletterEnabled });
console.log(member.profile.city);
console.log(updatedMember.profile.city);
ตัวอย่างนี้ช่วยแสดงให้เห็นว่าแต่ละเทคนิคมีหน้าที่ต่างกัน Destructuring ช่วยดึงข้อมูล Optional Chaining ช่วยเข้าถึงข้อมูลที่อาจไม่มี Nullish Coalescing ช่วยกำหนดค่าเริ่มต้น และ Spread Syntax ช่วยสร้าง Object ใหม่ที่มีข้อมูลส่วนใหญ่เหมือนเดิมแต่เปลี่ยนเฉพาะเมือง

13. แบบฝึกหัดท้ายบท

โจทย์ที่ 1: อ่านคุณสมบัติ

กำหนด Object student ที่มี name, age และ score ให้เขียนคำสั่งอ่านชื่อและคะแนน โดยใช้ Dot Notation

โจทย์ที่ 2: ใช้ชื่อคุณสมบัติจากตัวแปร

กำหนด field = 'price' และ Object product ที่มีคุณสมบัติ price ให้เขียนคำสั่งอ่านราคาด้วย Bracket Notation

โจทย์ที่ 3: สร้าง Object ใหม่

กำหนด Object product ที่มี name และ price ให้สร้าง discountedProduct โดยใช้ Spread Syntax และเปลี่ยน price เป็น 99 โดยไม่เปลี่ยน Object ต้นฉบับ

โจทย์ที่ 4: อ่านข้อมูลที่อาจไม่มีอยู่

กำหนด Object user ที่อาจไม่มี profile หรือ profile อาจเป็น null ให้เขียนคำสั่งอ่าน profile.city อย่างปลอดภัย และใช้ค่าเริ่มต้นเป็นข้อความ ไม่ระบุเมือง

โจทย์ที่ 5: แสดงชื่อคุณสมบัติและค่า

กำหนด Object scores ที่เก็บคะแนนหลายวิชา ให้ใช้ Object.entries() วนแสดงชื่อวิชาและคะแนนทีละรายการ

14. เฉลยแบบฝึกหัด

const student = {
    name: 'ฟ้า',
    age: 12,
    score: 95
};
console.log(student.name, student.score);

const field = 'price';
const product = {
    name: 'กระเป๋า',
    price: 199
};
console.log(product[field]);

const discountedProduct = {
    ...product,
    price: 99
};
console.log(product.price);
console.log(discountedProduct.price);

const user = {
    profile: null
};
const city = user.profile?.city ?? 'ไม่ระบุเมือง';
console.log(city);

const scores = {
    math: 90,
    science: 85,
    english: 92
};
for (const [subject, score] of Object.entries(scores)) {
    console.log(`${subject}: ${score}`);
}
เฉลยใช้วิธีอ่านคุณสมบัติโดยตรง ใช้ตัวแปรเป็นชื่อคุณสมบัติ สร้าง Object ใหม่ด้วย Spread Syntax เข้าถึงข้อมูลซ้อนอย่างปลอดภัย และวนอ่านคู่ชื่อกับค่าด้วย Object.entries() คุณสามารถปรับข้อมูลตัวอย่างและทดลองเพิ่มเงื่อนไขอื่นเพื่อฝึกความเข้าใจได้
สรุปบทเรียนObject ช่วยจัดกลุ่มข้อมูลตามชื่อคุณสมบัติ Dot Notation เหมาะกับชื่อที่แน่นอน ส่วน Bracket Notation เหมาะกับชื่อที่เป็นตัวแปร Destructuring ช่วยดึงข้อมูล Spread Syntax ช่วยรวมและคัดลอกข้อมูลระดับบน Optional Chaining ช่วยเข้าถึงข้อมูลที่อาจไม่มี และ Nullish Coalescing ช่วยกำหนดค่าเริ่มต้นโดยรักษาค่า 0, false และข้อความว่างไว้
โครงสร้างข้อมูลที่ดีทำให้โค้ดไม่เพียงทำงานได้ แต่ยังสื่อความหมายของข้อมูลได้ชัดเจนและต่อยอดได้ง่าย

EP14: JavaScript Functions และการออกแบบฟังก์ชันที่นำกลับมาใช้ได้

ตอนถัดไปจะเรียนรู้การประกาศฟังก์ชัน Function Declaration, Function Expression และ Arrow Function รวมถึงพารามิเตอร์ ค่าเริ่มต้น Return Value การส่งฟังก์ชันเป็นอาร์กิวเมนต์ และการแยกงานเป็นฟังก์ชันย่อยเพื่อให้โค้ดอ่านง่ายและทดสอบได้สะดวก
อ่านจนถึงระยะ 500px ก่อนท้ายหน้าและอยู่ในหน้านี้อย่างน้อย 5 นาที เพื่อรับ EXP วันละครั้ง

⏳ กำลังจับเวลาอ่าน...

🌐
เลือกภาษา / Select Language
21 World Languages Supported