ES6 Proxy 的基本用法

ES6 Proxy 的基本用法

一、Proxy 是什么?

Proxy(代理)是 ES6 引入的一个新特性,用于创建一个对象的代理,从而可以拦截并自定义该对象的基本操作。

二、基本语法

javascript 复制代码
const proxy = new Proxy(target, handler);
  • target:要包装的目标对象
  • handler:包含拦截器(traps)的对象

三、常用的拦截方法

1. get() - 拦截属性读取

javascript 复制代码
const person = { name: 'Alice', age: 25 };
const proxy = new Proxy(person, {
  get(target, property) {
    if (property in target) {
      return target[property];
    }
    return `属性 ${property} 不存在`;
  }
});

console.log(proxy.name); // 'Alice'
console.log(proxy.address); // '属性 address 不存在'

2. set() - 拦截属性设置

javascript 复制代码
const validator = {
  set(target, property, value) {
    if (property === 'age') {
      if (typeof value !== 'number') {
        throw new Error('年龄必须是数字');
      }
      if (value < 0) {
        throw new Error('年龄不能为负数');
      }
    }
    target[property] = value;
    return true; // 表示设置成功
  }
};

const person = new Proxy({}, validator);
person.age = 25; // 正常
// person.age = -5; // Error: 年龄不能为负数

3. has() - 拦截 in 操作符

javascript 复制代码
const handler = {
  has(target, property) {
    if (property.startsWith('_')) {
      return false; // 隐藏私有属性
    }
    return property in target;
  }
};

const obj = { name: 'Alice', _password: '123' };
const proxy = new Proxy(obj, handler);
console.log('name' in proxy); // true
console.log('_password' in proxy); // false

4. apply() - 拦截函数调用

javascript 复制代码
function sum(a, b) {
  return a + b;
}

const proxy = new Proxy(sum, {
  apply(target, thisArg, argumentsList) {
    console.log(`调用函数: ${target.name}`);
    console.log(`参数: ${argumentsList}`);
    return target.apply(thisArg, argumentsList);
  }
});

console.log(proxy(1, 2)); // 3
// 输出:
// 调用函数: sum
// 参数: 1,2

5. construct() - 拦截 new 操作符

javascript 复制代码
class Person {
  constructor(name) {
    this.name = name;
  }
}

const ProxyPerson = new Proxy(Person, {
  construct(target, args) {
    console.log(`创建 ${target.name} 实例`);
    return new target(...args);
  }
});

const p = new ProxyPerson('Alice'); // 输出: 创建 Person 实例

四、实用示例

1. 数据验证

javascript 复制代码
const userValidator = {
  set(target, prop, value) {
    if (prop === 'email' && !/^\S+@\S+\.\S+$/.test(value)) {
      throw new Error('邮箱格式不正确');
    }
    if (prop === 'age' && (value < 0 || value > 150)) {
      throw new Error('年龄无效');
    }
    target[prop] = value;
    return true;
  }
};

const user = new Proxy({}, userValidator);
user.email = 'test@example.com'; // 正常
// user.email = 'invalid-email'; // Error

2. 数组负索引支持

javascript 复制代码
function createNegativeArray(array) {
  return new Proxy(array, {
    get(target, prop) {
      const index = Number(prop);
      if (index < 0) {
        return target[target.length + index];
      }
      return target[prop];
    }
  });
}

const arr = createNegativeArray([1, 2, 3, 4, 5]);
console.log(arr[-1]); // 5
console.log(arr[-2]); // 4

3. 计算属性

javascript 复制代码
const expensiveComputation = {
  cache: new Map(),
  get(target, property) {
    if (property === 'expensiveData') {
      if (!this.cache.has('expensiveData')) {
        console.log('执行昂贵计算...');
        const result = /* 昂贵计算 */ '计算结果';
        this.cache.set('expensiveData', result);
      }
      return this.cache.get('expensiveData');
    }
    return target[property];
  }
};

const obj = new Proxy({}, expensiveComputation);
console.log(obj.expensiveData); // 执行昂贵计算... 计算结果
console.log(obj.expensiveData); // 计算结果(从缓存读取)

4. 方法链式调用

javascript 复制代码
const chainable = {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    }
    return new Proxy(() => {}, {
      apply() {
        console.log(`调用了方法: ${prop}`);
        return proxy; // 返回代理以支持链式调用
      }
    });
  }
};

const obj = new Proxy({}, chainable);
obj.say().hello().world();
// 输出:
// 调用了方法: say
// 调用了方法: hello
// 调用了方法: world

五、注意事项

  1. 性能:Proxy 操作比直接对象操作稍慢,在性能关键代码中慎用
  2. this 绑定:Proxy 中的 this 指向代理对象,而非目标对象
  3. 不可撤销代理 :使用 Proxy.revocable() 创建可撤销的代理
javascript 复制代码
const { proxy, revoke } = Proxy.revocable(target, handler);
revoke(); // 撤销代理

六、浏览器兼容性

  • 现代浏览器基本都支持
  • Node.js 6.0+ 支持

Proxy 是 JavaScript 元编程的强大工具,可以用于实现各种高级功能,如数据绑定、验证、日志、性能优化等。
Vue 3 中使用 Proxy 来替代 Vue 2 中的 Object.defineProperty,实现了响应式系统。以后可以基于这个方面,全面分析一下Vue3 怎么实现的响应式系统。里面牵涉到Proxy用法,依赖收集和触发更新,副作用函数,数组的处理,reactive的实现逻辑,watch、ref、shallowReactive、computed等,挺有意思的。

相关推荐
DevUI团队7 小时前
从“即兴创作”到“规格先行”,华为云码道(CodeArts)代码智能体持续深耕企业级规范驱动开发能力
前端·人工智能·后端
weixin_431600448 小时前
NestJS 入门(3):Guard 如何挡住未登录请求?
前端·后端·学习·nest.js
kyriewen9 小时前
我用Claude Code两天干完了团队两周的排期——周报发出去那一刻我就后悔了
前端·javascript·ai编程
IT_陈寒9 小时前
JavaScript类型转换把我坑惨了,这破玩意真该早点搞明白
前端·人工智能·后端
用户938515635079 小时前
Type vs Interface:读完这篇就没有面试官能难倒你了
前端·面试·typescript
油丶酸萝卜别吃10 小时前
jquery-ajax.js 说明文档
前端·javascript·jquery
windliang10 小时前
Claude Code 源码分析(九):子 Agent 如何分叉、继续与回到父会话
前端·javascript·面试
柒和远方11 小时前
V063: TS 面试必考:interface 与 type 的四大差异,与 LLM Harness 的自动化择优
前端·javascript
半个落月11 小时前
React useRef 详解:DOM 引用、持久化值与 Worker 实例
前端·react.js
阿黎梨梨11 小时前
TypeScript 类型编程:从新手到 Harness 工程实践
前端