ในหัวข้อนี้ ผมจะพูดถึง Metaprogramming ใน Javascript
- Introspectionหมายถึง รหัสสามารถตรวจสอบตัวเองได้ สามารถอ่านโครงสร้างของโปรแกรมได้
- Self-modification : หมายความว่าโค้ดสามารถเปลี่ยนแปลงตัวเองได้
- การขอร้อง : หมายความว่ารหัสสามารถเปลี่ยนความหมายได้ รหัสมีความสามารถในการตัดสินใจด้วยตัวเอง
ลองมาดูโค้ดนี้กัน
เราสร้างวัตถุที่เรียกว่ารถยนต์และเรากำลังใช้วิธี Object.keys และส่งวัตถุรถยนต์ของเราเพื่อรับกุญแจทั้งหมดของมัน Object มีหลายวิธีในการรับคุณสมบัติทั้งหมด ซึ่งหมายความว่ามีความสามารถในการอ่านโค้ดและส่งคืนคุณสมบัติในกรณีนี้ ปรากฎว่าฉันกำลังเขียนโค้ดเพื่อตรวจสอบโค้ดของฉัน (โดยเฉพาะในส่วนนี้ case car object) และรับคุณสมบัติของมัน จากนั้นฉันก็วนซ้ำแล้วพิมพ์ออกมา ดังนั้น Object.keys คือ Introspection
การปรับเปลี่ยนตัวเอง
ลองมาเป็นตัวอย่างสำหรับมัน
เราเขียนฟังก์ชันที่รับอาร์กิวเมนต์และข้างคำสั่ง if มันจะคืนค่าผลรวมของตัวเลขสองตัวนี้ แต่เมื่อเราเรียกใช้ฟังก์ชันนี้และส่งค่าอาร์กิวเมนต์แรกที่มากกว่าหรือเท่ากับ 3 ฟังก์ชันจะเปลี่ยนตัวเองและตอนนี้จะคืนค่า 0 ทุกครั้งที่เราเรียกฟังก์ชันนี้ และไม่ว่าเราจะส่งผ่านค่าอาร์กิวเมนต์ใดก็ตาม
การขอร้อง — รหัสตัดสินใจโดยอิงจากตัวมันเอง
ดังที่เราได้กล่าวไปแล้วนี้เกี่ยวกับการเปลี่ยนแปลงความหมายของภาษา ลองมาดูใน Object.defineProperty ต้องใช้อาร์กิวเมนต์ 3 รายการ วัตถุจริง คุณสมบัติ และวัตถุที่เราเรียกว่าตัวอธิบายคุณสมบัติ เรามีตัวอธิบายสองประเภท: ตัวอธิบายข้อมูลและตัวอธิบายการเข้าถึง ตัวอธิบายข้อมูลเป็นคุณสมบัติที่มีค่าที่อาจเขียนได้หรือไม่ได้ accessor descriptor เป็นคุณสมบัติที่อธิบายโดยคู่ของฟังก์ชัน getter-setter ลองทำตัวอย่างด้วยdata descriptor
ด้วยการกำหนดคุณสมบัติและด้วยตัวอธิบาย เราได้เปลี่ยนพฤติกรรมของคุณสมบัติอายุฉันทำให้คุณสมบัติเป็นแบบอ่านอย่างเดียวโดยกำหนดให้เขียนเป็นเท็จ ถ้าฉันจะพยายามกำหนดค่าบางอย่างให้กับคุณสมบัติอายุ มันจะโยน TypeError ในโหมดเข้มงวด โดยการตั้งค่านับได้เป็นเท็จ เราจะซ่อนมันจากการวนซ้ำ ตัวอย่างเช่นObject.keys(exampleObject)ไม่รวมคุณสมบัติอายุอีกต่อไป มีวิธีการสะท้อนอีกมากมายสำหรับ Javascript Object
แต่จะเกิดอะไรขึ้นถ้าสำหรับบางวิธีของ Object Intercession คุณสมบัตินั้นไม่ถูกกำหนดสำเร็จ ตัวอย่างเช่น หากเราพยายามระบุตัวเข้าถึงและค่าพร้อมกันใน descriptor ก็จะส่ง TypeError ออกมา
เพื่อหลีกเลี่ยง พฤติกรรมนี้ เรามีReflect Reflectเป็นวัตถุสแตติกในตัวที่มีเมธอด Javascript Reflection คุณสมบัติและวิธีการทั้งหมดReflectเป็นแบบคงที่ (เช่นเดียวกับMathวัตถุ) มันมีเมธอดบางอย่างที่คล้ายกับ Object เช่นdefineProperty แต่มีความแตกต่างกันเล็กน้อย หากเกิดข้อผิดพลาดใด ๆ ก็จะส่งกลับเป็นเท็จ จากตัวอย่างก่อนหน้านี้ เราสามารถตรวจสอบว่า (Reflect.defineProperty(…)) เป็นจริง แล้วดำเนินการต่อหรือไม่ ดังนั้น Reflect static method จะคืนค่าบูลีน
สะท้อนนำไปใช้
วิธีหนึ่งที่มีประโยชน์ที่สุดคือReflect.apply(target, context, args);
มันใช้ฟังก์ชันเป้าหมาย ค่าของสิ่งนี้มีไว้สำหรับการเรียกไปยังเป้าหมายและรายการอาร์กิวเมนต์ ตามบริบท จะเรียกฟังก์ชันเป้าหมายและส่งอาร์กิวเมนต์เป็นรายการอาร์กิวเมนต์
สะท้อน.สร้าง
Reflect.construct ()เป็นวิธีการแบบสแตติกที่ทำหน้าที่เหมือนตัวดำเนินการใหม่ แต่เป็นฟังก์ชัน มันต้องใช้ฟังก์ชั่นเป้าหมายเพื่อเรียกเป็นเป้าหมายใหม่ (…)และ argumentList เพื่อส่งต่อไปยังฟังก์ชั่น
เครื่องหมาย
ก่อน จะไปต่อ เรามาพักกันสักหน่อยแล้วมาดูประเภท Primitive ใหม่ใน ES6 ที่เรียกว่าSymbol สัญลักษณ์เป็นประเภทดั้งเดิมใหม่เช่น Number, Boolean และ String และรับประกันได้ว่าจะไม่ซ้ำกัน จุดประสงค์หลักคือสิ่งนี้ — เพื่อให้ได้ค่าที่ไม่ซ้ำใคร และมักจะใช้เพื่อเพิ่มรหัสคุณสมบัติเฉพาะให้กับวัตถุที่จะไม่ชนกับรหัสอื่น ๆ ที่รหัสอื่น ๆ อาจเพิ่มให้กับวัตถุ ลองมาบางตัวอย่าง
สัญลักษณ์ที่ดีสำหรับอะไร
มันให้คุณค่าและคุณสมบัติที่ไม่เหมือนใครแก่เราอย่างสมบูรณ์ ตัวอย่างเช่น เราสามารถบันทึกทุกบริบทที่เรียกใช้ฟังก์ชันของเราได้อย่างง่ายดายด้วยวิธีนี้
อย่างที่เราได้กล่าวไปแล้วว่าสัญลักษณ์สามารถใช้เป็นคุณสมบัติของอ็อบเจกต์ได้
เรามีสัญลักษณ์ที่รู้จักกันดีในจาวาสคริปต์ คุณสมบัติสแตติกทั้งหมดของ ตัวสร้าง Symbolเช่นSymbol.hasInstanceเป็น Symbols เอง ซึ่งมีค่าเป็นค่าคงที่เสมอ พวกมันถูกเรียกว่าSymbols ที่รู้จักกันดีและพวกมันถูกใช้สำหรับการดำเนินการ Javascript ในตัวบางอย่างตัวอย่างเช่น ถ้าคลาสมี เมธอดแบบสแตติกที่มีSymbol.hasInstanceชื่อ เมธอดนี้จะเข้ารหัสลักษณะการทำงานด้วยinstanceofโอเปอเรเตอร์
ดังนั้นเราจึงสามารถแก้ไขพฤติกรรมของคำหลักอินสแตน ซ์นี้ได้อย่างง่ายดาย
ลองพิจารณาสัญลักษณ์ที่รู้จักกันดีอีกตัว[Symbol.toStringTag]ที่เข้ารหัสคำอธิบายสตริงของออบเจกต์ ลองมาดูตัวอย่างและกำหนดคำอธิบายสตริงเริ่มต้นใหม่
โดยปกติเมื่อเราเขียน .toString() สำหรับวัตถุที่จะพิมพ์
oo.toString() -> [object Object]
ดังนั้นตอนนี้หากเราจะเขียน .toString() วิธีการสำหรับวัตถุนี้ มันจะส่งคืนสิ่งนี้
oo.toString() -> '[object "Saba Shavidze"]'
ตอนนี้ฉันต้องการมุ่งเน้นไปที่สิ่งหนึ่ง อย่างที่คุณเห็น เราสามารถแทรกแซงพฤติกรรมของคุณสมบัติเฉพาะของออบเจกต์ได้ แต่จะเกิดอะไรขึ้นเมื่อเราต้องการให้ฟังก์ชันรับ นี้ ที่เรากำหนดให้ทำงานไม่ใช่สำหรับคุณสมบัติเฉพาะ แต่สำหรับคุณสมบัติทั้งหมดของ object นี่คือ JavaScript object Proxy ใหม่
ลองนิยามว่ามันคืออะไร? อProxyอบเจ็กต์อนุญาตให้คุณสร้างออบเจ็กต์ที่สามารถใช้แทนออบเจ็กต์ดั้งเดิม แต่อาจกำหนดการObjectดำเนินการพื้นฐานใหม่ เช่น การรับ การตั้งค่า และการกำหนดคุณสมบัติ ใช้เป้าหมายพารามิเตอร์ 2 ตัว — วัตถุจริงไปยังพร็อกซีและตัวจัดการ — วัตถุเหมือนตัวอธิบายที่มักเรียกว่ากับดัก Proxy is revocable หมายความว่าเราสามารถเรียก revoke แล้ว proxy จะไม่ทำงานอีกต่อไป
ลองพิจารณาตัวอย่างง่ายๆโดยตรง สมมติว่าเราต้องการจำกัดการเข้าถึงคุณสมบัติบางอย่างของวัตถุ
ดังนั้นเราจึงกำหนดวิธีการดัก - รับวัตถุของเราเพื่อจัดการกรณีเมื่อมีคนต้องการเข้าถึงคุณสมบัติที่เรียกว่ารหัสผ่าน
เรามีวิธีการดักหลายวิธีในตัวจัดการออบเจกต์: รับ ตั้งค่า นำไปใช้ สร้าง กำหนดพร็อพเพอร์ตี้ และอื่น ๆ ดูเหมือนคุ้นเคย ? เรามีเมธอดทั้งหมดนี้ในวัตถุ Reflect เช่นกัน ดังนั้นแทนที่จะส่งคืน target[property] เราส่งคืนใน get trap เราสามารถส่งคืน Reflect.get(target,property) ได้ง่ายๆ อะไรจะดีขนาดนั้น? วิธีการสะท้อนกลับช่วยให้เราหลีกเลี่ยงกรณีต่างๆ เมื่อเราต้องดำเนินการผิดพลาดและจัดการกับกรณีที่ไม่ถูกต้องต่างๆ เขาจะทำเอง
ดังนั้นพร็อกซีจึงให้ความสามารถอย่างเต็มที่แก่เราในการขอร้อง
ติดตามประวัติวัตถุ
ไม่ ฉันต้องการเขียนตัวอย่างที่ดีสำหรับ Intercession with Proxy ฉันต้องการมีประวัติว่าวัตถุของฉันเปลี่ยนไปอย่างไร มาเขียนฟังก์ชันที่รับวัตถุและส่งคืน Proxy ใหม่ตามวัตถุนี้ และจัดการประวัติที่เปลี่ยนแปลงไปในขณะที่เราตั้งค่าหรือรับคุณสมบัติจากวัตถุนี้
ดังนั้น trackObject ได้รับวัตถุเป็นอาร์กิวเมนต์ จากนั้นเราสร้างวัตถุพร็อกซีตามนั้น เรากำหนดฟังก์ชั่น get และ set ในตัวจัดการวัตถุเพื่อจัดการกับ getter และ setter หากผู้ใช้จะพยายามตั้งค่าคุณสมบัติด้วยค่า ก่อนอื่นเราจะตรวจสอบว่าผู้ใช้พยายามตั้งค่าคุณสมบัติประวัติหรือไม่ เนื่องจากผู้ใช้ไม่ควรตั้งค่า จากนั้นเราตั้งค่าคุณสมบัติด้วยค่าและผลัก Object ใหม่ไปยังอาร์เรย์ของเราที่เรียกว่า history เพื่อบันทึกคุณสมบัติและค่าที่ตั้งค่าไว้และประทับเวลาที่ทำเสร็จแล้ว ในขณะที่ผู้ใช้พยายามรับคุณสมบัติบางอย่าง เราจะตรวจสอบว่าเธอ/เขาต้องการรับประวัติหรือไม่ เราจะส่งคืนอาร์เรย์ของประวัติ มิฉะนั้น เราจะได้รับค่าของคุณสมบัติ
ลงทะเบียนคุณสมบัติใหม่
อีกตัวอย่างที่ยอดเยี่ยมที่ฉันต้องการแบ่งปันคือการเขียนฟังก์ชันที่ทำให้สามารถแนบคุณสมบัติ Symbol ใหม่กับวัตถุและลงทะเบียนค่าได้ งั้นเรามาเขียนกันเถอะ
ดังนั้น ฟังก์ชันของเราจะรับวัตถุที่เราต้องการแนบคุณสมบัติใหม่และส่งคืนฟังก์ชันที่รับฟังก์ชันที่เราต้องการตั้งค่าเป็นคุณสมบัติใหม่ ตอนนี้เราสามารถลงทะเบียนคุณสมบัติใหม่ด้วยฟังก์ชั่นที่เราส่งไป ดังนั้นสิ่งแรกที่อยู่ในใจของฉันคือฉันจะใช้วิธีนี้ได้อย่างไรคือการสร้างไปป์ไลน์สำหรับวัตถุ ไปป์ไลน์สำหรับวัตถุในกรณีนี้คืออะไร? มันทำให้เราสามารถแนบฟังก์ชั่นไปยังวัตถุและส่งผ่านฟังก์ชั่นที่ส่งคืนวัตถุที่แก้ไข เราจะทำสิ่งนี้ได้อย่างไร เราสามารถแนบคุณสมบัติใหม่กับ Object.prototype ก่อน เนื่องจากเป็นคลาสพื้นฐานสำหรับออบเจกต์ทั้งหมด ดังนั้นคุณสมบัติ Symbol ใหม่ของเราจะคืนค่าฟังก์ชันและเราต้องการส่งอ็อบเจกต์ของเราไปยังมัน ดังนั้นเราต้องการมีฟังก์ชันที่รับฟังก์ชันเป็นอาร์กิวเมนต์และเรียกมันด้วยออบเจกต์ที่แนบมาและส่งกลับ มาเขียนกันเถอะ





































![รายการที่เชื่อมโยงคืออะไร? [ส่วนที่ 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)