Skip to main content

Destructuring

Destructuring assignment

Data structure 2 ตัวที่ใช้งานได้มากที่สุด คือ Array และ Object

  • Object ทำให้เราสามารถสร้างสิ่งที่เก็บข้อมูลโดย key ได้
  • Array ทำให้เราสามารถเก็บข้อมูลหลาย ๆ ตัว เรียงกันไว้ได้

อย่างไรก็ตาม เมื่อเราส่งข้อมูลเข้าไปใน function บางทีเราอาจไม่ต้องการข้อมูลทั้งหมดของมัน เช่น มันอาจต้องการแค่ข้อมูลเดียว หรือ property ตัวเดียว

Destructuring assignment เป็น syntax พิเศษที่ช่วยทำให้ “unpack” Array หรือ Object ให้เป็นข้อมูลที่ใช้งานได้ง่ายมากขึ้น

Destructuring ก็สามารถทำงานได้ดีกับ function ที่มี่หลาย parameter, default values, และ อื่น ๆ


Array destructuring

ตัวอย่างในการ destructured array เป็นตัวแปร

// we have an array with a name and surname
let arr = ["John", "Smith"]

// destructuring assignment
// sets firstName = arr[0]
// and surname = arr[1]
let [firstName, surname] = arr;

alert(firstName); // John
alert(surname); // Smith

ตอนนี้ เราสามารถใช้งานตัวแปร จากสมาชิกของ Array ได้

คราวนี้ เราลองมาใช้กับ split method หรือ Array method อื่น ๆ

let [firstName, surname] = "John Smith".split(' ');
alert(firstName); // John
alert(surname); // Smith

อย่างที่คุณเห็น มันสามารถใช้งานได้ง่ายมาก แต่มันยังมีรายละเอียดลึกกว่านี้อีกนิดหน่อย

The rest ‘…’

โดยปกติแล้ว ถ้า Array มีมากกว่าตัวแปรที่จะรับ

ตัวอย่างเช่น โค้ดด้านล่างนี้ มีแค่ 2 ตัวที่ได้รับ และตัวที่เหลือถูกละเว้น

let [name1, name2] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert(name1); // Julius
alert(name2); // Caesar
// Further items aren't assigned anywhere

ถ้าเราต้องการตัวที่เหลือมาด้วย - เราสามารถเพิ่มตัวแปรอีกตัวนึงได้ เพื่อรับค่าที่เหลือทั้งหมดมา โดยการใช้จุดสามจุด "...":


let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

// rest is an array of items, starting from the 3rd one
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2

ค่าของ rest จะเป็นค่าที่เหลือของ element ที่เหลือ

เราสามารถใช้ชื่ออื่น แทนการใช้ rest ได้ ทำให้มั่นใจว่าจุดสามจุดอยู่หน้าชื่อตัวแปร

let [name1, name2, ...titles] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
// now titles = ["Consul", "of the Roman Republic"]

Default values

ถ้า Array มีน้อยกว่าตัวที่เราจะรอรับทางด้านซ้าย มันจะไม่มี Error แต่มันจะเป็น undefined แทน

let [firstName, surname] = [];

alert(firstName); // undefined
alert(surname); // undefined

ถ้าเราต้องการค่า “default” เพื่อกำหนดค่าเริ่มต้นของตัวที่ไม่ได้กำหนดค่า เราสามารถใช้ = ในการกำหนดได้

// default values
let [name = "Guest", surname = "Anonymous"] = ["Julius"];

alert(name); // Julius (from array)
alert(surname); // Anonymous (default used)

ค่าเริ่มต้น สามารถซับซ้อนกว่านี้ได้ หรือ สามารถเรียกใช้ function

ตัวอย่างเช่น เราสามารถใช้ prompt ในการกำหนดค่าเริ่มต้นได้

// runs only prompt for surname
let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];

alert(name); // Julius (from array)
alert(surname); // whatever prompt gets

จำไว้ว่า prompt จะทำงานแค่เมื่อค่านั้น เป็นค่าที่หายไป (surname).


Object destructuring

destructuring assignment ก็สามารถทำงานกับ Object ได้เหมือนกัน

syntax อย่างง่าย

let {var1, var2} = {var1:, var2:}

