• 零基础学习JS--基础篇--元编程


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

    代理

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

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

    1. let handler = {
    2. get(target, name) {
    3. return name in target ? target[name] : 42;
    4. },
    5. };
    6. let p = new Proxy({}, handler);
    7. p.a = 1;
    8. 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()

    属性删除

    delete proxy[foo]
    delete proxy.foo
    Reflect.deleteProperty()

    如果存在一个对应于 target 的属性是不可配置的自有属性,那么该属性不能被删除。
    handler.ownKeys()Object.getOwnPropertyNames()
    Object.getOwnPropertySymbols()
    Object.keys()
    Reflect.ownKeys()
    • ownKeys 的返回值是一个数组。
    • 返回值中的每个元素类型为 String 或 Symbol
    • 返回值中必须包含 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

    1. const revocable = Proxy.revocable(
    2. {},
    3. {
    4. get(target, name) {
    5. return `[[${name}]]`;
    6. },
    7. },
    8. );
    9. const proxy = revocable.proxy;
    10. console.log(proxy.foo); // "[[foo]]"
    11. revocable.revoke();
    12. console.log(proxy.foo); // TypeError: Cannot perform 'get' on a proxy that has been revoked
    13. proxy.foo = 1; // TypeError: Cannot perform 'set' on a proxy that has been revoked
    14. delete proxy.foo; // TypeError: Cannot perform 'deleteProperty' on a proxy that has been revoked
    15. console.log(typeof proxy); // "object", `typeof` 不会触发任何陷阱

    反射

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

    Reflect 并不是一个函数对象。

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

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

    Reflect.has(Object, "assign"); // true
    
    更好的apply函数:

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

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

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

    1. Reflect.apply(Math.floor, undefined, [1.75]);
    2. // 1;
    3. Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);
    4. // "hello"
    5. Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index;
    6. // 4
    7. Reflect.apply("".charAt, "ponies", [3]);
    8. // "i"
    检查属性是否定义成功:

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

    1. if (Reflect.defineProperty(target, property, attributes)) {
    2. // success
    3. } else {
    4. // failure
    5. }

    这篇文章和上一篇文章一样,都是新知识,内容不多,但难于理解,里面还有很多细节大家可以点击链接进行更详细的学习,这里不再进行详细的阐述。

    附:以上内容均为个人在MDN网站上学习JS的笔记,若有侵权,将在第一时间删除,若有错误,将在第一时间修改。

  • 相关阅读:
    FullGC 过多 为什么会让CPU飙升100%
    简述Mysql中索引类型及对数据库的性能的影响
    2.3、传输方式
    【Rust日报】2023-11-08 RustyVault -- 基于 rust 的现代秘密管理系统
    函数式编程01
    《重构改善代码设计》
    工具篇 | 基于SpringBoot的H2数据库入门实战
    Vue+element 商品参数业务实现
    CSS 实现新拟态(Neumorphism) UI 风格
    [iOS]LLDB调试
  • 原文地址:https://blog.csdn.net/weixin_67679565/article/details/136660933