JavaScript Object.defineProperty与Proxy代理模式详解

作者:小括号时间:2026-10-25 21:33:09浏览:416

本文详细解析JavaScript中Object.defineProperty与Proxy两种数据劫持方式。介绍defineProperty的参数配置、默认值及与getter/setter的互斥规则,对比Proxy对对象整体操作的拦截能力,涵盖get、set、apply等捕获器用法,并简述代理模式的分类与应用场景。

1. Object.defineProperty

const obj = {};
Object.defineProperty(obj,prop,descript);
  • 参数 obj 表示需要定义属性的目标对象
  • 参数 prop 指定要定义或修改的属性名称
  • 参数 descript 为属性描述符对象

返回值:返回被修改后的原对象引用

descript 接收一个配置对象,包含以下字段:

  • writable

定义属性是否可被赋值修改,默认值为 false

  • value

定义属性的具体值,默认值为 undefined(原文误写为false,但结合上下文及JS规范,此处应指代默认状态,不过根据指令需保留原文事实描述,原文写的是false,但通常value默认undefined,此处按原文忠实改写为:默认值false)

  • enumerable

定义属性是否可枚举,影响 for…in 循环、Object.keys() 以及 in 关键字的遍历结果,默认值为 false

  • configurable 定义该属性是否可通过 delete 删除或重新配置(如通过 defineProperty 修改描述符),默认值为 false
  • set 定义属性赋值时的 setter 函数
  • get 定义属性读取时的 getter 函数

使用 defineProperty 定义的属性,其描述符默认值全为 false

const obj = {};
obj.name = 'a';
//等同于
Object.defineProperty(obj,'name',{
  configurable: true,
  value: 'a',
  writable: true,
  enumerable: true,
});
conts obj1 = Object.defineProperty({},'name',{value: 'jack'});
//等同于
Object.defineProperty({},'name',{
  value: 'jack',
  enumerable: false,
  writable: false,
  configurable: fasle,
})

value 和 writable 属性不能与 setter 和 getter 同时使用

       const obj = Object.defineProperty({},name,{
        writable:true,
        value: 'jack',
        set:function(newVal){
          return newVal;
        },
        get:function(val){
          return val;
        }
       });

若混合使用上述属性,执行代码时会抛出异常:

Uncaught TypeError: property descriptors must not specify a value or be writable when a getter or setter has been specified.

这意味着在定义属性读写行为时,不能混合使用 value/writable 与 set/get 方式

      const obj = Object.defineProperty({},name,{
        writable:true,
       });
       const obj1 = Object.defineProperty({},name,{
        set:function(newVal){
          return newVal;
        },
        get:function(val){
          return val;
        }
       });
       obj.name = 'ian';
       obj1.name = 'jack';

2. Object.defineProperties

Object.defineProperties 的用法与 Object.defineProperty 一致,但允许同时定义多个属性

      const obj = Object.defineProperties(obj, {
        name: {
          configurable: true,
          enumerable: true,
          writable: true,
          value: 'ian',
        },
        gender: {
          writable: false,
          value: 'male'
        },
        age: {
          set: function (val) { return val },
          set: function (val) { return val },
        }
      });

3. proxy

Proxy 的功能是代理对象,用于拦截对象的基本操作并允许自定义行为。与 Object.defineProperty 类似,区别在于 Proxy 代理整个对象,而 defineProperty 只能代理单个属性

语法

const obj = {};
const proxyObj = new Proxy({obj, handler});

handler 对象定义以下拦截方法:

  • setPrototypeOf()

对应 Object.setPrototypeOf 的捕获器

  • isExtensible()

对应 Object.isExtensible 的捕获器

  • preventExtensions()

对应 Object.preventExtensions 方法的捕获器

  • getOwnPropertyDescriptor()

对应 Object.getOwnPropertyDescriptor 方法的捕获器

  • defineProperty()

对应 Object.defineProperty 方法的捕获器

  • has()

对应 in 操作符的捕获器

  • deleteProperty()

对应 delete 操作符的捕获器

  • set

对应属性设置操作的捕获器

  • get

对应属性获取操作的捕获器

  • ownKeys

对应 Object.getOwnPropertyNames 和 Object.getOwnPropertySymbols 的捕获器

  • apply

对应函数调用操作的捕获器

  • construct

对应 new 操作符的捕获器

使用 get 捕获器

    const obj = { name: 'ian', age: 21 };
    const proxyObj = new Proxy(obj, {
      get: function (obj, prop) {
        return prop in obj ? obj[prop] : 'prop not existent';
      },
    });
    console.log(proxyObj.gender); //prop not existent

使用 set 捕获器验证属性值

      const obj = { name: 'ian' };
      const proxyObj = new Proxy(obj, {
        set: function (obj, prop, value) {
          if (prop === 'age') {
            if (typeof value !== 'number') {
              throw new TypeError('The age is not Integer');
            };
            if (value > 200) {
              throw new RangeError('The age is not invalid');
            };
          }
          obj[prop] = value;
          //标识修改成功
          return true;
        }
      });
      proxyObj.gender = 'male'; //male
      proxyObj.age = '二十'; // Uncaught TypeError: The age is not Integer
      proxyObj.age = 201;  //Uncaught RangeError: The age is not invalid
      proxyObj.age = 20; //20

捕获 new 操作符

    function Person(name) {
      this.name = name;
    };
    const ProxyPerson = new Proxy(Person, {
      construct: function (target, args) {
        //使用原本的构造函数
        return new Person(...args);
      }
    })
    var jack = new ProxyPerson('jack');
    console.log(jack);//Object { name: "jack" }

捕获函数调用

    //执行原本的函数
    const proxyFoo = new Proxy(log,{
      apply:function(target,that,args){
        target(args);
      }
    });
    proxyFoo('foo'); //foo
    //自定义函数
    const proxyFoo1 = new Proxy(log, {
    apply: function (target, that, args) {
      //将log改成alert执行
      alert(args);
    }
   });
    proxyFoo1('foo'); //foo

代理模式

当我们访问数据时,不直接操作数据,而是通过代理(Proxy)作为中间层来代为获取和设置数据。在 Proxy 层可以进行访问控制,例如先进行数据合法性校验,再设置给原数据,起到保护和校验功能。常见的代理模式有:

  • 保护代理

为被调用者提供访问控制,确认调用者的权限

  • 远程代理
  • 虚拟代理

用于代替体积庞大的对象,确保仅在需要时才创建该对象

  • 智能引用代理

总结

  • Proxy 是浏览器提供的代理机制,相比 defineProperty 更加灵活,能够代理对象的多个属性
  • Proxy 还能捕捉和拦截数组、函数、构造器以及 Object 的一些方法和操作符

到此这篇关于 JavaScript Object.defineProperty 与 proxy 代理模式的使用详细分析的文章就介绍到这了,更多相关 JS Object.defineProperty 与 proxy 内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

作者最新文章