โดยเราจะมี Object อยู่ด้านขวา และแบ่งเข้าไปในตัวแปรแต่ละตัว โดยด้านซ้ายเก็บค่าเป็น “pattern” เรียงตาม ชื่อที่เหมือนกัน

ตัวอย่างเช่น

let options = {
title: "Menu",
width: 100,
height: 200
};

let {title, width, height} = options;

alert(title); // Menu
alert(width); // 100
alert(height); // 200

การเรียงลำดับไม่จำเป็น เพราะมันจะเก็บค่าเข้าตัวแปร ที่มีชื่อเหมือนกัน

// changed the order in let {...}
let {height, width, title} = { title: "Menu", height: 200, width: 100 }

pattern ทางด้านซ้ายจะ map ระหว่าง property และ variable เข้าด้วยกัน

ถ้าเราต้องการจะกำหนดค่าเข้าไปที่ตัวแปรที่มีชื่อต่างจากตัวเดิม เช่น options.width เข้าไปตัวแปร w ในกรณีนั้นเราสามารถตั้งชื่อตัวแปรโดยใช้ colon ได้

let options = {
title: "Menu",
width: 100,
height: 200
};

// { sourceProperty: targetVariable }
let {width: w, height: h, title} = options;

// width -> w
// height -> h
// title -> title

alert(title); // Menu
alert(w); // 100
alert(h); // 200

colon แสดงถึง “จาก: ไปที่” ตัวอย่างเช่น จาก property width ไปที่ w , property height ไปที่ h, และ title กำหนดไปที่ตัวแปรที่มีชื่อเดียวกัน

ในกรณีที่มีตัวที่ไม่มีค่า เราสามารถใช้ "=" ในการกำหนดค่าเริ่มต้นได้

let options = {
title: "Menu"
};

let {width = 100, height = 200, title} = options;

alert(title); // Menu
alert(width); // 100
alert(height); // 200

เหมือนกับ Array เราสามารถดึงค่าจาก function มาเป็นค่าเริ่มต้นได้

ในตัวอย่างข้างล่าง prompt จะถาม width แต่จะไม่ถาม title

let options = {
title: "Menu"
};

let {width = prompt("width?"), title = prompt("title?")} = options;

alert(title); // Menu
alert(width); // (whatever the result of prompt is)

เราสามารถรวมการใช้ colon และการกำหนดค่าเริ่มต้นได้

let options = {
title: "Menu"
};

let {width: w = 100, height: h = 200, title} = options;

alert(title); // Menu
alert(w); // 100
alert(h); // 200

ถ้าเราต้องการจะใช้แค่ตัวเดียว เราก็สามารถเลือกกำหนดแค่ตัวเดียวได้

let options = {
title: "Menu",
width: 100,
height: 200
};

// only extract title as a variable
let { title } = options;

alert(title); // Menu

The rest pattern “…”

จะเกิดอะไรขึ้นเมื่อเรามี properties มากกว่าที่เราจะรับ เราสามารถจะใช้ “rest” ได้หรือไม่

เราสามารถใช้ “rest” ได้เหมือนกับ Array เลย แต่ว่าบาง Browser อาจจะไม่ซัพพอร์ต แต่ Browser สมัยใหม่รับรอง

let options = {
title: "Menu",
height: 200,
width: 100
};

// title = property named title
// rest = object with the rest of properties
let {title, ...rest} = options;

// now title="Menu", rest={height: 200, width: 100}
alert(rest.height); // 200
alert(rest.width); // 100

Nested destructuring

ถ้า Object หรือ Array เก็บค่าที่เป็น Object หรือ Array ซ้อนไว้ เราสามารถใช้การกำหนดค่าที่ซับซ้อนขึ้นได้ เพื่อการกำหนดค่าที่ลึกขึ้น

ในโค้ดด้านล่าง options มี Object อยู่ใน proerty size และมี Array ใน property items โดยโครงสร้างของตัวแปรใหม่ต้องคล้าย ๆ กับ โครงสร้างตัวเก่า

 let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true
};

// destructuring assignment split in multiple lines for clarity
let {
size: { // put size here
width,
height
},
items: [item1, item2], // assign items here
title = "Menu" // not present in the object (default value is used)
} = options;

alert(title); // Menu
alert(width); // 100
alert(height); // 200
alert(item1); // Cake
alert(item2); // Donut

ตัวแปรทั้งหมดของ option ถูกกำหนดเข้าไปในตัวแปรใหม่ ยกเว้น extra ที่ไม่ได้รับค่าเข้ามา

