162、Proxy 和 Reflect

来源 http://caibaojian.com/es6/proxy.html
Proxy 同一个拦截器对象,可以设置拦截多个操作。
1、Proxy
var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    /*
    target:目标对象
    property:属性名
    receiver:操作行为所针对的对象,一般指proxy实例本身
    */
    if (key === ‘prototype‘) {
      return Object.prototype;
    }
    return ‘Hello, ‘ + key;
  },
  set: function (target, key, value, receiver) {
    /*
    target:目标对象
    propertyName:属性名
    propertyValue:属性值
    receiver:Proxy实例本身
    */
    console.log(`setting ${key}!`);
    return Reflect.set(target, key, value, receiver);
  },
  apply: function (target, key, receiver) {
    return receiver[0];
  },

  construct: function (target, key) {
    return { value: key[1] };
  }

});

obj.count;
obj.count = 1

下面是 Proxy 支持的拦截操作一览。

对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。

(1)get(target, propKey, receiver)

拦截对象属性的读取,比如proxy.foo和proxy[‘foo‘]。

最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。

(2)set(target, propKey, value, receiver)

拦截对象属性的设置,比如proxy.foo = v或proxy[‘foo‘] = v,返回一个布尔值。

(3)has(target, propKey)

拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。

(4)deleteProperty(target, propKey)

拦截delete proxy[propKey]的操作,返回一个布尔值。

(5)ownKeys(target)

拦截Object.getOwnPropertyNames(proxy) 、Object.getOwnPropertySymbols(proxy) 、Object.keys(proxy) ,返回一个数组。该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。

(6)getOwnPropertyDescriptor(target, propKey)

拦截Object.getOwnPropertyDescriptor(proxy, propKey) ,返回属性的描述对象。

(7)defineProperty(target, propKey, propDesc)

拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs) ,返回一个布尔值。

  (8)preventExtensions(target)

拦截Object.preventExtensions(proxy) ,返回一个布尔值。

  (9)getPrototypeOf(target)

拦截Object.getPrototypeOf(proxy) ,返回一个对象。

  (10)isExtensible(target)

拦截Object.isExtensible(proxy) ,返回一个布尔值。

  (11)setPrototypeOf(target, proto)

拦截Object.setPrototypeOf(proxy, proto) ,返回一个布尔值。

  如果目标对象是函数,那么还有两种额外操作可以拦截。

  (12)apply(target, object, args)

拦截 Proxy 实例作为函数调用的操作,比如proxy(...args) 、proxy.call(object, ...args) 、proxy.apply(...) 。

  (13)construct(target, args)

