Movatterモバイル変換


[0]ホーム

URL:


MDN Web Docs

此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。

元编程

ProxyReflect 对象允许你拦截并自定义基本语言操作(例如属性查找、赋值、枚举和函数调用等)。借助这两个对象,你可以在 JavaScript 进行元级别的编程。

代理

Proxy 对象可以拦截某些操作并实现自定义行为。

例如获取一个对象上的属性:

js
let handler = {  get(target, name) {    return name in target ? target[name] : 42;  },};let p = new Proxy({}, handler);p.a = 1;console.log(p.a, p.b); // 1, 42

Proxy 对象定义了一个target(这里是一个空对象)和一个实现了get陷阱handler 对象。这里,代理的对象在获取未定义的属性时不会返回undefined,而是返回42

更多例子参见Proxy 页面。

术语

在讨论代理的功能时会用到以下术语:

handler

包含陷阱的占位符对象(下译作“处理器”)。

陷阱

提供属性访问的方法(这类似于操作系统中陷阱的概念)。

target

代理所虚拟化的对象(下译作“目标”)。它通常用作代理的存储后端。JavaScript 会验证与不可扩展性或不可配置属性相关的不变式。

不变式

实现自定义操作时保持不变的语义称为不变式。如果你破坏了处理器的不变式,则会引发TypeError 异常。

处理器和陷阱

以下表格中总结了Proxy 对象可用的陷阱。详细的解释和例子请看参考页

处理器 / 陷阱拦截的操作不变式
handler.getPrototypeOf()Object.getPrototypeOf()
Reflect.getPrototypeOf()
__proto__
Object.prototype.isPrototypeOf()
instanceof
  • getPrototypeOf 方法必须返回一个对象或null
  • 如果target 不可扩展,Object.getPrototypeOf(proxy) 必须返回和Object.getPrototypeOf(target) 一样的值。
handler.setPrototypeOf()Object.setPrototypeOf()
Reflect.setPrototypeOf()
如果target 不可扩展,参数prototype 必须与Object.getPrototypeOf(target) 的值相同。
handler.isExtensible()Object.isExtensible()
Reflect.isExtensible()
Object.isExtensible(proxy) 必须返回和Object.isExtensible(target) 一样的值。
handler.preventExtensions()Object.preventExtensions()
Reflect.preventExtensions()
如果Object.isExtensible(proxy) 值为false,那么Object.preventExtensions(proxy) 只可能返回true
handler.getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor()
Reflect.getOwnPropertyDescriptor()
  • getOwnPropertyDescriptor 必须返回对象或者undefined
  • 如果存在一个对应于target 的属性是不可配置的自有属性,那么该属性不能被报告为不存在的。
  • 如果存在一个对应于target 的属性是自有属性,且该target 不可扩展,那么该属性不能被报告为不存在的。
  • 如果并不存在一个对应于target 的属性是自有属性,且该target 不可扩展,那么该属性不能被报告为存在的。
  • 如果并不存在一个对应于target 的属性是自有属性,或存在一个对应于target 的属性是可配置的自有属性,那么它不能被报告为不可配置的。
  • Object.getOwnPropertyDescriptor(target) 的结果可以通过Object.defineProperty 应用到target 上,且不会抛出异常。
handler.defineProperty()Object.defineProperty()
Reflect.defineProperty()
  • 如果target 不可扩展,那么就不能添加属性。
  • 如果并不存在一个对应于target 的属性是不可配置的自有属性,那么就不能添加(或修改)该属性为不可配置的。
  • 如果存在一个对应于target 的属性是可配置的,那么这个属性未必是不可配置的。
  • 如果存在一个对应于target 的属性,那么Object.defineProperty(target,prop,descriptor) 将不会抛出异常。
  • 在严格模式下,如果defineProperty 处理器返回false,则会抛出TypeError 异常。
handler.has()
属性查询
foo in proxy
继承属性查询
foo in Object.create(proxy)
Reflect.has()
  • 如果存在一个对应于target 的属性是不可配置的自有属性,那么该属性不能被报告为不存在的。
  • 如果存在一个对应于target 的属性是自有属性,且target 不可扩展,那么该属性不能被报告为不存在的。
