`Proxy` 是什么?

147 阅读3分钟

背景

群友在群里提出了一道题目

add[1][2][3] + 4 = 10
add[10][20] + 70 = 100

处理这道题用到了 Proxy,我知道 vue响应式原理就是从vue2的Object.defineProperty转变为了Proxy,但是一直没有深入了解过

基础知识

什么是 Proxy

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

基本语法

// 语法
//const p = new Proxy(target, handler)

// 代理目标
const target = {} 
// 代理对象
const proxyHandler = {}
// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler)

这就是基本的代理创建。

属性查找(get 拦截器)

// 代理目标
const target = { a: 1, b: 2, c: 3, d: 4, e: 5 };
// 代理对象
const proxyHandler = {
//++++++++++++++++++++++++++ start
  get() {
    return 100; // 这里无论如何访问,返回都是100
  },
// ++++++++++++++++++++++++++ edd
};
// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler);

console.log(proxy.a); // 100
console.log(proxy.notExit) // 100

标记的这一块就是我们代理的拦截器,他会拦截所有通过 proxy这个代理对象的访问操作

get(target,property,recevtive) 属性查找(get 拦截器)

  1. target 没错这个就是我们初始创建的那个代理目标
  2. property 就是我们访问的属性,比如proxy.a,那proerty就是a; proxy.notExitproerty就是notExit
  3. recevtive 这个就是代理本身,实际就是 recevtive === proxy,但是我不知道这个应用场景在哪里?
// 代理目标
const target = { a: 1, b: 2 };
// 代理对象
const proxyHandler = {
  get(target, property, recetive) {
  // ++++++++++++++++++++++++++++++ start
    if (property in target) {
      return target[property];
    } else {
      return `Sorry, ${property} is not exist`;
    }
  // ++++++++++++++++++++++++++++++ end
  },
};
// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler);

console.log(proxy.a); // 输出: 1
console.log(proxy.notExit); // 输出: Sorry, notExit is not exist

属性赋值(set 拦截器)

set(target,property,value,recetive)

  1. targetpropertyrecetiveget拦截器一样
  2. value : 这个value就是你设置的 proxy.a=100,value就是100
// 代理目标
const target = { a: 1, b: 2 };
// 代理对象
const proxyHandler = {
  set(target, property, value, recetive) {
   // ++++++++++++++++++++++++++++++ start
    console.log(`target[${property}]: ${target[property]} ====> ${value}`);
    target[property] = value;
    return true; // 返回true表示赋值成功
    // ++++++++++++++++++++++++++++++ end
  },
};
// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler);
//设置值
proxy.a = 100; // target[a]: 1 ====> 100

属性删除(deleteProperty 拦截器)

deleteProperty(target,property)

  1. targetpropertyget拦截器一样
// 代理目标
const target = { a: 1, b: 2 };
// 代理对象
const proxyHandler = {
  deleteProperty(target, property) {
  // ++++++++++++++++++++++++++++++ start
    console.log(`target[${property}]: ${target[property]}`);
    delete target[property];
 // ++++++++++++++++++++++++++++++ end
  },
};
// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler);
delete proxy.a; // target[a]: 1
delete proxy.notExit; // target[notExit]: undefined
console.log(proxy); // { b: 2 }

枚举(ownKeys 拦截器)

ownKeys(target)

  1. targetget拦截器一样
// 代理目标
const target = { a: 1, b: 2 };
// 代理对象
const proxyHandler = {
  ownKeys(target) {
  // ++++++++++++++++++++++++++++++ start
   return ["a"];
 // ++++++++++++++++++++++++++++++ end
  },
};
// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler);
// 未处理的情况下应该返回 ['a','b']
console.log(Object.keys(proxy)); // 返回值:['a']

函数调用(apply 拦截器)

apply(target, isthis, argumentsList)

  1. target 这个同上面get拦截器一样
  2. isthis 这是函数调用时的 this 值,即调用目标函数时 this 的绑定对象
// 代理目标        如果这是一个箭头函数 isthis 是什么样的呢?
const target = function () {
  return this.value;
};

// 代理对象
const proxyHandler = {
  apply(target, isthis, argumentsList) {
    console.log("target => ", target); // 这是target函数
    console.log("isthis:", isthis); // 这个是我们指向的对象 {value: 42}
    console.log("argumentsList => ", argumentsList); // 这是参数列表,没有就是 [] *** 注意这里是function函数,如果是箭头函数会是什么?***
    return target.apply(isthis, argumentsList);
  },
};

// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler);
const content = { value: 42 };
console.log(proxy.apply(content)); // 42
  1. argumentsList 这是调用目标函数时传递的参数列表,形式为数组
// 代理目标!!!! 这里是一个 function
const target = function (x, y) {
  return x + y;
};
// 代理对象
const proxyHandler = {
  apply(target, isthis, argumentsList) {
    console.log("target => ", target); // target函数
    console.log("isthis:", isthis); // undefined
    console.log("argumentsList => ", argumentsList); // [1,2]
    return target(argumentsList) * 2;
  },
};
// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler);

console.log(proxy(1, 2)); // 6

拦截属性定义(defineProperty 拦截器)

defineProperty(target, property, descriptor)

  1. targetpropertyget拦截器一样
  2. descriptor 这是包含属性描述符的对象,用于定义或修改属性的特性
const target = {};
// 代理对象
const proxyHandler = {
  defineProperty(target, property, descriptor) {
    console.log("target => ", target); // {}
    console.log("property => ", property); // a
    console.log("descriptor => ", descriptor); // { value: 10, writable: true }
    return Object.defineProperty(target, property, descriptor);;
  },
};
// 创建一个Proxy
const proxy = new Proxy(target, proxyHandler);
Object.defineProperty(proxy, "a", { value: 10, writable: true });
console.log(proxy.a); // 10

新的问题

  1. ProxydefineProperty 区别是啥?
  2. vue2 => vue3为什么把defineProperty 转为使用 Proxy