码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 零基础学习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的笔记,若有侵权,将在第一时间删除,若有错误,将在第一时间修改。

  • 相关阅读:
    可微硬件:AI将如何重振摩尔定律的良性循环
    Linux操作系统中的yum命令
    强化学习从基础到进阶-案例与实践[2]:马尔科夫决策、贝尔曼方程、动态规划、策略价值迭代
    NoSQL--3.MongoDB配置(Linux版)
    [python-大语言模型]从浅到深一系列学习笔记记录
    解决Windows 10更新安装失败的问题
    FT2004(D2000)开发实战之移植OpenCV-3.4.16
    智能网联汽车云控系统第2部分:车云数据交互规范
    【Unity/XLua】xlua自带教程示例分析(8)—— 热修复
    【2022】Python自动化测试,软件测试最全学习路线......
  • 原文地址:https://blog.csdn.net/weixin_67679565/article/details/136660933
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号