handler.get()
属性访问
proxy[foo]
proxy.bar
继承属性访问
Object.create(proxy)[foo]
Reflect.get()
  • 如果对应于target 的属性是不可写且不可配置的数据属性,那么该属性值必须与其相同。
  • 如果对应于target 的属性是不可配置的访问器属性,且其[[Get]] 属性为undefined,那么该属性值必须为undefined
handler.set()
属性赋值
proxy[foo] = bar
proxy.foo = bar
继承属性赋值
Object.create(proxy)[foo] = bar
Reflect.set()
  • 如果对应于target 的属性是不可写且不可配置的数据属性,那么就不能修改该属性的值使其不同于target 上对应属性的值。
  • 如果对应于target 的属性是不可配置的访问器属性,且其[[Set]] 属性为undefined,那么就不能设置该属性的值。
  • 在严格模式下,如果set 处理器返回false,则会抛出TypeError 异常。
handler.deleteProperty()
属性删除
deleteproxy[foo]
deleteproxy.foo
Reflect.deleteProperty()
如果存在一个对应于target 的属性是不可配置的自有属性,那么该属性不能被删除。
handler.ownKeys()Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
Reflect.ownKeys()
  • ownKeys 的返回值是一个数组。
  • 返回值中的每个元素类型为StringSymbol
  • 返回值中必须包含target 的所有不可配置自有属性的键名。
  • 如果target 不可扩展,那么返回值中必须有且仅有target 的所有自有属性的键名。
handler.apply()proxy(..args)
Function.prototype.apply()
Function.prototype.call()
Reflect.apply()
不存在关于handler.apply 方法的不变式。
handler.construct()new proxy(...args)
Reflect.construct()
返回值必须是一个Object

可撤销的Proxy

可以用Proxy.revocable() 方法来创建可撤销的Proxy 对象。这意味着可以通过revoke 函数来撤销并关闭一个代理。

此后,对代理进行的任意的操作都会导致TypeError

js
const revocable = Proxy.revocable(  {},  {    get(target, name) {      return `[[${name}]]`;    },  },);const proxy = revocable.proxy;console.log(proxy.foo); // "[[foo]]"revocable.revoke();console.log(proxy.foo); // TypeError: Cannot perform 'get' on a proxy that has been revokedproxy.foo = 1; // TypeError: Cannot perform 'set' on a proxy that has been revokeddelete proxy.foo; // TypeError: Cannot perform 'deleteProperty' on a proxy that has been revokedconsole.log(typeof proxy); // "object", `typeof` 不会触发任何陷阱

反射

Reflect 是一个内置对象,它为可拦截的 JavaScript 操作提供了方法。这些方法与代理处理器所提供的方法类似。

Reflect 并不是一个函数对象。

Reflect 将默认操作从处理器转发到target

Reflect.has() 为例,你可以将in 运算符作为函数:

js
Reflect.has(Object, "assign"); // true

更好的apply 函数

在不借助Reflect 的情况下,我们通常使用Function.prototype.apply() 方法调用一个具有给定this 值和arguments 数组(或类数组对象)的函数。

js
Function.prototype.apply.call(Math.floor, undefined, [1.75]);

借助Reflect.apply,这些操作将变得更加简洁:

js
Reflect.apply(Math.floor, undefined, [1.75]);// 1;Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);// "hello"Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index;// 4Reflect.apply("".charAt, "ponies", [3]);// "i"

检查属性定义是否成功

使用Object.defineProperty,如果成功则返回一个对象,否则抛出一个TypeError,你可使用try...catch 块来捕获定义属性时发生的任何错误。因为Reflect.defineProperty 返回一个布尔值表示的成功状态,你可以在这里使用if...else 块:

js
if (Reflect.defineProperty(target, property, attributes)) {  // success} else {  // failure}

Help improve MDN

Learn how to contribute.

This page was last modified on byMDN contributors.


[8]ページ先頭

©2009-2025 Movatter.jp