Proxy实践
概述
Proxy是ES6推出的一个类,用于给对象架设一层拦截器,但凡要访问或修改对象上的值或属性,都必须经过这层拦截器,  Proxy也叫代理器, 它代理了对对象的操作。
Proxy
Proxy 用于创建一个对象的代理,从而实现操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
const p = new Proxy(target, handler)
参数详见:
target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
基础示例
在以下简单的例子中,当对象中不存在属性名时,默认返回值为 37,下面以此展示 get handler 的使用场景。
const handler = {
    get: function(obj, prop) {
        return prop in obj ? obj[prop] : 37;
    }
};
const p = new Proxy({}, handler);
p.a = 1;
console.log('c' in p, p.c); // false, 37
代理转发示例
代理会将所有应用到它的操作转发到这个源 target 对象上
let target = {};
let p = new Proxy(target, {});
p.a = 37;   // 操作转发到目标
console.log(target.a);   // 37 操作已经被正确地转发
详见MDN文档
Proxy使用示例
<script>
const obj = {
    name: "花花",
    age: 18,
    address: "陕西省西安市",
};
// 创建一个代理的对象进行属性的:增,读,删,等操作
const ObjProxy = new Proxy(obj, {
    //set与get中传入的值说明
    //1.target:原对象
    //2.key:当前属性名
    //3.newValue:修改的新值
    //4.receiver:代理的这个对象
    set: function (target, key, newValue, receiver) {
     // 第一种写法:监听到某个属性名新值就赋值给原对象的某个属性名
     // target[key] = newValue;
     // 第二种写法:也是vue3源码的写法,利用Reflect.set来实现监听新值
     Reflect.set(target, key, newValue);
     console.log(`监听:监听到${target[key]}的值已经被修改成了`, newValue);
    },
 
    get: function (target, key, receiver) {
     console.log(`监听:${key}属性被读取了`);
     // 第一种写法
     // return target[key];
     // 第二种写法
     return Reflect.get(target, key, receiver);
    },
   });
   //对这个代理对象进行值修改
   ObjProxy.name = "小红";
   //读取原对象看看是否修改成功
   console.log(obj);
  </script>
Object.defineProperty和proxy的区别
两者常被用于数据拦截,其区别如下:
Object.defineProperty 是一个老方法,兼容性好,Proxy是新方法,有更好的性能和功能,但不兼容IE,也没有polyfill;
Object.defineProperty 是通过修改原对象上的属性来实现数据拦截, 而Proxy是在原对象上加一层拦截,并不会修改原对象;
Object.defineProperty 功能单一,如:无法监听数组变化等;Proxy功能强大,是对整个对象进行拦截;
Object.defineProperty的问题有三个:
- 不能监听数组变化,如:数组的 push、pop、shift、unshift、splice、sort、reverse等方法都不能触发set,Vue是对其变异方法进行了重写。
 - 必须遍历对象的每个属性,并对其设置 set、get 方法;
 - 当一个对象有深层嵌套时,必须逐层遍历所有属性,直到每个对象都调用到Object.defineProperty为止
 
Object.keys(obj).forEach(key => {
   Object.defineProperty(obj, key, {
      ...
   })
})
Proxy优势
1.0> 支持数组,不需要对数组方法进行重载;
Proxy代理整个对象,省略了使用 Object.keys() 逐个遍历所有属性的过程;
Proxy只是代理原对象,并未修改原对象;因此在Proxy中可调用原对象本身的方法和属性;
let arr = [1,2,3]
let proxy = new Proxy(arr, {
    get(target, key, receiver) {
        console.log('get', key)
        return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver){
         console.log('set', key, value)
         return Reflect.get(target, key, value, receiver)
    }
})
proxy.push(4)
// 打印内容
4 get push
4 get length
8 set 3 4
8 set length 4
2.0> 嵌套支持:Get里面递归调用proxy并返回
let obj = { a:[1, 2, 3], b:1 }
let handler = {
    get(target, key, receiver) {
        console.log('get', key)
        if(typeof target[key] === 'object' && target[key] !== null)
           return new Proxy(target[key], handler)
        return Reflect.get(target, key, receiver)
    },
   set(target, key, value, receiver) {
       console.log('set', value)
       return Reflect.set(target, key, value, receiver)
   }
}
let proxy = new Proxy(obj, handler)
proxy.b = 8
proxy.a.push(4)
Reflect是一个内置对象,提供拦截js的方法;即,通过Reflect可以直接调用某个对象上的属性或方法,同时 Reflect 具有功能强大的方法。
Polyfill 是一块代码,用来为旧浏览器提供它没有原生支持的较新的功能。