面试题:Proxy 相较于 Object.defineProperty 有什么优势?

面试题:Proxy 相较于 Object.defineProperty 有什么优势?

Object.defineProperty 详解

  • 语法Object.defineProperty(obj, prop, descriptor)

  • 功能:在一个对象上定义一个新属性修改其现有属性,并返回此对象

  • 参数

    • obj 要定义或修改属性的对象

    • prop 字符串或 Symbol,指定要定义或修改的属性键

    • descriptor 对象,指定要定义或修改的属性的描述符

      通过 descriptor,可以设置 prop 指定的属性值,gettersetter 等等特性。默认情况下,Object.defineProperty 添加的属性是不可写、不可枚举和不可配置的

  • 属性描述符(descriptor)的语法descriptor 有两种类型,数据描述符和访问器描述符,其只能是这两种类型之一。根据传入的属性描述符类型不同,给对象中定义或修改的属性的特性也将有所不同。因此,属性也可以分为数据属性和访问器属性

    因为 descriptor 是一个对象,因此有一系列可选的配置项。有些配置项是数据描述符特有的,有些配置项是访问器描述符特有的,有些配置项则是二者共享使用的。根据是否存在特有配置项,从而可以判断当前属性描述符的类型。注意,一个属性描述符中不能同时拥有两种类型的特有配置项

    适用情况配置取值描述默认值
    数据/访问器描述符configurabletrue/false该配置取值为 false 时,
    ①属性类型不可更改(数据属性、访问器属性)
    ②属性不可被删除
    ③ 属性描述符的配置项不可更改(特例,对于可写的数据属性,可以修改其数据描述符的 valuewritable
    否则:TypeError
    false
    数据/访问器描述符enumerabletrue/false该配置取值为 true 时,属性可以在对象的属性枚举中出现。(如 for...inObject.keys() 等)false
    数据描述符value任何有效的 JavaScript 值数据属性值undefined
    数据描述符writabletrue/false该配置取值为 true 时,属性可写(即可以使用赋值运算符 = 更改属性值)false
    访问器描述符get属性 getter 函数访问属性时,会不带参调用该函数,返回值被作为该属性的值undefined
    访问器描述符set属性 setter 函数当给属性赋值时,会调用该函数,并携带一个参数(表示要赋给属性的值)undefined

    注意事项:

    • getset 配置项对应的函数中的 this 不一定是属性所在对象,而是通过该属性访问的对象(由于继承关系的存在,当子对象访问父对象的 gettersetter 函数时,其中 this 指向子对象)。

      let parent = Object.defineProperty({}, "self", {
          get() {
              return this;
          }
      });
      
      let child = Object.create(parent);
      
      console.log(child.self === child); // true
      console.log(child.self === parent); // false
      console.log(parent.self === child); // false
      console.log(parent.self === parent); // true
      
    • 如果属性描述符没有包含任何特有配置项valuewritablegetset),此时该属性描述符被视作数据描述符

    • 如果属性描述符同时包含两种类型的特有配置项,则会抛出异常

    • 因为判断属性描述符是数据描述符还是访问器描述符时,常需要参考其中的属性,但是属性描述符其原型上的属性也会被考虑在内,这有可能会带来潜在问题,有以下两种解决方式。

      • 方式一:创建一个原型为 null 的属性描述符 const descriptor = Object.create(null);

      • 方式二:冻结 Object 原型对象 (Object.freeze || Object)(Object.prototype);

        Object.freeze 方法用于冻结一个对象,从而 ①不能向对象中添加新属性 ②不能删除对象中已有属性 ③不能修改对象中已有属性 ④ 不可有改对象的原型

Proxy 详解

  • 语法const p = new Proxy(target, handler)

  • 功能:用于创建一个对象的代理,从而实现拦截或自定义对象的基本操作(属性查找、赋值、枚举、函数调用等等)。

  • 参数

    • target被代理的对象,可以是任何类型的对象(包含原生数组、函数、甚至代理)。
    • handler对象,其中的属性通常是函数,用于自定义代理 p各种操作所对应的具体的执行逻辑
  • 处理器对象(handler)的语法handler 中定义了一批特定属性,取值通常为函数,称之为捕获器trap)。

    配置项描述触发条件
    getPrototypeOf()Object.getPrototypeOf() 方法的捕捉器。当读取代理对象的原型时,该方法调用。如果该方法返回的不是对象也不是 null,则会报错 TypeErrorhandler.getPrototypeOf()
    setPrototypeOf()Object.setPrototypeOf() 方法的捕捉器。成功修改原型则返回 true,否则 falsehandler.setPrototypeOf()
    isExtensible()Object.isExtensible() 方法的捕捉器。handler.isExtensible()
    preventExtensions() Object.preventExtensions() 方法的捕捉器。handler.preventExtensions()
    getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor() 方法的捕捉器。handler.getOwnPropertyDescriptor()
    defineProperty()Object.defineProperty()方法的捕捉器。handler.defineProperty()
    has()in 操作符的捕捉器。handler.has()
    get()属性读取操作的捕捉器。handler.get()
    set()属性设置操作的捕捉器。handler.set()
    deleteProperty()delete 操作符的捕捉器。handler.deleteProperty()
    ownKeys()Object.getOwnPropertyNames() 方法和 Object.getOwnPropertySymbols() 方法的捕捉器。handler.ownKeys()
    apply()函数调用操作的捕捉器。handler.apply()
    construct()new 操作符的捕捉器。handler.construct()

Proxy 相较于 Object.defineProperty 的优势

通过上边的介绍,可以看出 Proxy 有以下几点优势

  • 拦截范围更广Proxy 支持属性访问、修改、删除、枚举、原型操作、对象构造、方法调用、属性存在性检测、获取属性描述符、定义属性、in 操作符等的拦截。而 Object.defineProperty 只能拦截属性访问、修改。
  • 拦截实现更简单Proxy 可以直接拦截整个对象。而 Object.defineProperty 只能对单个属性进行拦截,如果要拦截整个对象,则需要在对象上逐个进行属性拦截。
  • 可撤销的拦截Proxy.revocable() 方法可以创建一个可撤销的代理对象。而 Object.defineProperty 一旦对属性进行了定义,就无法撤销。
  • 支持对数组的全方位拦截Proxyset 捕获器可以捕捉数组的任何操作,包含通过索引修改数组元素,通过数组方法修改数组元素,以及数组长度的变化。但是 Object.defineProperty 则无法实现。

REFERENCES

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

https://juejin.cn/post/7275551128854560779

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值