目录

Proxy-Reflect使用

# 监听对象操作

在ES5时提到如果需要监听一个对象中属性的变化可以使用Object.defineProperty的存储属性符来对属性进行监听

const obj = {
  name: "zs",
  age: 20,
};
Object.keys(obj).forEach((key) => {
  let value = obj[key];
  Object.defineProperty(obj, key, {
    set: function (newValue) {
      console.log(`监听到给${key}设置新的值`);
      value = newValue;
    },
    get: function () {
      console.log(`获取${key}属性的值`);
      return value;
    },
  });
});
console.log(obj.name);
console.log(obj.age);
obj.name = "ls";
console.log(obj.name);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

缺点

  • Object.defineProperty的设计初衷不是为了监听截获对象中的所有属性的。在定义某些属性的时候初衷是定义普通属性,但是使用时强行将它变成了数据属性描述符
  • 如果想监听更加丰富的操作,比如新增属性、删除属性,那么Object.defineProperty是做不到的

# Proxy的基本使用

在ES6中,新增加了一个Proxy类。也就是说如果希望监听一个对象的相关操作,可以先创建一个代码对象(Proxy对象);之后对该对象的所有操作都用过代理对象来完成,代理对象可以监听想要对原生对象进行哪些操作。

使用Proxy的步骤

  • 首先需要创建一个Proxy对象,并且传入需要侦听的对象以及一个处理对象,称为handler
    • const p = new Proxy(target, handler)
  • 之后的操作都是直接对Proxy的操作,而不是原有对象,因为需要在handler中进行监听
const obj = {
    name: 'zs',
    age: 20
}
const objProxy = new Proxy(obj, {})
1
2
3
4
5

# Proxy的set和get捕获器

如果需要侦听某些具体的操作,那么就可以在handler中添加对应的捕捉器

set和get分别对应的是函数类型

set函数有四个参数

  • target:目标对象(侦听的对象)
  • property:将被设置的属性key
  • value:新属性值
  • receiver:调用的代理对象

get函数有三个参数

  • target:目标对象(侦听的对象)
  • property:被获取的属性key
  • receiver:调用的代理对象
