การวนซ้ำพรอเพอร์ตีในออบเจ็กต์ด้วย for๛in
ใน
บทที่ ๘
ได้เขียนถึงเรื่องการเขียนโปรแกรมให้มีการวนซ้ำโดยใช้ for และ while ไปแล้ว
แต่นอกจากนั้นแล้วยังมีอีกวิธีที่สามารถทำได้ คือวิธีการที่เรียกว่า for๛in
for ในที่นี้ต่างจาก for ที่แนะนำไปใน
บทที่ ๘
ซึ่งใช้ for เดี่ยวๆไม่มี in อยู่ด้วย เมื่อใช้ร่วมกับ in
แล้วความหมายและการทำงานจึงเปลี่ยนไป จึงถือเป็นคนละโครงสร้างการใช้งาน
for๛in นั้นจะทำได้ต่อเมื่อมีออบเจ็กต์ และออบเจ็กต์นั้นมีพรอเพอร์ตีอยู่
เราสามารถทำการวนซ้ำเพื่อไล่จัดการกับพรอเพอร์ตีในออบเจ็กต์ทีละตัวได้โดยใช้
for๛in วิธีการใช้เป็นดังนี้
for (var ชื่อตัวแปรที่จะรับคีย์ในแต่ละรอบ in ออบเจ็กต์) {
สิ่งที่ต้องการทำ
}
ตัวอย่าง
var rakha = {
soba: 100,
ramen: 110,
udon: 120
};
var s = "";
for (var ahan in rakha) {
s += ahan + " ราคา " + rakha[ahan] + " บาท, ";
}
alert(s); // ได้ soba ราคา 100 บาท, ramen ราคา 110 บาท, udon ราคา 120 บาท,
จะเห็นว่าเมื่อใช้ for in แล้ว ตัวแปร ahan จะแทนคีย์ของออบเจ็กต์ทีละตัว
รอบแรกแทน soba ต่อมาแทน rame แล้วก็ udon
การวนซ้ำในแถวลำดับด้วย for๛in
แถวลำดับก็เป็นออบเจ็กต์ชนิดหนึ่ง จึงสามารถใช้ for๛in เพื่อวนซ้ำได้ โดยเมื่อใช้
จะได้ค่า 0,1,2 ตามลำดับ
var ar = ["ก", "ข", "ค"];
alert(ar); // ได้ ก,ข,ค
for (var a in ar) {
alert(a + ": " + ar[a]); // ได้ 0: ก, 1: ข, 2: ค ตามลำดับ
}
อย่างไรก็ตาม หากมีค่าระหว่างทางที่ไม่ได้ถูกป้อนค่าไว้
ก็จะไม่ได้ไล่เรียงเลขไปทั้งหมด แต่โดดข้าม
var kaeng = ["แกงเขียวหวาน", "แกงฮังเล"];
kaeng[5] = "แกงกะหรี่";
alert(kaeng); // ได้ แกงเขียวหวาน,แกงฮังเล,,,,แกงกะหรี่
var s = "";
for (var k in kaeng) {
s += k + "~" + kaeng[k] + "] ";
}
alert(s); // ได้ 0~แกงเขียวหวาน] 1~แกงฮังเล] 5~แกงกะหรี่]
อีกทั้งแถวลำดับเองก็สามารถใส่พรอเพอร์ตีที่มีคีย์ที่ไม่ใช่ตัวเลขลงไปได้เช่นเดียวกับออบเจ็กต์ทั่วไป
หากใส่ลงไปแล้ว เวลาใช้ for in แล้ว คีย์นั้นก็จะออกมาด้วย
ดังนั้นโดยทั่วไปแล้ว มักไม่นิยมใช้ for๛in ในการวนซ้ำของแถวลำดับ
เพียงแต่ว่าใน ES6 มีรูปแบบการวนซ้ำที่เรียกว่า for๛of เพิ่มเข้ามา
วิธีนั้นนิยมใช้กับแถวลำดับ รายละเอียดเกี่ยวกับ for๛of ดูได้ที่
บทที่ ๓๗ ซึ่งอยู่ในส่วนของเนื้อหา ES6
อนึ่ง หากใครใช้ภาษาไพธอนหรือรูบีมาจะพบว่ามีกาารวนซ้ำด้วย for๛in เหมือนกัน แต่
for๛in ของไพธอนและรูบีนั้นจะคล้ายกับ for๛of ของจาวาสคริปต์มากกว่า ต่างจาก
for๛in ของจาวาสคริปต์ ดังนั้นระวังสับสน
พรอเพอร์ตีที่ใช้ใน for๛in ได้
จะเห็นว่า for๛in นั้นจะไล่เรียงพรอเพอร์ตีที่มีอยู่ในออบเจ็กต์ออกมา
แต่ว่าที่จริงแล้วออบเจ็กต์นั้นล้วนมีพรอเพอร์ตีติดตัวอยู่
เป็นสิ่งที่มีอยู่แล้วโดยไม่ได้ป้อนให้ เช่น .constructor หรือพวกเมธอดต่างๆอย่าง
.hasOwnProperty
ส่วนในออบเจ็กต์ที่เป็นแถวลำดับนั้นก็มีพรอเพอร์ตีอย่าง .length
และเมธอดอีกมากมายอย่าง .join, .sort, .push, ฯลฯ
ทั้งหมดนี้ล้วนเป็นพรอเพอร์ตี แต่เมื่อใช้ for๛in กลับไม่ถูกไล่เรียงออกมาด้วย
นั่นหมายความว่ามีพรอเพอร์ตีที่ใช้ for๛in ได้และไม่ได้อยู่
โดยทั่วไปแล้วพวกพรอเพอร์ตีและเมธอดส่วนหนึ่งที่มีอยู่ในตัวออบเจ็กต์ตั้งแต่แรกจะไม่ถูกไล่เรียงใน
for๛in
นอกจากนี้ ออบเจ็กต์ในตัวของจาวาสคริปต์ เช่น Math
เองก็ประกอบด้วยเมธอดและตัวแปรที่เป็นค่าคงที่ต่างๆมากมาย
แต่ก็ไม่สามารถไล่เรียงใน for๛in ได้เช่นกัน
แต่ออบเจ็กต์พวกนี้ก็สามารถเพิ่มพรอเพอร์ตีลงไปได้เช่นกัน
แล้วพรอเพอร์ตีนั้นก็ถูกไล่เรียงใน for๛in ได้ด้วย
Math.a = 0;
for (var m in Math) {
alert(m); // ได้ a
}
จะได้ว่ามีแต่ a ที่ใส่เข้าไปใหม่เท่านั้นที่ปรากฏเมื่อใช้ for๛in
แต่แม้จะไม่ปรากฏใน for๛in แต่พรอเพอร์ตีพวกนี้ก็มีตัวตนอยู่จริง เมื่อใช้ in
หรือ .hasOwnProperty ก็จะได้ true
เช่น PI ใน Math
alert("PI" in Math); // ได้ true
alert(Math.hasOwnProperty("PI")); // ได้ true
เมธอด propertyIsEnumerable
การตรวจสอบว่าพรอเพอร์ตีไหนปรากฏใน for๛in ได้ อาจใช้เมธอด propertyIsEnumerable
เมธอดนี้จะให้ค่า true เมื่อใส่ชื่อพรอเพอร์ตีที่ปรากฏใน for๛in ได้ และให้ false
หากใส่ชื่อพรอเพอร์ตีที่ไล่เรียงใน for๛in ไม่ได้ หรือไม่มีอยู่
ตัวอย่าง
alert(Math.propertyIsEnumerable("PI")); // ได้ false
var obj = { a: 1 };
alert(obj.propertyIsEnumerable("a")); // ได้ true
var arr = ["ก", "ข", "ค"];
alert(arr.propertyIsEnumerable(1)); // ได้ true
alert(arr.propertyIsEnumerable(3)); // ได้ false
alert(arr.propertyIsEnumerable("length")); // ได้ false
พรอเพอร์ตีของโพรโทไทป์และออบเจ็กต์ที่รับทอด
หากมีการรับทอด หรือมีการใส่พรอเพอร์ตีลงไปให้โพรโทไทป์
พรอเพอร์ตีเหล่านั้นก็จะปรากฏใน for๛in ด้วยเช่นกัน
ตัวอย่าง
var Mew = function(chue) {
this.chue = chue;
};
Mew.prototype.praphet = "เอสเปอร์";
Mew.prototype.namnak = 4.0;
var Mewtwo = function(chue, chuelen) {
Mew.call(this, chue);
this.chuelen = chuelen;
};
Mewtwo.prototype = new Mew();
Mewtwo.prototype.namnak = 127.0;
var mewtwo = new Mewtwo("มิวทู", "พระเจ้ามิวที่สอง");
var s = [];
for (var p in mewtwo) {
s.push(p);
}
alert(s); // ได้ chue,chuelen,namnak,praphet
อย่างไรก็ตาม เมื่อมาลองใช้ propertyIsEnumerable ดูจะพบว่า
เฉพาะพรอเพอร์ที่ถูกป้อนให้ตัวอินสแตนซ์โดยตรงเท่านั้นที่จะได้ true
ส่วนพรอเพอร์ตีในโพรโทไทป์จะได้ false
alert(mewtwo.propertyIsEnumerable("chue")); // ได้ true
alert(mewtwo.propertyIsEnumerable("chuelen")); // ได้ true
alert(mewtwo.propertyIsEnumerable("praphet")); // ได้ false
alert(mewtwo.propertyIsEnumerable("namnak")); // ได้ false
ดังนั้นจะเห็นว่าพรอเพอร์ตีในโพรโทไทป์ก็สามารถปรากฏใน for๛in ได้
เพียงแต่ไม่สามารถตรวจสอบด้วย .propertyIsEnumerable ได้
เมธอดนี้สนเฉพาะพรอเพอร์ตีภายในตัวอินสแตนซ์เองเท่านั้น
ใช้กับพรอเพอร์ตีในโพรโทไทป์ไม่ได้
พรอเพอร์ตีของออบเจ็กต์ global ในเบราว์เซอร์
ออบเจ็กต์ global นั้นมีพรอเพอร์ตีอยู่มากมาย และแต่ละอันนั้นสามารถปรากฏเมื่อใช้
for๛in
ลองไล่พรอเพอร์ตีของ global ในเบราว์เซอร์
var s = [];
for (k in this) {
s.push(k);
}
alert(s.sort().join(" "));
ได้ผลลัพธ์ออกมามากมาย
InstallTrigger addEventListener alert applicationCache atob blur btoa caches
cancelAnimationFrame cancelIdleCallback captureEvents clearInterval
clearTimeout close closed confirm createImageBitmap crypto customElements
devicePixelRatio dispatchEvent document dump event external fetch find focus
frameElement frames fullScreen getComputedStyle getDefaultComputedStyle
getSelection history indexedDB innerHeight innerWidth isSecureContext length
localStorage location locationbar matchMedia menubar moveBy moveTo
mozInnerScreenX mozInnerScreenY mozPaintCount name navigator onabort
onabsolutedeviceorientation onafterprint onanimationcancel onanimationend
onanimationiteration onanimationstart onauxclick onbeforeprint
onbeforeunload onblur oncanplay oncanplaythrough onchange onclick onclose
oncontextmenu oncuechange ondblclick ondevicelight ondevicemotion
ondeviceorientation ondeviceproximity ondrag ondragend ondragenter
ondragexit ondragleave ondragover ondragstart ondrop ondurationchange
onemptied onended onerror onfocus ongotpointercapture onhashchange oninput
oninvalid onkeydown onkeypress onkeyup onlanguagechange onload onloadeddata
onloadedmetadata onloadend onloadstart onlostpointercapture onmessage
onmessageerror onmousedown onmouseenter onmouseleave onmousemove onmouseout
onmouseover onmouseup onmozfullscreenchange onmozfullscreenerror onoffline
ononline onpagehide onpageshow onpause onplay onplaying onpointercancel
onpointerdown onpointerenter onpointerleave onpointermove onpointerout
onpointerover onpointerup onpopstate onprogress onratechange onreset
onresize onscroll onseeked onseeking onselect onselectstart onshow onstalled
onstorage onsubmit onsuspend ontimeupdate ontoggle ontransitioncancel
ontransitionend ontransitionrun ontransitionstart onunload onuserproximity
onvolumechange onvrdisplayactivate onvrdisplayconnect onvrdisplaydeactivate
onvrdisplaydisconnect onvrdisplaypresentchange onwaiting
onwebkitanimationend onwebkitanimationiteration onwebkitanimationstart
onwebkittransitionend onwheel open opener origin outerHeight outerWidth
pageXOffset pageYOffset parent performance personalbar postMessage print
prompt releaseEvents removeEventListener requestAnimationFrame
requestIdleCallback resizeBy resizeTo s screen screenLeft screenTop screenX
screenY scroll scrollBy scrollByLines scrollByPages scrollMaxX scrollMaxY
scrollTo scrollX scrollY scrollbars self sessionStorage setInterval
setResizable setTimeout sidebar sizeToContent speechSynthesis status
statusbar stop toolbar top u2f updateCommands window
ทั้งหมดนี้เป็นสิ่งที่ติดตัวเบราว์เซอร์
และสามารถเรียกใช้และควบคุมเพื่อทำอะไรต่างๆในหน้าเว็บได้