35.Proxy和Reflect详解

2021-11-29  本文已影响0人  静昕妈妈芦培培

监听对对象的操作

使用Object.defineProperty()监听对对象进行操作
const obj = {
  name: "coderwhy",
  age: 18,
};

Object.keys(obj).forEach((key) => {
  let value = obj[key];
  Object.defineProperty(obj, key, {
    set: function (newVal) {
      value = newVal;
      console.log(`监听到给${key}属性设置值`);
    },
    get: function () {
      console.log(`监听到获取${key}属性的值`);
      return value;
    },
  });
});

console.log(obj.name);
obj.age = 30;

// 监听到获取name属性的值
// coderwhy
// 监听到给age属性设置值

Proxy基本使用

在ES6中,新增了一个Proxy类,用于帮助我们创建一个代理:

new Proxy(target, handler)

target为要监听的目标对象
Proxy对象有13种默认的捕获器方法,
如果需要监听对原对象的某种操作,然后做出相应处理,可以在handler对象中重写对应的捕获器方法

使用Proxy监听对对象进行操作
const obj = {
  name: "coderwhy",
  age: 18,
};

//创建一个obj的代理对象
const objProxy = new Proxy(obj, {
  //重写proxy对象的get捕获器
  get(target, key) {
    console.log(`监听到获取obj对象的${key}属性的值`);
    return target[key];
  },
  //重写proxy对象的set捕获器
  set(target, key, newVal) {
    console.log(`监听到obj对象的${key}属性的值被重新赋值了`);
    target[key] = newVal;
  },
});

//对obj对象要做的所有操作,都对其代理对象操作,
console.log(objProxy.name);
objProxy.age = 30;

// 监听到获取obj对象的name属性的值
// coderwhy
// 监听到obj对象的age属性的值被重新赋值了

Proxy的13中捕获器用法

const obj = {
  name: "coderwhy",
  age: 18,
};

//创建一个obj的代理对象
const objProxy = new Proxy(obj, {
  //重写proxy对象的get捕获器:监听获取目标对象的属性值
  get(target, key, receiver) {
    //get捕获器有三个参数:
    //target为目标对象,key为当前操作的属性名,receiver为目标对象的代理对象即objProxy
    console.log(`监听到获取obj对象的${key}属性的值`);
    return target[key];
  },
  //重写proxy对象的set捕获器:监听设置目标对象的属性值
  set(target, key, newVal, receiver) {
    //set捕获器有四个参数:
    //target为目标对象,key为当前操作的属性名,newVal为给此属性设置的新值,receiver为目标对象的代理对象即objProxy
    console.log(`监听到obj对象的${key}属性的值被重新赋值了`);
    target[key] = newVal;
  },
  //监听对对象进行的in操作
  has: function (target, key) {
    console.log(`监听到对obj对象的${key}属性的in操作`);
    return key in target;
  },
  //监听对对象的delete操作的捕获器
  deleteProperty: function (target, key) {
    console.log(`监听到对obj对象的${key}属性的delete操作`);
    delete target[key];
  },
});

//对obj对象要做的所有操作,都对其代理对象操作,

//获取对象的属性值
console.log(objProxy.name);
//设置对象的属性值
objProxy.age = 30;

// in操作
console.log("name" in objProxy);

//delete操作
delete objProxy.age;
console.log(obj);

proxy对函数对象的监听

function foo(name, age) {
  this.name = name;
  this.age = age;
}

//创建foo的代理对象
const fooProxy = new Proxy(foo, {
  //监听对函数对象的apply调用
  apply(target, thisArg, argArray) {
    //target为目标对象,thisArg为给目标函数绑定的this,argArray为传给目标函数的参数数组
    console.log("对foo进行了apply调用");
    return target.apply(thisArg, argArray);
  },
  //监听对函数对象的new调用
  construct(target, argArray) {
    //target为目标对象,argArray为传给目标函数的参数数组
    console.log("对foo进行了new调用");
    return new target(...argArray);
  },
});

fooProxy.apply({}, ["why", "18"]);
new fooProxy("lily", 30);

另外七种捕获器用法:
下面为知识点扩展:

const obj = { name: "why", age: 18 };
const objProxy = new Proxy(obj, {
  //监听对目标对象进行的Object.getPrototypeOf操作
  getPrototypeOf(target) {
    console.log("监听到了对obj进行的Object.getPrototypeOf操作");
    return Object.getPrototypeOf(target);
  },
  setPrototypeOf(target, prototype) {
    // target为目标对象 prototype为要被设置为目标对象原型对象的对象
    console.log("监听到了对obj进行的Object.setPrototypeOf操作");
    return Object.setPrototypeOf(target, prototype);
  },
  getOwnPropertyDescriptor(target, prop) {
    console.log("监听到了对obj进行的Object.getOwnPropertyDescriptor操作");
    return Object.getOwnPropertyDescriptor(target, prop);
  },
  defineProperty(target, property, descriptor) {
    console.log("监听到了对obj进行的Object.defineProperty操作");
    return Object.defineProperty(target, property, descriptor);
  },
  //监听对目标对象进行的Object.getOwnPropertySymbols或Object.getOwnPropertyNames或Reflect.ownKeys操作
  ownKeys(target) {
    console.log(
      "监听到了对obj进行的Object.getOwnPropertyNames/Object.getOwnPropertySymbols操作"
    );
    return Object.getOwnPropertyNames(target);
  },
  isExtensible(target) {
    console.log("监听到了对obj进行的Object.isExtensible操作");
    return Object.isExtensible(target);
  },
  preventExtensions(target) {
    console.log("监听到了对obj进行的Object.preventExtensions操作");
    return Object.preventExtensions(target);
  },
});
const objPrototype = Object.getPrototypeOf(objProxy);
console.log(objPrototype); // [Object: null prototype] {}

