代理(Proxy)是 JavaScript 提供的一种元编程的能力,它可以拦截对象的一些基本操作,比如读写属性、调用方法等。在 ES11 中,代理的功能得到了进一步增强,本文将详细介绍如何更好地使用 JavaScript 的代理。
基本用法
在 ES6 中,我们已经可以使用代理来拦截对象的基本操作了,例如:
const obj = { name: 'Alice', age: 18 }; const proxy = new Proxy(obj, { get(target, prop) { console.log(`Getting ${prop}`); return target[prop]; }, set(target, prop, value) { console.log(`Setting ${prop} to ${value}`); target[prop] = value; } }); proxy.name; // => Getting name // => 'Alice' proxy.age = 20; // => Setting age to 20 console.log(proxy.age); // => Getting age // => 20
上面的代码中,我们使用 new Proxy()
创建了一个代理对象 proxy
,它可以拦截对象 obj
的读写操作。其中,拦截读操作的方法是 get()
,拦截写操作的方法是 set()
。当我们使用 proxy.name
读取属性值时,会触发 get()
方法,并输出 Getting name
;当我们使用 proxy.age = 20
修改属性值时,会触发 set()
方法,并输出 Setting age to 20
。
新增的拦截方法
在 ES11 中,代理新增了一些拦截方法,让我们可以更加精细地控制对象的行为。
has()
has()
方法用于拦截 in
操作符,即判断对象是否包含某个属性。例如:
const obj = { name: 'Alice', age: 18 }; const proxy = new Proxy(obj, { has(target, prop) { console.log(`Checking ${prop} existence`); return prop in target; } }); 'age' in proxy; // => Checking age existence // => true 'gender' in proxy; // => Checking gender existence // => false
上面的代码中,我们使用 has()
方法拦截了 in
操作符,当我们使用 'age' in proxy
判断属性存在时,会触发 has()
方法,并输出 Checking age existence
。
apply()
apply()
方法用于拦截函数的调用。例如:
function add(a, b) { return a + b; } const proxy = new Proxy(add, { apply(target, thisArg, args) { console.log(`Calling ${target.name} with ${args}`); return target.apply(thisArg, args); } }); proxy(1, 2); // => Calling add with 1,2 // => 3
上面的代码中,我们使用 apply()
方法拦截了函数的调用,当我们使用 proxy(1, 2)
调用函数时,会触发 apply()
方法,并输出 Calling add with 1,2
。
construct()
construct()
方法用于拦截 new
操作符,即创建对象实例时的行为。例如:
class Person { constructor(name, age) { this.name = name; this.age = age; } } const proxy = new Proxy(Person, { construct(target, args) { console.log(`Creating ${target.name} instance with ${args}`); return new target(...args); } }); const p = new proxy('Alice', 18); // => Creating Person instance with Alice,18 console.log(p); // => Person { name: 'Alice', age: 18 }
上面的代码中,我们使用 construct()
方法拦截了 new
操作符,当我们使用 new proxy('Alice', 18)
创建对象实例时,会触发 construct()
方法,并输出 Creating Person instance with Alice,18
。
deleteProperty()
deleteProperty()
方法用于拦截 delete
操作符,即删除对象属性的行为。例如:
const obj = { name: 'Alice', age: 18 }; const proxy = new Proxy(obj, { deleteProperty(target, prop) { console.log(`Deleting ${prop}`); return delete target[prop]; } }); delete proxy.age; // => Deleting age console.log(proxy); // => { name: 'Alice' }
上面的代码中,我们使用 deleteProperty()
方法拦截了 delete
操作符,当我们使用 delete proxy.age
删除属性时,会触发 deleteProperty()
方法,并输出 Deleting age
。
getOwnPropertyDescriptor()
getOwnPropertyDescriptor()
方法用于拦截 Object.getOwnPropertyDescriptor()
方法,即获取对象属性描述符的行为。例如:
const obj = { name: 'Alice', age: 18 }; const proxy = new Proxy(obj, { getOwnPropertyDescriptor(target, prop) { console.log(`Getting descriptor of ${prop}`); return Object.getOwnPropertyDescriptor(target, prop); } }); Object.getOwnPropertyDescriptor(proxy, 'age'); // => Getting descriptor of age // => { value: 18, writable: true, enumerable: true, configurable: true }
上面的代码中,我们使用 getOwnPropertyDescriptor()
方法拦截了 Object.getOwnPropertyDescriptor()
方法,当我们使用 Object.getOwnPropertyDescriptor(proxy, 'age')
获取属性描述符时,会触发 getOwnPropertyDescriptor()
方法,并输出 Getting descriptor of age
。
getPrototypeOf()
getPrototypeOf()
方法用于拦截 Object.getPrototypeOf()
方法,即获取对象原型的行为。例如:
const obj = { name: 'Alice', age: 18 }; const proto = { gender: 'female' }; const proxy = new Proxy(obj, { getPrototypeOf(target) { console.log(`Getting prototype of ${target}`); return proto; } }); Object.getPrototypeOf(proxy); // => Getting prototype of [object Object] // => { gender: 'female' }
上面的代码中,我们使用 getPrototypeOf()
方法拦截了 Object.getPrototypeOf()
方法,当我们使用 Object.getPrototypeOf(proxy)
获取对象原型时,会触发 getPrototypeOf()
方法,并输出 Getting prototype of [object Object]
。
isExtensible()
isExtensible()
方法用于拦截 Object.isExtensible()
方法,即判断对象是否可扩展的行为。例如:
const obj = { name: 'Alice', age: 18 }; const proxy = new Proxy(obj, { isExtensible(target) { console.log(`Checking ${target} extensibility`); return Object.isExtensible(target); } }); Object.isExtensible(proxy); // => Checking [object Object] extensibility // => true
上面的代码中,我们使用 isExtensible()
方法拦截了 Object.isExtensible()
方法,当我们使用 Object.isExtensible(proxy)
判断对象是否可扩展时,会触发 isExtensible()
方法,并输出 Checking [object Object] extensibility
。
ownKeys()
ownKeys()
方法用于拦截 Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
和 Object.keys()
方法,即获取对象自身属性名的行为。例如:
const obj = { name: 'Alice', age: 18 }; const proxy = new Proxy(obj, { ownKeys(target) { console.log(`Getting own keys of ${target}`); return Reflect.ownKeys(target); } }); Object.getOwnPropertyNames(proxy); // => Getting own keys of [object Object] // => [ 'name', 'age' ] Object.getOwnPropertySymbols(proxy); // => Getting own keys of [object Object] // => [] Object.keys(proxy); // => Getting own keys of [object Object] // => [ 'name', 'age' ]
上面的代码中,我们使用 ownKeys()
方法拦截了 Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
和 Object.keys()
方法,当我们使用这些方法获取对象自身属性名时,会触发 ownKeys()
方法,并输出 Getting own keys of [object Object]
。
总结
在 ES11 中,代理的功能得到了进一步增强,我们可以使用新增的拦截方法更加精细地控制对象的行为。本文介绍了这些拦截方法的用法,并给出了示例代码。希望本文对你学习和使用 JavaScript 的代理有帮助。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65bdf1d4add4f0e0ff78bf74