image.png

สุดท้ายนี้ เรามีตัวแปร widthheightitem1item2 และ title จากค่าของ option

โปรดจำไว้ว่า ไม่มีตัวแปรให้กับ size และ items

Smart function parameters

สมมติว่าเรามี function หนึ่งใช้สร้าง menu ขึ้นมา โดยรับ parameter เข้ามาเป็น width, height, title และ item list และอื่น ๆ

นี่เป็นวิธีที่ค่อนข้างแย่ในการเขียน function

function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}

ในการทำงานจริง เราอาจลืมการเรียงลำดับของ arguments หรือไม่ IDE ที่เราใช้ อาจจะช่วยเรา แต่อีกปัญหานึงเมื่อเราส่งค่าเข้าไปใน function และเราไม่ต้องการจะใส่ค่าบางค่าเข้าไป เราจะทำอย่างไร

อย่างนี้หรอ?

// undefined where default values are fine
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])

นั่นค่อนข้างดูไม่ดี และอ่านยากมาก เมื่อเราต้องรับกับการส่งค่า parameter หลาย ๆ ตัว

การ Destructuring จะเข้ามาช่วยในการทำงานเราให้ง่ายขึ้น

เราสามารถส่ง parameter ที่เป็น Object เข้าไปได้ และ มันจะ destructurizes เป็นตัวแปรให้

// we pass object to function
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};

// ...and it immediately expands it to variables
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
// title, items – taken from options,
// width, height – defaults used
alert( `${title} ${width} ${height}` ); // My Menu 200 100
alert( items ); // Item1, Item2
}

showMenu(options);

เราสามารถใช้การ destructuring ที่ซับซ้อนกว่านี้ได้ โดยการใช้ colon :

let options = {
title: "My menu",
items: ["Item1", "Item2"]
};

function showMenu({
title = "Untitled",
width: w = 100, // width goes to w
height: h = 200, // height goes to h
items: [item1, item2] // items first element goes to item1, second to item2
}) {
alert( `${title} ${w} ${h}` ); // My Menu 100 200
alert( item1 ); // Item1
alert( item2 ); // Item2
}

showMenu(options);

รูปแบบของ syntax แบบเต็ม ๆ เขียนแบบนี้

function({
incomingProperty: varName = defaultValue
...
})

ดังนั้น สำหรับ parameter ที่เป็น Object เราจะใช้ตัวแปร varName สำหรับ incomingProperty โดยมี defaultValue เป็นค่าเริ่มต้น

โปรดจำไว้ว่า showMenu() จะต้องมี argument ถึงแม้เราจะกำหนดค่าเริ่มต้นหมดแล้ว แต่เราก็ต้องใส่ตัวแปรลงไปอยู่ดี โดยถ้าเราไม่อยากใส่ค่าอะไรลงไป เราก็แค่ใส่ Object เปล่าลงไป

showMenu({}); // ok, all values are default

showMenu(); // this would give an error

เราสามารถแก้ไขได้โดย ทำให้ทั้งก้อน มีค่าเริ่มต้นเป็น Object เปล่า

function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
alert( `${title} ${width} ${height}` );
}

showMenu(); // Menu 100 200

Summary

  • Destructuring สามารถทำให้เรา Map ตัวแปรจาก Object หรือ Array เข้าไปในตัวแปรได้

  • รูปแบบ syntax เต็ม ๆ ของ Object

    let {prop : varName = defaultValue, ...rest} = object

    นี่แปลว่า ตัวแปร prop จาก object จะไปเป็นค่าของ varName และถ้าค่านั้นไม่มีอยู่ มันจะใช้ค่า defaultValue จะถูกใช้ และ properties ของ object ที่เหลือ จะเข้าไปอยู่ในตัวแปร rest

  • รูปแบบ syntax เต็ม ๆ ของ Array

    let [item1 = defaultValue, item2, ...rest] = array

    element ตัวแรก เข้าไปที่ item1 ตัวที่สองเข้าไปที่ item2 และตัวที่เหลือเข้าไปที่ rest

  • มันเป็นไปได้ที่เราจะคัดลอกค่าจาก nested arrays/objects, แต่ว่าโครงสร้างจะต้องเหมือนกับตัวที่ต้องการจะคัดลอก