Object.setPrototypeOf(objProxy, { title: "讲师" });
Object.getOwnPropertyDescriptor(objProxy, "name");
Object.defineProperty(objProxy, "height", {
  value: "1.88",
  writable: true,
  enumerable: true,
  configurable: true,
});
Object.getOwnPropertyNames(objProxy);
Object.getOwnPropertySymbols(objProxy);
console.log(Reflect.ownKeys(objProxy));
console.log(Object.isExtensible(objProxy));
Object.preventExtensions(objProxy);
console.log(Object.isExtensible(objProxy));

知识拓展:
例:

const s = Symbol();
const obj = {
  name: "why",
  age: 30,
  [s]: "我是灵魂画手",
};

console.log(Reflect.ownKeys(obj)); //[ 'name', 'age', Symbol() ]
console.log(Object.getOwnPropertyNames(obj)); //[ 'name', 'age' ]
console.log(Object.getOwnPropertySymbols(obj)); //[ Symbol() ]
console.log(
  Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj))
); //[ 'name', 'age', Symbol() ]

Reflect

Reflect也是ES6新增的一个API,它是一个对象,字面的意思是反射。
那么这个Reflect有什么用呢?

如果我们有Object可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢?

那么Object和Reflect对象之间的API关系,可以参考MDN文档:
https://developer.mozilla.org/zh�CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/Comparing_Reflect_and_Object_methods
下表详细介绍了Object 和 Reflect API上可用方法之间的差异。请注意,如果API中不存在某种方法,则将其标记为N/A。

image.png
image.png
image.png
let s = Symbol();
const obj = {
  _name: "why",
  get name() {
    return this._name;
  },
  set name(newVal) {
    this._name = newVal;
  },
  s: "不可描述的秘密",
};

//为对象obj添加一个不可枚举属性work
Object.defineProperty(obj, "work", {
  value: "teacher",
});

// console.log(Object.getOwnPropertyDescriptors(obj))
console.log(Object.keys(obj)); // [ '_name', 'name', 's' ]
//Object.keys(target)返回目标对象所有可枚举属性名组成的数组
console.log(Reflect.ownKeys(obj)); //[ '_name', 'name', 's', 'work' ]
//Reflect.ownKeys(target)返回目标对象素有的属性名组成的数组

Refect和Proxy结合使用

const obj = {
  name: "why",
  age: 18,
};

const objProxy = new Proxy(obj, {
  get: function (target, key) {
    console.log(`监听到获取对象的${key}的属性值`)
    return Reflect.get(target, key);
  },
  set: function (target, key, newVal) {
    //Reflect.set的执行返回一个布尔值,如果设置值成功,为true,如果失败为false
    const result = Reflect.set(target, key, newVal);
    if (result) {
      console.log("设置值成功");
    } else {
      console.log("设置值失败");
    }
  },
});

objProxy.name = 'lily'
console.log(objProxy.name)

Reflect的receiver参数

const obj = {
  _name: "why",
  get name() {
    return this._name;
  },
  set name(newVal) {
    this._name = newVal;
  },
};

const objProxy = new Proxy(obj, {
  get(target, key) {
    console.log(`监听到了获取对象的${key}的值的操作`)
    return Reflect.get(target, key)
  }
})

console.log(objProxy.name)
image.png
分析:

Reflect.get(target, key, receiver)方法接受三个参数,

所以可以 通过给Reflect.get传递receiver来改变目标对象中getter调用时的this指向
而Proxy对象的get捕获器接收的第三个参数receiver为该Proxy对象

const obj = {
  _name: "why",
  get name() {
    return this._name;
  },
  set name(newVal) {
    this._name = newVal;
  },
};

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    console.log(`监听到了获取对象的${key}的值的操作`)
    return Reflect.get(target, key, receiver)
  }
})

console.log(objProxy.name)
image.png

Reflect.construct(target, argArray[, newTarget])

function Student(name, age) {
  this.name = name;
  this.age = age;
}
function Teacher() {}
const stu = new Student("lily", 18);
console.log(stu); //Student { name: 'lily', age: 18 } 可以看到创建了一个Student类的对象
console.log(stu.__proto__ === Student.prototype); //true

//现在有一个需求,执行Student方法,但是创建出来的是Teacher类的对象
var stu1 = Object.create(Teacher.prototype); //创建一个空对象,其__proto__指向Teacher.prototype
Student.apply(stu1, ["lily", 18]);
console.log(stu1); // Teacher { name: 'lily', age: 18 }
console.log(stu1.__proto__ === Teacher.prototype); //true

//下面的方式与上面的方式等效
const stu2 = Reflect.construct(Student, ["lily", 18], Teacher);
console.log(stu2); // Teacher { name: 'lily', age: 18 }
console.log(stu2.__proto__ === Teacher.prototype); //true

非常感谢王红元老师的深入JavaScript高级语法让我学习到很多 JavaScript 的知识

上一篇下一篇

猜你喜欢

热点阅读