ES6 中的 Proxy 对象是一种可以在对象访问中拦截、更改或扩展行为的工具。通过 Proxy 对象,我们可以更加灵活地处理对象的属性访问、方法调用、构造器调用等操作。本文将详细介绍 Proxy 对象的使用方法和特性,并给出相关示例代码。
Proxy 对象的基本用法
创建一个 Proxy 对象,需要定义一个 Target 对象和一个 Handler 对象,其中 Target 对象为被代理的对象,Handler 对象为拦截器,用于拦截、处理 Target 对象的各种操作。以下为创建 Proxy 对象的代码示例:
let target = { name: 'Lucy', age: 20 }; let handler = { get: (target, prop, receiver) => { console.log(`Getting ${prop}`); return target[prop]; }, set: (target, prop, value, receiver) => { console.log(`Setting ${prop} to ${value}`); target[prop] = value; return true; } }; let proxy = new Proxy(target, handler);
在以上代码中,我们定义了一个 Target 对象 target
,包含 name
和 age
两个属性。同时,我们定义了一个 Handler 对象 handler
,其包含了两个拦截器:get
拦截器用于处理属性访问操作,set
拦截器用于处理属性设置操作。最后,我们使用 new Proxy()
方法创建了 Proxy 对象 proxy
。
通过以上代码,我们可以在 proxy
对象上进行属性的访问和设置,同时拦截器可以捕捉到相关操作并做出相应的处理。
例如,我们可以使用以下代码访问 proxy
对象中的 name
属性:
console.log(proxy.name);
执行以上代码,会先输出 "Getting name"
,表示访问了 name
属性,然后输出 "Lucy"
,表示返回了 target.name
属性的值。相应地,我们也可以使用以下代码来设置 proxy
对象的 name
属性:
proxy.name = 'Lily';
执行以上代码,会先输出 "Setting name to Lily"
,表示设置了 name
属性的值为 "Lily"
。
Proxy 对象的拦截器类型
除了 get
和 set
拦截器,我们还可以使用 Proxy 对象提供的其他拦截器,包括:apply
、construct
、deleteProperty
、has
、getOwnPropertyDescriptor
、defineProperty
、getPrototypeOf
、setPrototypeOf
、isExtensible
、preventExtensions
、ownKeys
等。以下是拦截器的介绍和使用方法:
apply 拦截器
apply
拦截器用于拦截函数调用,比如 func(...args)
或 func.apply(receiver, args)
。其接收三个参数:
- target:被代理的函数;
- thisArg:函数调用时的 this 值;
- argArray:函数调用时传入的参数数组。
以下是一个 apply
拦截器的代码示例:
let func = (...args) => { console.log(`Calling func with args:`, args); return args.reduce((sum, n) => sum + n, 0); }; let handler = { apply: (target, thisArg, argArray) => { console.log(`Calling function with args:`, argArray); return target(...argArray); } }; let proxy = new Proxy(func, handler); console.log(proxy(1, 2, 3, 4, 5)); // 输出 15
在以上代码中,我们定义了一个函数 func
,接收多个参数并返回它们的和。我们还定义了一个 apply
拦截器,拦截了 proxy
对象的函数调用,输出传入的参数并调用 target(...argArray)
方法。
construct 拦截器
construct
拦截器用于拦截构造函数的调用,例如 new func(...args)
。其接收两个参数:
- target:被代理的构造函数;
- argArray:构造函数调用时传入的参数数组。
以下是一个 construct
拦截器的代码示例:
class Person { constructor(name, age) { this.name = name; this.age = age; } sayHello() { console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`); } } let handler = { construct: (target, argArray) => { let [name, age] = argArray; console.log(`Creating new Person with name ${name} and age ${age}.`); return new target(name, age); } }; let proxy = new Proxy(Person, handler); let person = new proxy('Lucy', 20); person.sayHello(); // 输出 "Hello, my name is Lucy, I'm 20 years old."
在以上代码中,我们定义了一个 Person
类,包含 name
、age
两个属性,和 sayHello()
方法。我们还定义了一个 construct
拦截器,拦截了 proxy
对象的构造调用,输出传入的参数并调用 target(...argArray)
方法。
deleteProperty 拦截器
deleteProperty
拦截器用于拦截删除一个属性的操作,例如 delete obj.prop
。其接收两个参数:
- target:被代理的对象;
- prop:要删除的属性名。
以下是一个 deleteProperty
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { deleteProperty: (target, prop) => { console.log(`Deleting property ${prop}`); delete target[prop]; return true; } }; let proxy = new Proxy(obj, handler); delete proxy.age; // 输出 "Deleting property age" console.log(proxy); // 输出 {name: 'Lucy'}
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 deleteProperty
拦截器,拦截了 proxy
对象的删除属性操作,输出被删除的属性名并调用 delete target[prop]
方法。
has 拦截器
has
拦截器用于拦截属性是否存在的操作,例如 'prop' in obj
。其接收两个参数:
- target:被代理的对象;
- prop:属性名。
以下是一个 has
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { has: (target, prop) => { console.log(`Checking if property ${prop} exists`); return prop in target; } }; let proxy = new Proxy(obj, handler); console.log('name' in proxy); // 输出 "Checking if property name exists",并返回 true console.log('gender' in proxy); // 输出 "Checking if property gender exists",并返回 false
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 has
拦截器,拦截了 proxy
对象的检查属性是否存在的操作,输出被检查的属性名并调用 prop in target
方法。
getOwnPropertyDescriptor 拦截器
getOwnPropertyDescriptor
拦截器用于拦截获取属性描述符的操作,例如 Object.getOwnPropertyDescriptor(obj, 'prop')
。其接收两个参数:
- target:被代理的对象;
- prop:属性名。
以下是一个 getOwnPropertyDescriptor
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { getOwnPropertyDescriptor: (target, prop) => { console.log(`Getting property descriptor of ${prop}`); return Object.getOwnPropertyDescriptor(target, prop); } }; let proxy = new Proxy(obj, handler); console.log(Object.getOwnPropertyDescriptor(proxy, 'name')); // 输出 "Getting property descriptor of name",并返回 {value: "Lucy", writable: true, enumerable: true, configurable: true}
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 getOwnPropertyDescriptor
拦截器,拦截了 proxy
对象的获取属性描述符的操作,输出被获取的属性名并调用 Object.getOwnPropertyDescriptor(target, prop)
方法。
defineProperty 拦截器
defineProperty
拦截器用于拦截定义一个属性的操作,例如 Object.defineProperty(obj, 'prop', {value: 123})
。其接收三个参数:
- target:被代理的对象;
- prop:要定义的属性名;
- desc:要定义的属性描述符对象。
以下是一个 defineProperty
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { defineProperty: (target, prop, desc) => { console.log(`Defining property ${prop} with descriptor:`, desc); return Object.defineProperty(target, prop, desc); } }; let proxy = new Proxy(obj, handler); Object.defineProperty(proxy, 'gender', {value: 'female'}); // 输出 "Defining property gender with descriptor: {value: 'female', writable: false, enumerable: false, configurable: false}" console.log(proxy); // 输出 {name: 'Lucy', age: 20, gender: 'female'}
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 defineProperty
拦截器,拦截了 proxy
对象的定义属性的操作,输出被定义的属性名和属性描述符并调用 Object.defineProperty(target, prop, desc)
方法。
getPrototypeOf 拦截器
getPrototypeOf
拦截器用于拦截获取一个对象的原型的操作,例如 Object.getPrototypeOf(obj)
。其接收一个参数:
- target:被代理的对象。
以下是一个 getPrototypeOf
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { getPrototypeOf: (target) => { console.log(`Getting prototype of object`); return Object.getPrototypeOf(target); } }; let proxy = new Proxy(obj, handler); console.log(Object.getPrototypeOf(proxy)); // 输出 "Getting prototype of object",并返回 Object.prototype
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 getPrototypeOf
拦截器,拦截了 proxy
对象的获取原型的操作,输出被代理的对象并调用 Object.getPrototypeOf(target)
方法。
setPrototypeOf 拦截器
setPrototypeOf
拦截器用于拦截设置一个对象的原型的操作,例如 Object.setPrototypeOf(obj, proto)
。其接收两个参数:
- target:被代理的对象;
- proto:要设置的新原型对象。
以下是一个 setPrototypeOf
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { setPrototypeOf: (target, proto) => { console.log(`Setting prototype of object to ${proto}`); return Object.setPrototypeOf(target, proto); } }; let proxy = new Proxy(obj, handler); Object.setPrototypeOf(proxy, {}); console.log(Object.getPrototypeOf(proxy)); // 输出 "[object Object]"
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 setPrototypeOf
拦截器,拦截了 proxy
对象的设置原型的操作,输出被代理的对象和新的原型对象并调用 Object.setPrototypeOf(target, proto)
方法。
isExtensible 拦截器
isExtensible
拦截器用于拦截检测一个对象是否可扩展的操作,例如 Object.isExtensible(obj)
。其接收一个参数:
- target:被代理的对象。
以下是一个 isExtensible
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { isExtensible: (target) => { console.log(`Checking if object is extensible`); return Object.isExtensible(target); } }; let proxy = new Proxy(obj, handler); console.log(Object.isExtensible(proxy)); // 输出 "Checking if object is extensible",并返回 true
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 isExtensible
拦截器,拦截了 proxy
对象的检测是否可扩展的操作,输出被代理的对象并调用 Object.isExtensible(target)
方法。
preventExtensions 拦截器
preventExtensions
拦截器用于拦截使一个对象不可扩展的操作,例如 Object.preventExtensions(obj)
。其接收一个参数:
- target:被代理的对象。
以下是一个 preventExtensions
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { preventExtensions: (target) => { console.log(`Preventing object from being extended`); return Object.preventExtensions(target); } }; let proxy = new Proxy(obj, handler); Object.preventExtensions(proxy); console.log(Object.isExtensible(proxy)); // 输出 "Preventing object from being extended",并返回 false
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 preventExtensions
拦截器,拦截了 proxy
对象的使其不可扩展的操作,输出被代理的对象并调用 Object.preventExtensions(target)
方法。
ownKeys 拦截器
ownKeys
拦截器用于拦截获取一个对象自身属性键名的操作,例如 Object.getOwnPropertyNames(obj)
或 Object.getOwnPropertySymbols(obj)
或 Reflect.ownKeys(obj)
。其接收一个参数:
- target:被代理的对象。
以下是一个 ownKeys
拦截器的代码示例:
let obj = { name: 'Lucy', age: 20 }; let handler = { ownKeys: (target) => { console.log(`Getting own property keys of object`); return Object.getOwnPropertyNames(target); } }; let proxy = new Proxy(obj, handler); console.log(Object.getOwnPropertyNames(proxy)); // 输出 "Getting own property keys of object",并返回 ["name", "age"]
在以上代码中,我们定义了一个对象 obj
,包含 name
、age
两个属性。我们还定义了一个 ownKeys
拦截器,拦截了 proxy
对象的获取其自身属性键名的操作,输出被代理的对象并调用 Object.getOwnPropertyNames(target)
方法。
Proxy 对象的符号键
除了上述拦截器类型,Proxy 对象还支持一些特殊的符号键,用于进行属性访问拦截、代理的相关操作。以下是常用的几种符号键:
get(target, prop, receiver)
:拦截对象的属性读取操作;set(target, prop, value, receiver)
:拦截对象的属性设置操作;has(target, prop)
:拦截in
操作符;deleteProperty(target, prop)
:拦截delete
操作符;apply(target, thisArg, args)
:拦截函数调用;construct(target, args)
:拦截构造函数调用。
其它符号键的使用方法和用途,请参考 MDN 文档。
总结
通过本文介绍,我们了解了 ES6 中的 Proxy 对象的基本用法和特性。Proxy 对象的拦截器类型和符号键,可以让我们更加灵活地处理对象的属性访问、方法调用、构造器调用等操作,为我们的编码提供了很大的方便和开发效率。经过深入的学习和练习,我们可以更好地掌握 Proxy 对象的使用方法,并在实际的项目开发中灵活运用。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a99fa4add4f0e0ff2fd314