ES6学习笔记(十)代理器Proxy

综合频道 2020-03-26167未知admin

  Java可以使用面向切面(AOP)的方法来实现某些的操作,比如某个操作的前置通知,后置通知等等,这种操作非常方便,其本质便是,JS的代理Proxy代理该如何使用呢?

  Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

  Proxy 可以理解成,在目标对象之前架设一层“拦截”,对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

  代码定义了一个空对象{ }的代理对象obj,在代理对象上重定义了属性的读取(get)和设置(set)行为。这里暂时先不解释具体的语法,只看运行结果。对设置了拦截行为的对象obj,去读写它的属性,就会触发定义的拦截操作。

  代码说明,Proxy实际上重载(overload)了点运算符(...),即用自己的定义覆盖了语言的原始定义。

  Proxy 对象的所有用法,都是这种形式,不同的只是handler参数的写法。

  其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

  代码中,作为构造函数,Proxy接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy的介入,操作原来要访问的就是这个对象;

  第二个参数是一个配置对象(不是回调函数),对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。

  代码中,配置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35,所以访问任何属性都得到35。

  注意,要使得Proxy起作用,必须针对Proxy实例(上例是proxy对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。

  代码中,handler是一个空对象,没有任何拦截效果,访问proxy就等同于访问target。

  一个技巧是将 Proxy 对象,设置到object.proxy属性,从而可以在object对象上调用。

  代码中,proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截。

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

  get()方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。

  代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。

  代码中,get的拦截操作定义在原型对象上,所以调用实例继承了proxy的obj实例时,拦截生效。

  是一个自定义的数组构造函数,获取值时,数组的参数是-2,就会输出数组的倒数第二个。

  下面是一个get方法的第三个参数的例子,它总是指向原始的读操作所在的那个对象,一般情况下就是 Proxy 实例。

  代码中,d对象本身没有a属性,所以读取d.a的时候,会去d的原型proxy对象找。这时,receiver就指向d,代表原始的读操作所在的那个对象。

  如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错。

  set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和Proxy 实例本身,其中最后一个参数可选。

  代码中,由于设置了存值函数set,任何不符合要求的age属性赋值,都会抛出一个错误,这是数据验证的一种实现方法。利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新 DOM。

  有时,我们会在对象设置内部属性,属性名的第一个字符使用下划线开头,表示这些属性不应该被外部使用。结合get和set方法,就可以做到防止这些内部属性被外部读写。

  代码中,只要读写的属性名的第一个字符是下划线,一律抛错,从而达到读写内部属性的目的。

  为了达到一个类似Java的private效果,不,这已经是finally效果了,可谓煞费苦心,这便是JS在封装上的短板吧。

  到目前为止发现,target对象 可以是某个对象,对象原型或者方法,而handler对象常用的几个代理方法却是不变的,所以代理的核心思想应该是需要改变的,做的处理过程---handler,并且这个处理对象是可以被重用的。

  set方法的第四个参数receiver,指的是原始的操作行为所在的那个对象,一般情况下是proxy实例本身;

  代码中,设置myObj.foo属性的值时,myObj并没有foo属性,因此引擎会到myObj的原型链去找foo属性。myObj的原型对象proxy是一个 Proxy 实例,设置它的foo属性会触发set方法。这时,第四个参数receiver就指向原始赋值行为所在的对象myObj。

  注意,如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用。

  代码中,严格模式下,set代理返回lse或者undefined,都会报错。

  apply方法拦截函数的调用、call和apply操作。按照来说,这个拦截操作才是重点。

  apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

  发现apply的三个参数:目标对象,目标对象的上下文this,参数args没有被使用,他们应该在需要使用的地方才用。

  代码中,每当执行proxy函数(直接调用或call和apply调用),就会被apply方法拦截。

  has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方生效。典型的操作就是in运算符。这个拦截应该不常用。

  代码中,如果原对象的属性名的第一个字符是下划线,proxy.has就会返回lse,从而不会被in运算符发现。

  注意:如果原对象不可配置或者扩展,使用has拦截就会报错,也就是说,如果某个属性不可配置(或者目标对象不可扩展),则has方法就不得“隐藏”(即返回lse)目标对象的该属性。

  注意:has方法拦截的是HasProperty操作,而不是HasOwnProperty操作,即has方法不判断一个属性是对象自身的属性,还是继承的属性。

  另外,虽然for...in循环也用到了in运算符,但是has拦截对for...in循环不生效。

  construct方法表示构造函数被调用,用于拦截new命令,下面是拦截对象的写法。

  construct方法返回的必须是一个对象,否则会报错,因为是构造函数。

  deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回lse,当前属性就无法被delete命令删除。

  代码中,deleteProperty方法拦截了delete操作符,删除第一个字符为下划线的属性会报错。

  注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错。

  getPrototypeOf方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。

  ownKeys方法用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。

  Proxy.revocable方法返回一个可取消的 Proxy 实例。用于一次性的代理对象。

  Proxy.revocable方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。代码中,当执行revoke函数之后,再访问Proxy实例,就会抛出一个错误。

  Proxy.revocable的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。

  虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。

  此外,有些原生对象的内部属性,只有通过正确的this才能拿到,所以 Proxy 也无法代理这些原生对象的属性。

  代码中,getDate方法只能在Date对象实例拿到,如果this不是Date对象实例就会报错。这时,this绑定原始对象,就可以解决这个问题。

  Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端。

  代码新建了一个 Web 服务的接口,这个接口返回各种数据。Proxy 可以拦截这个对象的任意属性,所以不用为每一种数据写一个适配方法,只要写一个 Proxy 拦截就可以了。这便是AOP的思想。

原文标题:ES6学习笔记(十)代理器Proxy 网址:http://www.allofmystyle.com/zonghepindao/2020/0326/39971.html

Copyright © 2002-2020 快言快语新闻网 www.allofmystyle.com 版权所有  

联系QQ:1352848661