重学JavaScript高级(九):Proxy用法详解

Proxy详解

Proxy-监听对象的操作

需求:监听某个对象属性的操作

先看以下之前的操作

  • 通过 Object.defineProperty进行监听某个属性是否发生了变化(同时也是 vue2中实现响应式的原理
let obj = { a: 100 };

let _value = "";
Object.defineProperty(obj, "a", {
  set(value) {
    _value = value;
    console.log("a的值发生了改变");
  },
  get() {
    console.log("a的值发生了访问");
    return _value;
  },
});

obj.a = 200;
console.log(obj.a);
  • 但是以上方法存在两个缺点
    • 1.Object.defineProperty属性设计的初衷并不是监听属性的操作,我们只是用这个API实现了相关的操作(无伤大雅
    • 2.对于属性操作的监听,只能实现读取和修改,对于新增和删除的属性,没有办法监听到(致命)

因此 引出了 Proxy类创建一个代理对象

  • 今后对 某个对象的操作,均可由代理对象完成,代理对象可以监听我们想要对原对象的某个操作

  • 以下是基本使用方法

    • set函数有四个参数
      • target:目标对象(监听对象)
      • property:监听的key值
      • value:新属性的值
      • receiver:调用的代理对象
    • get函数有三个参数
      • target:目标对象
      • property:监听的key
      • receiver:调用的代理对象
let obj = { a: 100 };

// new Proxy(target handler),
//target: 监听的属性
//handler具体操作:实际上是一个对象
let objProxy = new Proxy(obj, {
  get(target, key) {
    console.log(`获取了${key}的值`);
    return target[key];
  },
  set(target, key, newValue) {
    target[key] = newValue;
    console.log(`${key}进行了更改,改成了:${newValue}`);
  },
});

//修改现有属性
objProxy.a = 200;
//读取现有属性
console.log(objProxy.a);

//新增一个属性
objProxy.b = 300;
console.log(objProxy.b);

Proxy所有捕获器

Proxy13种捕获器

  • 我们说一下最常用的几个
    • handler.get() handler.set() handler.has() handler.deleteProperty
//上面对handler.get()  handler.set()进行了演示
let obj = { a: 100 };

// new Proxy(target handler),
//target: 监听的属性
//handler具体操作:实际上是一个对象
let objProxy = new Proxy(obj, {
  get(target, key) {
    console.log(`获取了${key}的值`);
    return target[key];
  },
  set(target, key, newValue) {
    target[key] = newValue;
    console.log(`${key}进行了更改,改成了:${newValue}`);
  },

  //删除属性的捕获器
  deleteProperty(target, key) {
    console.log(`${key}被删除了`);
    delete target[key];
    return true;
  },

  //in操作符的捕捉器
  has(target, key) {
    return key in target;
  },
});

//修改现有属性
objProxy.a = 200;
//读取现有属性
console.log(objProxy.a);

//新增一个属性
objProxy.b = 300;
console.log(objProxy.b);

//删除一个属性
delete objProxy.b;

//查看属性是否存在于对象种
console.log("b" in objProxy); //false
console.log("a" in objProxy); //true

  • 以上是针对普通对象的,针对于函数对象也有相应的捕获器 handler.apply() handler.construct()
function foo(name) {
  this.name = name;
  console.log(this.name);
}
let fooProxy = new Proxy(foo, {
  //第一个参数,构造函数本身
  //第二个参数,传入构造函数的参数
  construct(target, args) {
    console.log("创建了新的对象", target.name);
    //返回new之后的构造函数
    return new target(...args);
  },

    
  //第一个参数函数本身
  //第二个参数:执行上下文的对象
  //第三个参数:传入参数的数组
  apply(target, thisArg, otherArg) {
    //因此我们在这里可以写一些其他的操作
    //调用call,apply等等
    console.log("函数被调用了");
    return target(...otherArg);
  },
});

//通过new操作
let foo1 = new fooProxy("zhangcheng");
console.log(foo1);

//执行函数的代理
fooProxy("zhangcheng");

Reflect–一般与Proxy使用

作用

  • Reflect是一个对象,字面意思是反射

  • 它的作用与 Object种的操作对象的方法很类似,但是也会又细微的差别

    • Object.getPrototypeOf(obj)Reflect.getPrototypeOf(obj)

    • Object.defineProperty(obj,key,{})Reflect.defineProperty(obj,key,{})

  • 有了 Object可以进行这些操作,那么为什么还要增加 Reflect对象呢

    • 早期的ECMA规范种没有考虑到这种 操作对象本身,如何设计的更加规范,所以放到了Object上面
    • 但是 Object作为一个构造函数,这些操作方法放到它的身上是不合适的
    • 在ES6种,这些操作方法都增加到了Reflect上面
let obj = {a:100}
console.log(Reflect.deleteProperty(obj,"a"))//会直接返回boolean的值,显式有没有删除成功

Reflect.defineProperty(obj,"a",{})//对属性进行设置的时候,也会返回相应的boolean,来确定是否设置成功了

常见的方法

Reflect对象的13种用法,与Proxy一一对应

  • 与Proxy一同使用,可以避免操作原对象,同时可以返回boolean,告知是否操作成功
let obj = { a: 100 };

let objProxy = new Proxy(obj, {
  set(target, key, newValue, receiver) {
    //先前的做法
    // target[key] = newValue;
    //通过Reflect的做法,有返回值
    if (Reflect.set(target, key, newValue)) {
      console.log("修改成功");
    }
  },
});

objProxy.a = 200;
  • Reflect 可以设置receiver
    • receiver就是外面的 Proxy对象
    • 可以通过 receiver参数,改变源对象种this的指向

需求:创建一个对象,对象中有属性set方法,创建该对象的Proxy对象,实现监听对象内部的操作

let obj = {
  _a: 100,
  set a(newValue) {
    console.log(this);
    this._a = newValue;
  },
};

let objProxy = new Proxy(obj, {
  set(target, key, newValue, receiver) {
    console.log("更改成功");
    Reflect.set(target, key, newValue, receiver);
  },
});

/**
 内部操作
 1.调用obj中的set方法
 2.将this._a改变

 */
obj.a = 300;

/**
 内部操作
 1.调用objProxy中的set方法(第一次)
 2.调用obj中的set方法
 3.此时的this指向的是objProxy
 4.所以会再次调用objProxy中的set方法(第二次)
 相当于修改a的时候监听一次,修改_a的时候监听一次
 */
objProxy.a = 200;

你可能感兴趣的:(重学JavaScript高级,javascript,前端,开发语言)