← 返回目录

13. Proxy

Proxy 就是给对象装个拦截器,读写属性前先过一层处理。

核心两个:
get 拦截——读属性时拦截
set 拦截——写属性时拦截

坑:Vue3 响应式原理就是 Proxy、Reflect 配合 Proxy 使用、this 指向问题

13.1 互动演示(数据校验)

给 user 设置 age:
填负数点"设置 age",看 Proxy 怎么拦截。

13.2 知识点讲解

① 没有代理 vs 有代理

没有经纪人时,你直接摸原对象,摸到啥是啥;有了经纪人后,每一次读写都先过 handler 里的规则。

// 没有代理:直接拿结果,拦不住
// 原对象,没有经纪人
let target = { name: "John" };

// undefined(什么都没有)
console.log(target.age);

// 有代理:每次读都先过 handler.get
// 经纪人的规则本
let handler = {

// 拦截"读"这个动作
  get(target, prop) {

// 读 age 就直接返回 30
    if (prop === "age") return 30;

// 其他属性照原样读
    return target[prop];
  }
};

// 包一层经纪人
let proxy = new Proxy(target, handler);

// 30(被经纪人加工了)
console.log(proxy.age);

new Proxy(原对象, 经纪人规则) 生成代理对象。以后操作一律用 proxy,不要再直接碰 target——直接碰就绕过经纪人了,拦截规则白写。

② set:拦截"写"属性(记日志 / 校验)

经纪人不仅拦"读",还拦"写"。往对象上塞属性时,先问一句"这合法吗"。

// 经纪人规则本
let handler = {

// 拦截"写"
  set(target, prop, value) {

// 先记个日志
    console.log("有人要把 " + prop + " 改成 " + value);

// 真正写进去
    target[prop] = value;

// 表示放行
    return true;
  }
};

// 包一层
let proxy = new Proxy(target, handler);

// 写属性会先被 set 拦住
proxy.skill = "前端";

// 打印:有人要把 skill 改成 前端

③ 真实用途:数据校验(把好关)

最常见的实战场景:不允许 age 被设成负数。set 里判断一下,不合规就拒绝写入。

// 原对象,年龄 20
let user = { age: 20 };

// 经纪人规则本
let handler = {

// 拦截写
  set(target, prop, value) {

// 要写的是 age 且是负数
    if (prop === "age" && value < 0) {

// 报警
      console.log("年龄不能是负数!");

// ★ 拒绝这次写入
      return false;
    }

// 合法才真写
    target[prop] = value;

// 放行
    return true;
  }
};

// 包一层经纪人
let proxy = new Proxy(user, handler);

// 想写成负数,被拦下
proxy.age = -5;

// 打印:年龄不能是负数!
// 20(没写进去)
console.log(proxy.age);

setreturn false 表示"拒绝这次写入"(严格模式下还会抛错)。Vue 3 的响应式原理就是用 Proxy 拦截读写、数据一变就自动更新界面——理解它等于摸到响应式框架的门。

13.3 实战:用在哪 / 常见坑 / 怎么解决

① 可能在什么地方用:表单数据校验(不合法就拒写)、给对象读写加日志、Vue 3 响应式原理、实现"只读"对象、数据懒加载/默认值兜底。

② 常见的问题:创建代理后还直接用原对象导致拦截不生效;set 里忘了 return trueget 里循环引用导致死循环。

③ 解决思路:只通过 proxy 访问对象;set 放行时务必 return trueget 里要返回对象时记得返回 Reflect.get(...) 或原对象属性,别在拦截器里又触发自己。

一句话:Proxy = 给对象请个经纪人,读走 get、写走 set,能拦截、能加工、能把关。

本页重点

API作用参数返回值代码示例
new Proxy(obj, 处理器)给原对象包一层经纪人,读写先过它原对象, 处理器一个代理new Proxy(obj, h);
get(目标, 键)读属性时被拦住对象, 属性名读出来的值get(target, key) {}
set(目标, 键, 值)写属性时被拦住对象, 属性名, 新值true 放行set(target, key, v) {}