const obj = {
  name: "zs",
  age: 20,
};
const objProxy = new Proxy(obj, {
  has: function (target, key) {
    console.log("has捕获器", key);
    return key in target;
  },
  set: function (target, key, value) {
    console.log("set捕获器", key);
    target[key] = value;
  },
  get: function (target, key) {
    console.log("get捕获器", key);
    return target[key];
  },
  deleteProperty: function (target, key) {
    console.log("delete捕获器");
    delete target[key];
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# Proxy所有捕获器

handler.getPrototypeOf() Object.getPrototypeOf 方法的捕捉器
handler.setPrototypeOf() Object.setPrototypeOf 方法的捕捉器。
handler.isExtensible() Object.isExtensible 方法的捕捉器(判断是否可以新增属性)
handler.preventExtensions() Object.preventExtensions 方法的捕捉器
handler.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor 方法的捕捉器
handler.defineProperty() Object.defineProperty 方法的捕捉器
handler.ownKeys() Object.getOwnPropertyNames 方法和
Object.getOwnPropertySymbols 方法的捕捉器
handler.has() in 操作符的捕捉器
handler.get() 属性读取操作的捕捉器

# Proxy的constructor和apply

捕获器中包含constructorapply,主要应用于函数对象

function foo() {
  console.log("foo函数被调用", this, arguments);
  return "foo";
}
const fooProxy = new Proxy(foo, {
  apply: function (target, thisArg, otherArgs) {
    console.log("函数的apply侦听");
    return target.apply(thisArg, otherArgs);
  },
  construct(target, argArray, newTarget) {
    console.log(target, argArray, newTarget);
    return new target();
  },
});
foo()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Reflect

Reflect是ES6新增的API,是一个对象

Reflect的作用

  • 提供了操作JavaScript对象的方法,类似于Object中操作对象的方式
  • Reflect.getPrototypeOf(target)类似于Object.getPropertyOf()
  • Reflect.defineProperty(target, propertyKey, attributes)类似于Object.defineProperty()

Reflect对比Object的优势,为什么需要新增Reflect

  • 在早期ECMA规范中未考虑到对对象本身的操作如何设计更规范,因此将这些操作放到了Object上面
  • 但是Object作为一个构造哈数,这些实际操作放到它身上并不合适
  • 另外包括类似于in,delete操作符,让JS看起来有些奇怪
  • 所以在ES6中新增了Reflect,让这些操作都集中在Reflect对象上
  • 在使用Proxy时,可以不操作原对象

# Reflect和Object的区别

const obj = {
  name: "zs",
  age: 20,
};
Object.defineProperty(obj, "name", {
  configurable: false,
});
// 1. 之前的方式删除属性
// delete obj.name; // 删除失败,并且在严格模式下报错
// if (obj.name) {
//   console.log("name删除失败");
// } else {
//   console.log("name删除成功");
// }
// Reflect删除属性  Reflect.deleteProperty返回一个布尔值
if(Reflect.deleteProperty(obj, 'name')){
  console.log("name删除成功");
} else {
  console.log("name删除失败");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Reflect使用对应的方法后会返回一个布尔值,可以更严谨的判断方法是否执行成功;而Object并不能精确的判断是否执行成功,还有可能会报错导致后续的代码不能继续执行

# Reflect常见的方法

Reflect.getPropertyOf(target) 类似于Object.getPropertyOf()
Reflect.setPrototypeOf(target, prototype) 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true
Reflect.isExtensible(target) 类似于 Object.isExtensible()
Reflect.preventExtensions(target) 类似于 Object.preventExtensions()。返回一个Boolean
Reflect.getOwnPropertyDescriptor(target, propertyKey) 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined
Reflect.defineProperty(target, propertyKey, attributes) 和 Object.defineProperty() 类似。如果设置成功就会返回 true
Reflect.ownKeys(target) 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于Object.keys(), 但不会受enumerable影响).
Reflect.has(target, propertyKey) 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同
Reflect.get(target, propertyKey[, receiver]) 获取对象身上某个属性的值,类似于 target[name]
Reflect.set(target, propertyKey, value[, receiver]) 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true
Reflect.deleteProperty(target, propertyKey) 作为函数的delete操作符,相当于执行 delete target[name]
Reflect.apply(target, thisArgument, argumentsList) 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和Function.prototype.apply() 功能类似
Reflect.construct(target, argumentsList[, newTarget]) 对构造函数进行 new 操作,相当于执行 new target(...args)

# Reflect使用

一般情况下Reflect是和Proxy一起使用来共同完成代理的

const objProxy = new Proxy(obj, {
  has: function (target, key) {
    return Reflect.has(target, key);
  },
  set: function (target, key, value) {
    // 优势一:代理对象的目的是不再直接操作原对象
    // 优势二:Reflect.set方法有返回Boolean值,可以判断本次操作是否成功
    // 优势三:receiver就是外层的Proxy对象
    //        Reflect.set/get最后一个参数可以决定对象访问器中getter/setter中的this指向
    // target[key] = value 这里是可操作的,但是还是直接操作原对象的形式
    const isSuccess = Reflect.set(target, key, value);
      if(!isSuccess){
          throw new Error(`set ${key} failure`)
      }
  },
  get: function (target, key) {
    return Reflect.get(target, key);
  },
  deleteProperty: function (target, key) {
    return Reflect.deleteProperty(target, key);
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Reflect操作优势

  • 使用代理对象不再直接操作原对象

# Receiver的作用

在使用getter,setter的时候有一个receiver函数。

作用:如果源对象中有setter,getter的访问属性,那么可以通过receiver来改变里面的this

const obj = {
  name: 'zs',
  age: 123
}
const objProxy = new Proxy(obj, {
  has: function (target, key) {
    return Reflect.has(target, key);
  },
  set: function (target, key, value, receiver) {
    console.log(receiver === objProxy);
    console.log("set捕获器", key);
    Reflect.set(target, key, value, receiver);
  },
  get: function (target, key, receiver) {
    console.log("get捕获器", key);
    return Reflect.get(target, key, receiver);
  },
  deleteProperty: function (target, key) {
    return Reflect.deleteProperty(target, key);
  },
});
objProxy.name = '133'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

receiver实际上就是代理对象

# Reflect的constructor

function Person(name, age) {
  this.name = name;
  this.age = age;
}
function Student() {}
const stu = Reflect.construct(Person, ["zs", 20], Student); // (执行的构造函数,参数,最终创建的类型)
console.log(stu.__proto__ === Student.prototype); // true
1
2
3
4
5
6
7
上次更新: 2022/12/09, 08:40:50
最近更新
01
防抖和节流
02-06
02
正则表达式
01-29
03
async_await函数
12-30
更多文章>