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等,挺有意思的。

相关推荐
mCell5 小时前
如何零成本搭建个人站点
前端·程序员·github
mCell6 小时前
为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞
前端·设计模式·agent
恋猫de小郭6 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
少云清6 小时前
【安全测试】2_客户端脚本安全测试 _XSS和CSRF
前端·xss·csrf
银烛木7 小时前
黑马程序员前端h5+css3
前端·css·css3
m0_607076607 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
听海边涛声7 小时前
CSS3 图片模糊处理
前端·css·css3
IT、木易7 小时前
css3 backdrop-filter 在移动端 Safari 上导致渲染性能急剧下降的优化方案有哪些?
前端·css3·safari
0思必得07 小时前
[Web自动化] Selenium无头模式
前端·爬虫·selenium·自动化·web自动化
anOnion7 小时前
构建无障碍组件之Dialog Pattern
前端·html·交互设计