【JS】Object.defineProperty与Proxy

avatar
作者
猴君
阅读量:0

一、Object.defineProperty

这里只是简单描述,具体请看另一篇文章:Object.defineProperty。

Object.defineProperty 是 JavaScript 中用于定义或修改对象属性的功能强大的方法。它可以精确地控制属性的行为,如是否可枚举、可配置、可写等。

基本用法

Object.defineProperty(obj, prop, descriptor) 

Object.defineProperty 方法接受三个参数:

  1. 『目标对象』:要在其上定义属性的对象。
  2. 『属性名称』:要定义或修改的属性名称。
  3. 『描述符对象』:属性描述符对象,用于描述该属性的行为。
    属性描述符对象可以包含以下键:
    value:属性的值。默认为 undefined。
    writable:属性是否可写。默认为 false。
    configurable:属性是否可配置。默认为 false。
    enumerable:属性是否可枚举。默认为 false。
    get:属性的 getter 函数。如果没有 getter,值为 undefined。
    set:属性的 setter 函数。如果没有 setter,值为 undefined。

用例

1. 定义一个只读属性

let obj = {};  Object.defineProperty(obj, 'message', {     value: 'Hello, world!',     writable: false });  console.log(obj.message); // 输出 "Hello, world!" obj.message = 'Hi!'; // 无效,因为属性是只读的 console.log(obj.message); // 仍然输出 "Hello, world!" 

2. 定义一个不可枚举属性

let obj = {};  Object.defineProperty(obj, 'message', {     value: 'Hello, world!',     enumerable: false });  console.log(obj.message); // 输出 "Hello, world!" console.log(Object.keys(obj)); // 输出 [], 因为属性不可枚举 

3. getter与setter

let obj = {}; let value = 'Hello, world!';  Object.defineProperty(obj, 'message', {     get() {         return value;     },     set(newValue) {         value = newValue;     },     enumerable: true,     configurable: true });  console.log(obj.message); // 输出 "Hello, world!" obj.message = 'Hi!'; console.log(obj.message); // 输出 "Hi!" 

4. 定义不可配置属性

let obj = {};  Object.defineProperty(obj, 'message', {     value: 'Hello, world!',     configurable: false });  console.log(obj.message); // 输出 "Hello, world!"  Object.defineProperty(obj, 'message', {     value: 'Hi!' }); // 抛出错误,因为属性不可配置 

二、Proxy

Proxy 是 ES6 引入的一项功能,用于定义自定义行为来拦截并改变对某个对象的基本操作(例如属性读取、赋值、枚举、函数调用等)。

基本语法

let proxy = new Proxy(target, handler); 

Proxy 构造函数接受两个参数:

  1. target:要包装的目标对象(可以是任何类型的对象,包括数组、函数等)。
  2. handler:一个对象,其中包含一组捕捉器(traps)。这些捕捉器定义了在执行各种操作时,代理对象如何处理这些操作。
    捕捉器(Traps)包含:
    get(target, prop, receiver):拦截对象属性的读取。
    set(target, prop, value, receiver):拦截对象属性的设置。
    has(target, prop):拦截 in 操作符。
    deleteProperty(target, prop):拦截 delete 操作符。
    ownKeys(target):拦截 Object.getOwnPropertyNames 和 Object.getOwnPropertySymbols 方法。
    apply(target, thisArg, argumentsList):拦截函数调用。
    construct(target, args):拦截 new 操作符。

用例

1. 基本使用

let target = {     message: "Hello, world!" };  let handler = {     get(target, prop) {         return prop in target ? target[prop] : `Property ${prop} does not exist.`;     },     set(target, prop, value) {         console.log(`Setting ${prop} to ${value}`);         target[prop] = value;         return true;     } };  let proxy = new Proxy(target, handler);  console.log(proxy.message); // 输出 "Hello, world!" console.log(proxy.nonExistent); // 输出 "Property nonExistent does not exist." proxy.message = "Hi!"; // 输出 "Setting message to Hi!" 

2. 拦截函数调用

let target = function() {     return "I am the target"; };  let handler = {     apply(target, thisArg, argumentsList) {         return "I am the proxy";     } };  let proxy = new Proxy(target, handler);  console.log(proxy()); // 输出 "I am the proxy" 

3. 拦截属性删除

let target = {     message: "Hello, world!" };  let handler = {     deleteProperty(target, prop) {         if (prop in target) {             delete target[prop];             console.log(`Property ${prop} deleted`);             return true;         } else {             console.log(`Property ${prop} does not exist`);             return false;         }     } };  let proxy = new Proxy(target, handler);  delete proxy.message; // 输出 "Property message deleted" delete proxy.nonExistent; // 输出 "Property nonExistent does not exist" 

三、二者对比

代理的粒度不同

defineProperty 只能代理属性,Proxy 代理的是对象。
defineProperty 如果想代理对象的所有属性,需要遍历并为每个属性添加 setter 和 getter。
Proxy 只需要配置一个可以获取属性名参数的函数即可。

是否破坏原对象

defineProperty 的代理行为会破坏原对象,它会将原本的 value 变成了 setter 和 getter。
Proxy 则不会破坏原对象,只是在原对象上覆盖了一层。当新增属性时,希望属性被代理,defineProperty 需要显式调用该 API,而 Proxy 则可以直接用 obj.key = val的形式

Object.definePropertyProxy
拦截范围只能拦截对象的单个属性操作,即只能定义特定属性的getter和setter可以拦截对对象的所有操作,包括属性访问、赋值、删除、函数调用等,可以使用get、set、deleteProperty等捕捉器来拦截这些操作
动态属性不可以处理动态属性可以处理对象的动态添加和删除属性
是否破坏原对象
性能性能逊于Proxy对于处理嵌套对象和大量属性的情况,性能好
兼容性兼容性更好由于ES6中才引入Proxy,所以兼容性略差

四、与Vue2、Vue3的关系

vue2响应式数据原理是Object.defineProperty。

Vue3响应式数据原理是Proxy。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!