ดักจับการดำเนินการพื้นฐานของภาษาด้วย trap ของ Proxy และมอบหมายพฤติกรรมเริ่มต้นอย่างสะอาดด้วย Reflect
เปิดบทเรียนนี้ใน KodokonProxy ห่ออ็อบเจกต์เป้าหมายและดักจับ การดำเนินการภายใน ของภาษาผ่าน trap สิบสามตัว: get, set, has (โอเปอเรเตอร์ in), deleteProperty, ownKeys, apply, construct และอื่น ๆ อ็อบเจกต์ Reflect เปิดเผยการดำเนินการเดียวกันในรูปฟังก์ชัน โดยมี signature เหมือนกับ trap มันคือรากฐานของ reactivity ใน Vue 3 ของ mock แบบไดนามิก และของ API แบบ ORM
const target = { name: "Ada" };
const traced = new Proxy(target, {
get(obj, prop, receiver) {
console.log("get " + String(prop));
return Reflect.get(obj, prop, receiver);
}
});
traced.name; // get nameทำไมต้องใช้ Reflect.get(obj, prop, receiver) แทน obj[prop]? เพราะ receiver ถ้าพร็อพเพอร์ตี้เป็น accessor (getter) Reflect.get จะเรียกมันโดยให้ this ชี้ไปที่ receiver - proxy หรืออ็อบเจกต์ที่สืบทอดจากมัน - ในขณะที่ obj[prop] จะตั้ง this เป็นเป้าหมายดิบ ถ้าไม่มี receiver getter ที่อ่าน this จะพังทันทีที่อ็อบเจกต์หนึ่งสืบทอดจาก proxy
const base = {
get label() {
return this.name;
}
};
const proxy = new Proxy(base, {
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
}
});
const child = Object.create(proxy);
child.name = "Ada";
console.log(child.label); // "Ada" thanks to the receiverกรณีใช้งานจริงจัง: การตรวจสอบ ตอนเขียนค่า พร็อพเพอร์ตี้เสมือนที่คำนวณสด ๆ, logging, การควบคุมการเข้าถึง (ซ่อนคีย์ที่ขึ้นต้นด้วย _ ผ่าน has และ ownKeys) หรืออ็อบเจกต์ที่มีพฤติกรรมเริ่มต้น สังเกตว่า Reflect.set คืนค่าบูลีนแทนที่จะโยน error ในโหมด strict trap ของคุณจึงต้อง ส่งต่อค่าที่คืนนั้น
function validated(target) {
return new Proxy(target, {
set(obj, prop, value, receiver) {
if (prop === "age" && !Number.isInteger(value)) {
throw new TypeError("age must be an integer");
}
return Reflect.set(obj, prop, value, receiver);
}
});
}
const user = validated({ age: 30 });
user.age = 31; // ok
// user.age = "old"; // TypeErrorget ทำไมจึงควรใช้ Reflect.get(target, prop, receiver) แทน target[prop]?this ที่ถูกต้อง (receiver) แทนที่จะเป็นเป้าหมายดิบtarget[prop] จะทริกเกอร์ trap ซ้ำและทำให้เกิดการเรียกซ้ำไม่รู้จบ"id" in obj?get คืนค่า 42 สำหรับพร็อพเพอร์ตี้ของเป้าหมายที่มีค่าเป็น 1 ซึ่งประกาศเป็น non-configurable และ non-writable จะเกิดอะไรขึ้น?