ในภาษาจาวาสคริปต์เรามองออบเจกต์ (object) เป็นชนิดข้อมูลแบบนึงที่เก็บข้อมูลในรูปแบบของ Key และ Value โดยคีย์ (Key) เป็นเหมือนตัวแปรที่ชี้ไปยังพี้นที่ในหน่วยความจำที่เก็บข้อมูล (Value) และเราเรียกคีย์ว่าคุณสมบัติ (property) ของออบเจกต์ ข้อมูลของคุณสมบัติสามารถเป็นได้ทั้งชนิดข้อมูลต่างๆ ฟังก์ชั่น และออบเจกต์ เราประกาศออบเจกต์ด้วยรูปแบบตามตัวอย่างด้านล่างโดยทั้ง Key และ Value จะครอบด้วย ‘ ‘ และคั่นแต่ละชุดของ Key และ Vale ด้วย , แต่ถ้าคำที่ใช้เป็น Key ไม่มีอักขระพิเศษ ไม่ต้องครอบด้วย ‘ ‘ ก็ได้
let myObject = { }; // myObject เป็นออบเจกต์เปล่า
let myObject = {
‘first Property’: ‘value1’,
secondProperty: ‘value2’
}; // กำหนด Key/Value ให้กับออบเจกต์
เราสามารถเรียกใช้ค่าของคุณสมบัติได้ด้วยการใช้ . เชื่อมระหว่างชื่อออบเจกต์และชื่อคุณสมบัติ เช่น myObject.secondProperty
let myObject = {
‘first Property’: ‘value1’,
secondProperty: ‘value2’
};
console.log(myObject.secondProperty) // พิมพ์ value2
แต่ถ้าชื่อคุณสมบัติที่ใช้ประกอบด้วย ตัวเลข อักขระพิเศษ ช่องว่าง เราจะต้องครอบชื่อคุณสมบัติด้วย [ ] เช่น myObject[‘first Property’]
let myObject = {
‘first Property’: ‘value1’,
secondProperty: ‘value2’
};
console.log(myObject[‘first Property’]); // พิมพ์ value1
นอกจากนี้เรายังใช้การครอบชื่อคุณสมบัติด้วย [ ] ในส่วนของโปรแกรมที่ต้องการใช้ค่าของคุณสมบัติเมื่อเราใช้คุณสมบัติเป็นพารามิเตอร์ของฟังก์ชั่น การเรียกใช้งานในแบบฟังก์ชั่นอย่างนี้ ถ้าเราระบุในส่วนของโปรแกรมที่ต้องการใช้ค่าของคุณสมบัติด้วยรูปแบบ ชื่อออบเจกต์.ชื่อคุณสมบัติ จะกลายเป็นการอ้างถึงชื่อคุณสมบัติไม่ใช่ค่าของคุณสมบัติ
let myVar = (objectName, propName) => console.log(objectName[propName]);
myVar(myObject, ‘first Property’); // พิมพ์ value1
เราสามารถเพิ่มคุณสมบัติให้กับออบเจกต์ได้ด้วยการโดยระบุ ชื่อออบเจกต์.ชื่อคุณสมบัติใหม่ = ข้อมูล ดังตัวอย่างด้านล่าง หากเราระบุ “ชื่อคุณสมบัติใหม่” ด้วยชื่อคุณสมบัติที่มีอยู่จะเป็นการเปลี่ยนข้อมูลของคุณสมบัติ และถึงแม้จะประกาศตัวแปรออบเจกต์แบบ const ก็สามารถแก้ไข เพิ่ม หรือลบ คุณสมบัติได้
let myObject = {
‘first Property’: ‘value1’,
secondProperty: ‘value2’
};
myObject.thirdProperty = 15; // เพิ่ม Key/Value ใหม่
myObject.secondProperty = ‘value21’; // เปลี่ยนข้อมูลของ secondProperty
myObject[‘first Property’] = ‘value11’; // เปลี่ยนข้อมูลของ fisrt Property
myObject = {forthProperty : 12}; // error เนื่องจากพยายามกำหนดค่าให้กับตัวแปร myObject
เราสามารถลบคุณสมบัติ (Key/Value) ให้กับออบเจกต์ได้ด้วยคำสั่ง delete เช่น
let myObject = {
‘first Property’: ‘value1’,
secondProperty: ‘value2’
};
delete myObject[‘fisrt Property’];
delete myObject.secondProperty;
นอกจากคุณสมบัติแล้วเราสามารถกำหนดเมธอดให้กับออบเจกต์ด้วยรูปแบบ Key/Value เช่นกัน โดย Key คือชื่อเมธอด และ Value คือฟังก์ชั่น โดยมีรูปแบบ Key : function() {//ชุดคำสั่ง} และในเวอร์ชั่น ES6 จะเป็นรูปแบบ Key() {//ชุดคำสั่ง}
let myObject = {
‘first Property’: ‘value1’,
secondProperty: ‘value2’,
myMethod(){
console.log(‘myObject.myMethod is called’)
}
};
myObject.myMethod(); // พิมพ์ myObject.myMethod is called
เมื่อเราต้องการเรียกใช้งานคุณสมบัติในเมธอดเราต้องอ้างถึงคุณสมบัติด้วยคีย์เวิร์ด this เช่น
let myObject = {
‘first Property’: ‘value1’,
secondProperty: ‘value2’,
myMethod(){
console.log(this.secondProperty)
}
};
myObject.myMethod(); // พิมพ์ value2
แต่ในกรณีที่เราใช้ arrow function จะไม่สามารถใช้งานคีย์เวิร์ด this ได้ จากตัวอย่างจะพิมพ์ว่า undefined แทนที่จะเป็น value2
let myObject = {
‘first Property’: ‘value1’,
secondProperty: ‘value2’,
myMethod : () => {
console.log(this.secondProperty)
}
myObject.myMethod(); // พิมพ์ undefined
จากตัวอย่างด้านบนเราสามารถอ้างอิงด้วยชื่อออบเจกต์แทนที่การใช้คีย์เวิร์ด this ก็ได้ เช่น myObject.secondProperty แต่การใช้คีย์เวิร์ด this เป็นวิธีที่แนะนำเพื่อป้องกันปัญหาในอนาคต เช่น ถ้ามีการเปลี่ยนชื่อออบเจกต์ก็ไม่ต้องไปตามแก้ไขในจุดต่างๆ หรือ ในการสืบทอดออบเจกต์การใช้คีย์เวิร์ด this ทำให้มั่นใจว่าค่าที่ได้เป็นของออบเจกต์นั้นจริงๆ
คุณสมบัติของออบเจกต์ไม่ควรถูกเข้าถึงได้ง่ายๆ และบางคุณสมบัติของออบเจกต์เราอาจจะไม่ต้องการให้ถูกแก้ไข แต่ภาษาจาวาสคริปต์ไม่มีความสามารถในการปกป้อง (privacy) คุณสมบัติของออบเจกต์ ดังนั้นนักพัฒนาจึงสื่อสารกันด้วยการกำหนดอักขระ _ หน้าชื่อตัวแปรที่ไม่ต้องการให้ถูกแก้ไข เช่น _myVar (แต่ถ้าต้องการจะแก้ไขก็แก้ได้) ดังนั้นแทนที่เราจะปล่อยให้มีการใช้งานคุณสมบัติของออบเจกต์โดยตรง เราใช้วิธีสร้างเมธอดที่เรียกว่า getter และ setter ขึ้นมาเพื่อใช้อ่านค่าและแก้ไขค่าตามลำดับ และกำหนดในเอกสารการใช้งานว่าให้ใช้เมธอดอะไรและอย่างไรในการอ่านและแก้ไขค่า (อีกครั้ง แต่ถ้าต้องการจะแก้ไขก็แก้ได้) ข้อดีของการใช้ getter และ setter คือ เราสามารถดำเนินการกับข้อมูลก่อนที่จะส่งออกหรือบันทึกได้ เราไม่สามารถตั้งชื่อ getter และ setter ซ้ำกับชื่อของคุณสมบัติของออบเจกต์ ดังนั้นหากต้องการใช้ชื่อเดียวกันให้นำหน้าชื่อคุณสมบัติของ
ออบเจกต์ด้วยอักขระ _
การประกาศ getter ทำโดยนำหน้าเมธอดด้วยคีย์เวิร์ด get และการประกาศ setter ทำโดยนำหน้าเมธอดด้วยคีย์เวิร์ด set การเรียกใช้งาน getter และ setter จะเหมือนกับการเรียกใช้งานคุณสมบัติของออบเจกต์ คือ ไม่ต้องมี ( )
let myObject = {
_firstName : ‘Somchai’,
_lastName: ‘Jaidee’,
get fullName () {
console.log(this._firstName + ‘ ‘ + this._lastName);
},
set firstName (name) {
if (typeof name === ‘string’ ){
this._firstName = name;
} else {
console.log(‘only string required’);
}
}
}
myObject.fullName; // พิมพ์ Somchai Jaidee
myObject.firstName = 123; // พิมพ์ only string required’
myObject.firstName = ‘Aranya’;
myObject.fullName; // พิมพ์ Aranya Jaide
เราสามารถมองออบเจกต์เองเป็นชนิดข้อมูลแบบนึง ดังนั้นเราสามารถใช้ออบเจกต์ซ้อนออบเจกต์ได้เรียกว่า nested objects เช่น
let myObject = {
‘first Property’: ‘value1’,
secondProperty : ‘value2’,
innerObjectVar : {
innerObjectProperty : ‘inner value’
},
complexInnerObj : {
thisIsAlsoObj : {
thisIsAlsoObjProperty : 15
}
}
};
เมื่อเรากำหนดออบเจกต์ให้กับตัวแปร ตัวแปรจะชี้ไปยังหน่วยความจำที่เก็บออบเจกต์ เราเรียกการชี้แบบนี้ว่า pass by reference ดังนั้น ถ้าเราส่งตัวแปรออบเจกต์ผ่านพารามิเตอร์ให้ฟังก์ชั่นและมีการเปลี่ยนแปลงค่าคุณสมบัติของออบเจกต์อันเนื่องมาจากการดำเนินการในฟังก์ชั่น การเปลี่ยนแปลงนั้นจะเกิดขึ้นกับออบเจกต์จริงๆ ถึงแม้ว่าจะประกาศตัวแปรออบเจกต์แบบ const ก็ตาม เช่น
const myObject = {
‘first Property’: ‘value1’,
secondProperty : ‘value2’,
};
console.log(myObject.secondProperty); // พิมพ์ value2
let changeProperty = obj => {obj.secondProperty = ‘no value’}; //กำหนดฟังก์ชั่นเปลี่ยนค่าคุณสมบัติ
changeProperty(myObject); // เรียกใช้ฟังก์ชั่น
console.log(myObject.secondProperty); // พิมพ์ no value
เราสามารถวนรอบเพื่อดำเนินการกับแต่ละคุณสมบัติในออบเจกต์ๆได้ด้วยคำสั่ง for …in โดยรูปแบบคือ
for (ประกาศตัวแปร in ออบเจกต์) {ชุดคำสั่งที่ต้องการดำเนินการกับคุณสมบัติ}
let spaceship = {
crew: {
captain: {
name: ‘Lily’,
degree: ‘Computer Engineering’,
cheerTeam() { console.log(‘You got this!’) }
},
‘chief officer’: {
name: ‘Dan’,
degree: ‘Aerospace Engineering’,
agree() { console.log(‘I agree, captain!’) }
},
medic: {
name: ‘Clementine’,
degree: ‘Physics’,
announce() { console.log(Jets on!) } },
translator: {
name: ‘Shauna’,
degree: ‘Conservation Science’,
powerFuel() { console.log(‘The tank is full!’) }
}
}
};
// for…in
for (let crewMember in spaceship.crew) {
console.log(${crewMember}: ${spaceship.crew[crewMember].name});
}
// result
captain: Lily
chief officer: Dan
medic: Clementine
translator: Shauna
จากตัวอย่างที่ผ่านมาเป็นการประกาศออบเจกต์ทีละตัวเพราะเราระบุค่า (value) ให้กับคีย์ (key) ไว้เลย การประกาศออบเจกต์แบบนี้เราเรียกว่า object literal หรือ plain object หรือ initializer object แต่ถ้าเราต้องการประกาศหรือสร้างออบเจกต์ขึ้นมาหลายๆตัวโดยเป็นออบเจกต์ชนิดเดียวกันแต่มีค่าของคุณสมบัติต่างกันเราจะใช้รูปแบบ factory fucntion และเราจะสร้างออบเจกต์จากการเรียกใช้ factory fucntion อีกทีหนึ่ง ดังตัวอย่าง
const monsterFactory = (name, age, energySource, catchPhrase) => {
return {
name: name,
age: age,
energySource: energySource,
scare() {
console.log(catchPhrase);
}
}
};
// สร้างออบเจกต์แต่ละตัว
const ghost = monsterFactory(‘Ghouly’, 251, ‘ectoplasm’, ‘BOO!’);
ghost.scare(); // ‘BOO!’
ในภาษาจาวาสคริปต์เวอร์ชั่น ES6 เป็นต้นมา เราสามารถลดความซ้ำซ้อนในการกำหนดคุณสมบัติในรูปแบบ factory fucntion ได้ เรียกว่าเทคนิก destructuring โดยใช้วิธี property value shorthand
const monsterFactory = (name, age, energySource, catchPhrase) => {
return {
name, // <– property value shorthand
age, // <– property value shorthand
energySource: energySource,
scare() {
console.log(catchPhrase);
}
}
};
การอ่านค่าของคุณสมบัติเพื่อมาเก็บในตัวแปรทำได้ 2 แบบคือแบบที่ประกาศตัวแปรและกำหนดค่าให้กับตัวแปรโดยการอ้างอิง ชื่อออบเจกต์.ชื่อคุณสมบัติ หรือจะใช้เทคนิก destructured assignment ซึ่งเราจะได้ตัวแปรชื่อเดียวกับคุณสมบัติโดยอัตโนมัติ เช่น
const vampire = {
name: ‘Dracula’,
residence: ‘Transylvania’,
preferences: {
day: ‘stay inside’,
night: ‘satisfy appetite’
}
};
// แบบที่ประกาศตัวแปรและกำหนดค่าให้กับตัวแปรโดยการอ้างอิง ชื่อออบเจกต์.ชื่อคุณสมบัติ
const residence = vampire.residence;
console.log(residence); // พิมพ์ Transylvania
// destructured assignment ได้ตัวแปรชื่อเดียวกับคุณสมบัติ
const { residence } = vampire;
console.log(residence); // พิมพ์ Transylvania
const { day } = vampire.preferences;
console.log(day); // พิมพ์ stay inside
เราสามารใช้ destructured assignment กับการใช้เมธอดของคุณสมบัติได้เหมือนกัน เช่น
const robot = {
model: ‘1E78V2’,
energyLevel: 100,
functionality: {
beep() {
console.log(‘Beep Boop’);
},
fireLaser() {
console.log(‘Pew Pew’);
},
}
};
// เรียกใช้เมธอดของคุณสมบัติ
const {functionality} = robot;
functionality.beep();
นอกจากการสร้างออบเจกต์แบบ literal แล้ว เราสามารถสร้างออบเจกต์ได้ด้วย object constructor และกำหนดคุณสมบัติในภายหลัง เช่น
const myObject = new Object();
myObject.property1 = ‘Tiger’;
เนื่องจากออบเจกต์ทุกออบเจกต์ที่เราสร้างขึ้นมาจะสืบทอดมาจาก Object.prototype ดังนั้นจะมีเมธอดพื้นฐานที่ได้รับมาด้วยซึ่งเราสามารถใช้งานได้เรียกว่า built-in object method เช่น .hasOwnProperty(), .valueOf(), .entries() เป็นต้น สามารถดูรายละเอียดได้จาก https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#Methods_of_the_Object_constructor ตัวอย่างการใช้ built-in object
const robot = {
model: ‘SAL-1000’,
mobile: true,
sentient: false,
armor: ‘Steel-plated’,
energyLevel: 75
};
// อ่านชื่อคุณสมบัติ
const robotKeys = Object.keys(robot);
console.log(robotKeys);
console.log(‘===================================’);
// แสดง key/value
const robotEntries = Object.entries(robot);
console.log(robotEntries);
console.log(‘===================================’);
// สร้างออบเจตก์ใหม่และสำเนาคุณสมบัติจากออบเจกต์อื่นมาด้วย
const o = {laserBlaster: true, voiceRecognition: true};
const newRobot = Object.assign(o,robot);
console.log(newRobot);