2、Reflect概述
Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个。

  (1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。

  (2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。

// 老写法
try {
  Object.defineProperty(target, property, attributes);
  // success
} catch (e) {
  // failure
}

// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}
(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。

// 老写法
‘assign‘ in Object // true

// 新写法
Reflect.has(Object, ‘assign‘) // true
(4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。

Proxy(target, {
  set: function (target, name, value, receiver) {
    var success = Reflect.set(target, name, value, receiver);
    if (success) {
      log(‘property ‘ + name + ‘ on ‘ + target + ‘ set to ‘ + value);
    }
    return success;
  }
});
上面代码中,Proxy方法拦截target对象的属性赋值行为。它采用Reflect.set方法将值赋值给对象的属性,然后再部署额外的功能。

下面是另一个例子。

var loggedObj = new Proxy(obj, {
  get(target, name) {
    console.log(‘get‘, target, name);
    return Reflect.get(target, name);
  },
  deleteProperty(target, name) {
    console.log(‘delete‘ + name);
    return Reflect.deleteProperty(target, name);
  },
  has(target, name) {
    console.log(‘has‘ + name);
    return Reflect.has(target, name);
  }
});
上面代码中,每一个Proxy对象的拦截操作(get、delete 、has),内部都调用对应的Reflect方法,保证原生行为能够正常执行。添加的工作,就是将每一个操作输出一行日志。

有了Reflect对象以后,很多操作会更易读。

// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1

// 新写法
Reflect.apply(Math.floor, undefined, [1.75]) // 1
Reflect对象的方法
Reflect对象的方法清单如下,共13个。

Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。下面是对其中几个方法的解释。

(1)Reflect.get(target, name, receiver)

查找并返回target对象的name属性,如果没有该属性,则返回undefined。

如果name属性部署了读取函数,则读取函数的this绑定receiver。

var obj = {
  get foo() { return this.bar(); },
  bar: function () { ... }
};

// 下面语句会让 this.bar()
// 变成调用 wrapper.bar()
Reflect.get(obj, "foo", wrapper);
(2)Reflect.set(target, name, value, receiver)

设置target对象的name属性等于value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver。

(3)Reflect.has(obj, name)

等同于name in obj。

(4)Reflect.deleteProperty(obj, name)

等同于delete obj[name]。

(5)Reflect.construct(target, args)

等同于new target(...args) ,这提供了一种不使用new,来调用构造函数的方法。

(6)Reflect.getPrototypeOf(obj)

读取对象的__proto__属性,对应Object.getPrototypeOf(obj) 。

(7)Reflect.setPrototypeOf(obj, newProto)

设置对象的__proto__属性,对应Object.setPrototypeOf(obj, newProto) 。

(8)Reflect.apply(fun, thisArg, args)

等同于Function.prototype.apply.call(fun, thisArg, args) 。一般来说,如果要绑定一个函数的this对象,可以这样写fn.apply(obj, args) ,但是如果函数定义了自己的apply方法,就只能写成Function.prototype.apply.call(fn, obj, args) ,采用Reflect对象可以简化这种操作。

另外,需要注意的是,Reflect.set() 、Reflect.defineProperty() 、Reflect.freeze() 、Reflect.seal()和Reflect.preventExtensions()返回一个布尔值,表示操作是否成功。它们对应的Object方法,失败时都会抛出错误。

// 失败时抛出错误
Object.defineProperty(obj, name, desc);
// 失败时返回false
Reflect.defineProperty(obj, name, desc);
上面代码中,Reflect.defineProperty方法的作用与Object.defineProperty是一样的,都是为对象定义一个属性。但是,Reflect.defineProperty方法失败时,不会抛出错误,只会返回false。

实例:使用 Proxy 实现观察者模式
观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。

const person = observable({
  name: ‘张三‘,
  age: 20
});

function print() {
  console.log(`${person.name}, ${person.age}`)
}

observe(print);
person.name = ‘李四‘;
// 输出
// 李四, 20
上面代码中,数据对象person是观察目标,函数print是观察者。一旦数据对象发生变化,print就会自动执行。

下面,使用 Proxy 写一个观察者模式的最简单实现,即实现observable和observe这两个函数。思路是observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数。

const queuedObservers = new Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, { set });

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  queuedObservers.forEach(observer => observer());
  return result;
}
上面代码中,先定义了一个Set集合,所有观察者函数都放进这个集合。然后,observable函数返回原始对象的代理,拦截赋值操作。拦截函数set之中,会自动执行所有观察者。

原文地址:https://www.cnblogs.com/gushixianqiancheng/p/11929936.html

时间: 2024-11-05 20:42:14

162、Proxy 和 Reflect的相关文章

Proxy和Reflect

原因 最近在写 unar.js(一个技术超越react angular vue)的mvvm库.通过研究proxy的可行性.故作以下研究 Proxy代理一个函数 var fn=function(){ console.log("fn") } var proxyFn=new Proxy(fn,{ apply:function(target,scope,args){ target() } }) proxyFn()//fn proxyFn.call(window)//fn proxyFn.app

ES6(Proxy 和 Reflect)

Proxy 和 Reflect 1.Proxy 和 Reflect 的概念 Proxy 意为 ‘代理’,连接了用户和真实对象之间的一个层 Reflect 意为‘反射’   反射的是Object 2.适用场景 一.Proxy 语法 1.类似于供应商的原始对象 obj ,通过 Proxy 新生成对象,这个对象是映射 Object 的,用户访问 monitor, 通过 Proxy ,再传递给 obj 对象. 2.设置最简单的代理操作(拦截读取作用) 真实的为 2017-03-11,通过‘代理’的作用,

利用ES6中的Proxy和Reflect 实现简单的双向数据绑定

利用ES6中的Proxy (代理) 和 Reflect 实现一个简单的双向数据绑定demo. 好像vue3也把 obj.defineProperty()  换成了Proxy+Reflect. 话不多说,直接上代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>proxy</title> </hea

ES6--Set、Map、Symbol、Proxy及Reflect

九.Set和Map数据结构 Set ES6提供了新的数据结构Set.它类似于数组,但是成员的值都是唯一的,没有重复的值.之前的博文曾阐述过使用ES5实现JavaScript数据结构-集合. new Set([iterable]); var items = new Set([1,2,3,4,5,5,5,5]); console.log(items.size); // 5 console.log(items); // Set {1, 2, 3, 4, 5} console.log([...items

ES6初识-Proxy和Reflect

{ let obj={ time:'2017-03-11', name:'net', _r:123 }; let monitor=new Proxy(obj,{ // 拦截对象属性的读取 get(target,key){ return target[key].replace('2017','2018') }, // 拦截对象设置属性 set(target,key,value){ if(key==='name'){ return target[key]=value; }else{ return t

es6之Proxy,Reflect

Proxy 可以理解成,在目标对象之前架设一层"拦截",外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写. var proxy = new Proxy(target, handler); new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为. 如果handler没有设置任何拦截,那就等同于直接通向原对象. var target = {}; var handl

es6——Proxy和Reflect

Proxy代理,Reflect反射 Proxy对属性的读取 { //供应商,原始对象 let obj={ time:'2017-1-1', name:'net', _r:123 } //代理商,新生成一个Proxy对象,将要代理的对象放进去,后面是实现代理的方法 let monitor=new Proxy(obj,{ //拦截对象属性的读取 get(target,key){ return target[key].replace('2017','2016')//将读取到的2017全部替换成2018

ES6(十一)Proxy和Reflect

Proxy let obj = { time: '2019-01-01', name: 'ronle' } let monitor = new Proxy(obj, { // 拦截对象属性的读取 get (target, key) { return target[key].replace('2019', '2020') }, // 拦截对象设置属性 set (target, key, value) { // 只有key等于name才修改 if (key === 'name') { return

Proxy/Reflect

Proxy:像拦截器,对目标对象修改等进行拦截,是一种元编程(meta programming),即修改JS语言本身. //生成proxy实例,两个参数都是对象,targetObj是要拦截的目标对象,handlerObj参数用来定制拦截行为 var proxy = new Proxy(targetObj, handlerObj) Reflect:将Object对象一些明显的语言内部的方法,放到Reflect对象上. 修改某些Object的方法使返回更合理 1.规范语言 2.是Proxy对象的修改