ตั้งแต่ใน
บทที่ ๓
ได้เริ่มเขียนแนะนำข้อมูลชนิดต่างๆไปแล้ว
และได้แนะนำเกี่ยวกับข้อมูลชนิดที่เรียกว่า
"ออบเจ็กต์" (object) ไป
ออบเจ็กต์เป็นข้อมูลที่มีความสำคัญมากในภาษาจาวาสคริปต์
ในบทนี้จะมาเจาะลึกเพื่อให้เข้าใจยิ่งขึ้น
การสร้างออบเจ็กต์
ออบเจ็กต์ คือข้อมูลโครงสร้างที่ประกอบไปด้วยค่าต่างๆเก็บไว้ภายใน เรียกว่า
"พรอเพอร์ตี" (property)
แต่ละพรอเพอร์ตีจะมีการตั้งชื่อเรียกเพื่อเข้าถึง ชื่อนั้นเรียกว่า
"คีย์" (key)
การสร้างทำได้ง่ายๆโดยใช้วงเล็บปีกกา {} แล้วใส่คีย์ ตามด้วยทวิภาค (colon) :
ตามด้วยค่าพรอเพอร์ตี โดยพรอเพอร์ตีแต่ละตัวจะคั่นด้วยจุลภาค (comma) ,
ตัวอย่าง
var pokemon1 = {
chue: "ฟุชิงิดาเนะ",
sung: 0.7,
nak: 6.9
};
(ภาพฟุชิงิดาเนะ
ที่มา)
แบบนี้ก็จะได้ข้อมูลออบเจ็กต์ที่เป็น pokemon (โปเกมอน) ซึ่งมีพรอเพอร์ตีคือ chue
(ชื่อ) ฟุชิงิดาเนะ sung (สูง) 0.7 และ nak (หนัก) 6.9
ปกติเวลาสร้างออบเจ็กต์นั้นบ่อยครั้งที่เขียนแยกพรอเพอร์ตีแต่ละตัวไว้เขียนคนละบรรทัดแบบนี้เพื่อให้เข้าใจง่าย
แต่ที่จริงจะเขียนต่อกันทีเดียวในบรรทัดเดียวก็ทำได้ เช่น
var pokemon1 = {chue: "ฟุชิงิดาเนะ", sung: 0.7, nak: 6.9};
ที่สำคัญคือให้คั่นคีย์กับค่าพรอเพอร์ตีด้วยโคลอน :
และคั่นระหว่างแต่ละพรอเพอร์ตีด้วยจุลภาค ,
หลังพรอเพอร์ตีตัวสุดท้ายไม่จำเป็นต้องใส่จุลภาค แต่จะใส่ก็ได้ไม่ผิด
var pokemon1 = {chue: "ฟุชิงิดาเนะ", sung: 0.7, nak: 6.9,};
แม้ว่าจะเขียนยัดใส่บรรทัดเดียวกันทั้งหมดแบบนี้ได้
แต่ปกติถ้าหากมีหลายตัวแล้วถ้าใส่บรรทัดเดียวกันหมดจะยาวเกิน
ดังนั้นเพื่อให้ดูเรียบร้อยมักจะเขียนแยกแต่ละพรอเพอร์ตีไว้คนละบรรทัด
หากต้องการสร้างออบเจ็กต์ว่างเปล่าที่ไม่มีพรอเพอร์ตีอะไรเลยก็ทำได้
แบบนั้นก็แค่ใส่วงเล็บปีกกาเปิดปิดไว้เฉยๆ
var wangplao = {};
คีย์ของพรอเพอร์ตีนั้นจะใช้คำหรือข้อความอะไรก็ได้
เพียงแต่ถ้าหากมีพวกสัญลักษณ์พิเศษหรือมีเว้นวรรคจำเป็นจะต้องใช้เครื่องหมายคำพูดคร่อม
" " เวลาที่ประกาศสร้างขึ้น
เช่น
var pokemon2 = {
"chue": "พีคาชู",
"chue len": "พีคา",
"suan-sung": 0.4,
"nam-nak-tua": 6.0
};
ในตัวอย่างนี้ เฉพาะ chue (ชื่อ)
เท่านั้นที่ไม่จำเป็นต้องใส่เครื่องหมายคำพูดคร่อมก็ได้ แต่ว่าจะใส่ไว้ก็ได้
แล้วแต่
ส่วน chue len (ชื่อเล่น) มีเว้นวรรค และ suan-sung (ส่วนสูง) และ nam-nak-tua
(น้ำหนักตัว) มีขีด - อยู่ดังนั้นต้องใส่เครื่องหมายคำพูด
ไม่เช่นนั้นจะเกิดข้อผิดพลาด
การเข้าถึงข้อมูลในออบเจ็กต์
เมื่อประกาศสร้างออบเจ็กต์ขึ้นมาแล้วสามารถเข้าถึงเพื่อใช้หรือดูค่าพรอเพอร์ตีได้โดยพิมพ์ชื่อตัวแปรออบเจ็กต์ตามด้วยจุด
. แล้วตามด้วยคีย์
ขอยกตัวอย่างด้วย pokemon2 ที่สร้างขึ้นจากหัวข้อที่แล้ว
เช่นเมื่อต้องการดูข้อมูล chue (ชื่อ)
alert(pokemon2.chue); // ได้ พีคาชู
หรืออาจเข้าถึงค่าโดยใส่วงเล็บเหลี่ยม แล้วใส่คีย์ไว้ด้านใน
กรณีนี้จะต้องใส่เครื่องหมายคำพูดเสมอ
alert(pokemon2["chue"]); // ได้ พีคาชู
แต่สำหรับพรอเพอร์ตีที่ตั้งคีย์เป็นคำที่มีพวกเครื่องหมายหรือเว้นวรรคจะใช้วิธีแรกในการเข้าถึงไม่ได้
ต้องใช้วงเล็บเหลี่ยมเท่านั้น
alert(pokemon2["chue len"]); // ได้ พีคา
alert(pokemon2["suan-sung"]); // ได้ 0.4
นอกจากนี้ หากคีย์ขึ้นต้นด้วยตัวเลขก็ไม่สามารถเข้าถึงโดยใช้จุดเช่นกัน
เพียงแต่สำหรับตัวเลขจะใส่เครื่องหมายคำพูดหรือไม่ก็ได้
var h = {1: "a", 2: "b"};
alert(h[1]); // ได้ a
alert(h["2"]); // ได้ b
คีย์ถือเป็นข้อมูลชนิดสายอักขระเสมอ
แม้ต่อให้ตอนที่ประกาศชื่อคีย์จะใส่เป็นตัวเลขไปก็จะได้สายอักขระที่เป็นตัวเลข
ดังนั้นในที่นี้คีย์ไม่ใช่ตัวเลข 1 แต่เป็นสายอักขระ "1"
เพียงแต่ถ้าใส่ตัวเลข 1 ไปเฉยๆจะถูกตีความเป็นสายอักขระให้
จึงใช้เข้าถึงพรอเพอร์ตีได้เช่นกัน
ถ้าใส่ชื่อพรอเพอร์ตีที่ไม่มีอยู่จะได้ undefined
var obji = {a: 1};
alert(obji.b); // ได้ undefined
สิ่งที่ใส่ใน [ ] อาจเป็นตัวแปรที่เก็บค่าสายอักขระก็ได้ เช่น
var obja = {b: 5};
var prop = "b";
alert(obja[prop]); // ได้ 5
กรณีนี้ obja[prop] ไม่ได้หมายถึงหาพรอเพอร์ตีที่ชื่อ prop
แต่หมายถึงให้หาพรอเพอร์ตีที่ชื่อมีค่าตามที่อยู่ในตัวแปร prop นั่นก็คือ b
นั่นเอง
แต่ถ้าใส่เครื่องหมายคำพูดกลายเป็น obja["prop"] แบบนี้
จะมีค่าเท่ากับการเขียน obja.prop นั่นคือให้หาพรอเพอร์ตีที่ชื่อ prop
ซึ่งในเมื่อไม่ได้กำหนดไว้ก็จะได้ undefined
alert(obja["prop"]); // ได้ undefined
ดังนั้นจะเห็นว่าการใส่เครื่องหมายคำพูดคร่อมหรือไม่นั้นสำคัญ
ต้องระวังสับสนแล้วใช้ผิด
การเพิ่มหรือแก้ข้อมูลในออบเจ็กต์
การเพิ่มพรอเพอร์ตีให้กับออบเจ็กต์สามารถทำได้โดยวิธีการที่คล้ายกับตอนดูค่า
นั่นคือใช้จุดหรือวงเล็บเหลี่ยม
var pokemon3 = {
lek: 111,
lv: 30
};
pokemon3.chue = "ไซฮอร์น"; // เพิ่มพรอเพอร์ตี chue (ชื่อ)
pokemon3["nak"] = 115; // เพิ่มพรอเพอร์ตี nak (หนัก)
alert(pokemon3["chue"]); // ได้ ไซฮอร์น
alert(pokemon3.nak); // ได้ 115
ถ้าหากเป็นพรอเพอร์ตีที่มีอยู่แล้วจะเป็นการแก้ค่านั้น
ไม่ได้สร้างพรอเพอร์ตีใหม่ขึ้น
pokemon.lv = 34; // แก้ lv
ออบเจ็กต์ในออบเจ็กต์
สิ่งที่ใส่เป็นพรอเพอร์ตีในออบเจ็กต์จะเป็นข้อมูลชนิดไหนก็ได้
รวมถึงชนิดออบเจ็กต์
นั่นหมายความว่าอาจเกิดโครงสร้างแบบที่ว่าออบเจ็กต์เก็บอยู่ภายในออบเจ็กต์
ตัวอย่าง
phulen = {
chue: "ซาโตชิ",
pk1: {
chue: "พีคาชู",
lv: 98
},
pk2: {
chue: "ลิซาร์ดอน",
lv: 40
}
}
;
โครงสร้างเวลาสร้างอาจดูซับซ้อนขึ้นสักหน่อย
แต่ว่าโดยปกติแล้วจะอาศัยการเคาะวรรคให้ร่นเข้าไปอีกขั้นเพื่อให้เข้าใจว่านี่เป็นโครงสร้างที่อยู่ลึกเข้าไปด้านในอีก
ช่วยให้เข้าใจได้ง่ายขึ้น ลดการสับสน
เวลาสร้างออบเจ็กต์ซ้อนในออบเจ็กต์แบบนี้
เพื่อให้ดูง่ายขึ้นอาจเตรียมค่าเอาไว้ในตัวแปรนึงก่อนแล้วค่อยเอาตัวแปรนั้นมาใส่ลงในออบเจ็กต์หลัก
เช่นตัวอย่างข้างต้นอาจเขียนแบบนี้ได้ จะได้ผลเหมือนเดิม
chue = "ซาโตชิ";
pk1 = {
chue: "พีคาชู",
lv: 98
};
pk2 = {
chue: "ลิซาร์ดอน",
lv: 40
};
phulen = {
chue: chue,
pk1: pk1,
pk2: pk2
};
นอกจากนี้ อาจสร้างออบเจ็กต์เปล่าเตรียมไว้แล้วค่อยๆป้อนพรอเพอร์ตีให้ก็ได้
phulen = {};
phulen.chue = "ซาโตชิ";
phulen.pk1 = {
chue: "พีคาชู",
lv: 98
};
phulen.pk2 = {
chue: "ลิซาร์ดอน",
lv: 40
};
จะเห็นวิธีการสร้างมีอยู่หลากหลาย สามารถเลือกใช้ตามที่สะดวกได้
ส่วนการจะเข้าถึงข้อมูลที่อยู่ในออบเจ็กต์ด้านในก็อาจทำได้โดยการเขียนจุดต่อกันไปเลย
หรือจะใช้วงเล็บเหลี่ยมต่อกัน หรือปนกันก็ได้
alert(phulen.pk1.chue); // ได้ พีคาชู
alert(phulen["pk2"]["chue"]); // ได้ ลิซาร์ดอน
alert(phulen["pk1"].lv); // ได้ 98
alert(phulen.pk2["lv"]); // ได้ 40
alert(phulen.chue); // ได้ ซาโตชิ
จะเอาออบเจ็กต์ข้างในมาเก็บในตัวแปรอีกตัวก่อนแล้วค่อยเข้าถึงพรอเพอร์ตีข้างในอีกทีก็ได้
var pk = phulen.pk1;
alert(pk.chue); // ได้ พีคาชู
ออบเจ็กต์และตัวแปรที่เก็บออบเจ็กต์
เมื่อพูดถึงออบเจ็กต์ มีสิ่งที่จำเป็นจะต้องทำความเข้าใจให้ดี
เพื่อไม่ให้ทำอะไรบางอย่างผิดพลาดโดยไม่ตั้งใจ นั่นคือ
ความจริงแล้วเมื่อเราสร้างออบเจ็กต์แล้วใส่ไว้ในตัวแปรนึง
สิ่งที่ตัวแปรนั้นเก็บไม่ใช่ตัวข้อมูลที่อยู่ภายในออบเจ็กต์นั้น
แต่เป็นที่อยู่ที่ชี้ไปยังที่เก็บข้อมูล
ความหมายก็คือ สมมุติว่าเราสร้างออบเจ็กต์ป้อนให้ตัวแปรนึง
จากนั้นเราเอาค่าตัวแปรนั้นไปป้อนให้ตัวแปรอื่นอีก
var pokemon3 = {lv: 15};
var pokemonx = pokemon3;
จะได้ว่าตัวแปรทั้ง ๒ ตัวนั้นชี้ไปที่ออบเจ็กต์ตัวเดียวกัน
ค่าพรอเพอร์ตีของข้อมูลภายในออบเจ็กต์นั้นจะเข้าถึงผ่านตัวแปรไหนก็ได้เหมือนกัน
alert(pokemon3.lv); // ได้ 15
alert(pokemonx.lv); // ได้ 15
และหากมีการเปลี่ยนแปลงค่าพรอเพอร์ตีในนั้น
ความเปลี่ยนแปลงก็จะเกิดขึ้นกับทั้งสองตัวแปร เพราะถือเป็นตัวเดียวกัน
pokemonx.lv = 17; // แก้ค่า lv
alert(pokemonx.lv); // ได้ 17
alert(pokemon3.lv); // ได้ 17
เพียงแต่ระวังสับสน
ที่ว่าจะเปลี่ยนแปลงไปพร้อมกันนั้นคือเฉพาะเมื่อมีการแก้พรอเพอร์ตีเท่านั้น
แต่หากมีการป้อนค่าแทนเข้าไปที่ตัวแปรนั้นใหม่โดยตรง
ตัวแปรนั้นจะไปเก็บข้อมูลใหม่แทน
และไม่มีอะไรเกี่ยวข้องกับออบเจ็กต์ที่เคยอยู่กับตัวแปรนั้นอีก
ตัวแปรอีกตัวที่ไม่ได้ถูกแทนก็จะอยู่เหมือนเดิม
และต่อให้ทำอะไรอีกก็ไม่เกี่ยวข้องกันแล้ว
pokemonx = {lv: 21}; // เอาออบเจ็กต์ใหม่แทนลงตัวแปรเดิม
alert(pokemonx.lv); // ได้ 21
alert(pokemon3.lv); // ได้ 17
pokemon3.lv = 23;
alert(pokemon3.lv); // ได้ 23
alert(pokemonx.lv); // ได้ 21
ตรงนี้อาจเข้าใจยากและชวนสับสนสักหน่อย อาจต้องใช้เวลาคิดและทำความเข้าใจให้ดี
หากต้องการคัดลอกออบเจ็กต์โดยไม่ได้ต้องการให้เป็นออบเจ็กต์เดียวกัน
อาจต้องทำแบบนี้
var pk1 = { chue: "ฮิโตคาเงะ", lv: 14 }; // ออบเจ็กต์ต้นฉบับ
var pk2 = { chue: pk1.chue, lv: pk1.chue }; // ออบเจ็กต์ที่คัดลอกมา
แต่ก็ดูแล้วยุ่งยาก เพราะต้องมาไล่เขียนพรอเพอร์ตีทีละตัวทั้งหมด
แต่ใน ES5 มีฟังก์ชันที่ช่วยทำให้คัดลอกออบเจ็กต์ได้อย่างสะดวก
รายละเอียดอ่านใน
บทที่ ๒๘
หรืออย่างใน ES6 จะมีฟังก์ชัน Object.assign
ซึ่งก็เป็นอีกวิธีที่ช่วยทำให้คัดลอกออบเจ็กต์ได้
การตรวจดูว่าออบเจ็กต์มีคีย์ที่ต้องการอยู่หรือไม่
ทำได้โดยใช้ in โดยใส่ชื่อคีย์ ตามด้วย in แล้วตามด้วยออบเจ็กต์นั้น
var obju = {
a: 5,
"100": "!!!"
};
alert("a" in obju); // ได้ true
alert("b" in obju); // ได้ false
กรณีที่คีย์เป็นตัวเลข จะใส่ในรูปสายอักขระหรือตัวเลขก็ได้
alert("100" in obju); // ได้ true
alert(100 in obju); // ได้ true
การลบพรอเพอร์ตีออกจากออบเจ็กต์
การลบพรอเพอร์ตีที่มีอยู่ในออบเจ็กต์ออกไปทำได้โดยใช้คำสั่ง delete
พอลบไปแล้วก็จะกลายเป็นเหมือนไม่มีตัวตนอยู่อีก
ตัวอย่าง
var obra = {k: 10};
alert(obra.k); // ได้ 10
alert("k" in obra); // ได้ true
delete obra.k; // ลบ
alert(obra.k); // ได้ undefined
alert("k" in obra); // ได้ false
ออบเจ็กต์คือโครงสร้างแถวลำดับแบบจับคู่
ในภาษาโปรแกรมต่างๆจะมีข้อมูลกลุ่มประเภทที่เรียกว่า
"แถวลำดับแบบจับคู่" (associative array)
เพียงแต่ว่าในแต่ละภาษามีชื่อเรียกต่างกันไป เช่น
- ในภาษาไพธอน เรียกว่า "ดิกชันนารี" (dict)
- ในภาษารูบี เรียกว่า "แฮช" (hash)
- ในภาษา C++ เรียกว่า "โครงสร้างข้อมูล" (structure)
รายละเอียดก็อาจต่างกันออกไป
แต่โดยรวมแล้วมีสิ่งที่เหมือนกันคือเป็นข้อมูลชนิดกลุ่ม
ซึ่งประกอบด้วยข้อมูลหลายๆอย่างอยู่ด้วยกัน
โดยข้อมูลแต่ละตัวจะถูกตั้งชื่อไว้เพื่อใช้อ้างอิงได้เวลาที่ต้องการเข้าถึงข้อมูลในนั้น
ชื่อที่ใช้เข้าถึงข้อมูลนั้นเรียกว่า "คีย์" (key)
ปกติเวลาเข้าถึงข้อมูลในแถวลำดับแบบจับคู่จะทำได้โดยเขียน
["คีย์"] แบบนี้ต่อท้าย
และในภาษาที่มีแนวคิด
การเขียนโปรแกรมเชิงวัตถุ (OOP)
โดยทั่วไปจะมีสิ่งที่เรียกว่าเป็น "ออบเจ็กต์" คือเป็น
"วัตถุ" ในออบเจ็กต์จะประกอบไปด้วยสิ่งที่เรียกว่า
"พรอเพอร์ตี" (property) หรือในบางภาษาอาจเรียกว่า
"แอตทริบิวต์" (attribute)
แต่ในจาวาสคริปต์มักใช้คำว่าพรอเพอร์ตี
ที่จริงมักจะหมายถึงสิ่งเดียวกันหรือใกล้เคียงกัน
ซึ่งเวลาเข้าถึงข้อมูลในพรอเพอร์ตีจะเขียนในรูป
.ชื่อพรอเพอร์ตี แบบนี้
ออบเจ็กต์ในจาวาสคริปต์นั้นจะเห็นว่าการเข้าถึงพรอเพอร์ตีสามารถเขียนในรูป
ออบเจ็กต์["คีย์"] แบบนี้ได้
จึงมีคุณสมบัติเหมือนเป็นแถวลำดับแบบจับคู่ไปด้วย
ในขณะเดียวกัน ก็สามารถเข้าถึงข้อมูลได้โดยใช้
ออบเจ็กต์.ชื่อพรอเพอร์ตี
ได้เหมือนออบเจ็กต์ในภาษาอื่น
จึงมีคุณสมบัติเหมือนเป็นออบเจ็กต์ในภาษาอื่นไปด้วย
ดังนั้น
ออบเจ็กต์ในจาวาสคริปต์นั้นเป็นทั้งออบเจ็กต์และเป็นทั้งแถวลำดับแบบจับคู่ไปในตัว
ในขณะที่ในภาษาอื่นเช่นไพธอนหรือรูบีนั้น ออบเจ็กต์ กับ แถวลำดับแบบจับคู่
(ในไพธอนเรียก "ดิกชันนารี" ในรูบีเรียก "แฮช")
ถือเป็นคนละสิ่งกัน แม้จะมีส่วนคล้าย แต่อะไรหลายอย่างแตกต่างกันมาก
การรวมเอาออบเจ็กต์กับแถวลำดับแบบจับคู่ไว้เป็นสิ่งเดียวกันแบบนี้จึงอาจถือว่าเป็นลักษณะเฉพาะที่สำคัญอย่างหนึ่งของจาวาสคริปต์