再读ES6 之 Proxy代理器

1 篇文章 0 订阅

         Proxy,可以理解为在对目标对象操作之前,进行一次拦截,外界对于该对象的操作,必须经过Proxy,为目标对象提供了一种类似于“保护”的机制,Proxy可以对外界的操作可以进行过滤和修改。

const proxyObj = new Proxy({},{
    get:function(target, propKey){
        console.log('这里是获取值时候的拦截')
        return target[propKey]
    },
    set:function(target,propKey,value){
        console.log('这里是设置的时候的拦截')
        targe[propKey] = value;
        return target
    }
})

          Proxy 接受两个参数,第一个参数是所代理的对象,就是原本要被操作的对象;第二个参数为配置对象,其中有13种对应的操作拦截。

          这里补充一下,我们创建对象实例的时候,new的是代理之后的对象,在上面的例子中就是proxyObj(原对象的参数),参数还是赋值的原对象(目标对象)的属性。那么这个实例的方法与属性和哪个对象有关呢?记住了,是原对象(代理的目标对象)的方法和属性。和这13种拦截操作没有任何的关系。

以下是13种拦截操作:(看以下操作的时候,需要你提前了解一下JavaScript的面向对象编程,根据我看过的小红书以及相关的ES6中class语法糖,写了一篇相关的文章)

  • get(target, propKey, receiver):拦截对象属性的读取。
  • set(target, propKey, value, receiver):拦截对象属性的设置,返回一个布尔值。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。

 

get(target,propKey[,receiver])

     拦截读取的操作,接受三个参数,依次是目标对象,属性名和proxy本身。

// 拦截读取操作
let Person = {
    name:'张三',
    age:26
}

let personProxy = new Proxy(Person,{
    get:(target,propKey)=>{
        if(propKey in target){
            return target[propKey]    
        }else{
            return new ReferenceError("属性 "+propKey+" 不存在!")
        }
    }
})

personProxy.sex  //ReferenceError: 属性 sex 不存在!
personProxy.name  // 张三

set(target, propKey, value[, receiver])

     set方法用来拦截属性的赋值操作,接收四个参数,依次是目标对象、属性名、属性值和Proxy实例本身。

//这段代码实现了对数据的校验
let validator = {
  set: function (target, propKey, value) {
    if(propKey in target && propKey[0] === '_'){
      throw new RangeError('该属性不允许修改!')
    }
    if (propKey === 'age') {
      if (!Number.isInteger(value)) {
        throw new RangeError('请输入整数!')
      }
      if(value > 200 || value < 1){
        throw new RangeError('请输入合理的年龄!')
      }
      target[propKey] = value
    }
  }
}

let person = new Proxy({_name:'这里是'},validator);

let person = new Proxy({},validator);

person.age = 201

person.age = -10  // Uncaught RangeError: 请输入合理的年龄! 

person.age = 1.23  // Uncaught RangeError: 请输入整数!

person._name = 'asd' // Uncaught RangeError: 该属性不允许修改!

 vue3.0上重构的双向绑定就是通过proxy实现的。

 需要注意的是,proxy代理器只是在原本操作的对象外面再次包装了一层,对于原本的目标对象属性中不可以修改的,set是不会 起作用的。

apply(target, object, args)

主要拦截的是函数的调用、call和apply操作。接收三个参数,依次是目标对象、目标对象的上下文和目标对象参数数组。

let handle = {
  apply:(target,obj,args)=>{
    return args[0] - args[1]
  }
}
let fn = function(x,y){return x+y}
let proxyFn = new Proxy(fn,handle);
proxyFn(1,2)
proxyFn(10,2)
proxyFn.call(null,10,1)
proxyFn.apply(null,[10,1])

has(target, propKey)

has 方法主要是拦截HasPrototype操作,判断某个属性是否属于对象。典型操作就是 in 操作符。

has 方法主要接收两个参数,分别是目标对象、需要查询的属性名。

需要注意的地方就是has方法拦截的是HasPrototype操作,而不是HasOwnPrototype操作,所以has()方法不判断属性是对象自身的,还是继承下来的。同事需要注意的地方还有has 方法对于 for...in 循环不生效。

写个小例子看一下:

let person1 = { name:'Tom',age:20,skill:'cook'}
let person2 = { name:'李梅',age:10}
let proxyHandler = {
  has:(target,propKey)=>{
    if(propKey == 'skill' && propKey in target){
      if(['cook'].includes(target[propKey])){
        console.log('这是个有技能的人,而且是厨房里面的技能')
        return true
      }else{
        console.log('这是个有技能的人,但不是厨房里面的技能')
        return true
      }
    }else{
      console.log('这是个没有技能的人')
      return false
    }
  }
}
let proxyPerson1 = new Proxy(person1,proxyHandler)
let proxyPerson2 = new Proxy(person2,proxyHandler)

console.log('skill' in proxyPerson1)   // 这是个有技能的人,而且是厨房里面的技能  true
console.log('skill' in proxyPerson2)   // 这是个没有技能的人  false

construct(target, args)

        从名字上看,应该就猜出来是和构造函数有关的,和构造函数有关的操作符那就只是new了。没错,它就是用来拦截new操作符。它可以接收两个参数:依次是目标对象、构造函数的参数数组。

        需要注意的是:1、construct方法必须返回一个对象,否则会报错。2、construct方法中的this指向的是proxy第二个参数本身。

const proxy = new Proxy(function () {}, {
  construct: function(target, args) {
    return { argsToxx:args.map(e=>{return e**2})}
  }
});

console.log((new proxy(1,2,3,4,5,6)).argsToxx)   //[ 1, 4, 9, 16, 25, 36 ]

deleteProperty(target, propKey)

      很明显这是delete操作符的,返回false,则propKey这个属性无法被删除。例子不写了,很简单。

ownKeys(target)

      从字面意思来看是属性自身的一些属性,猜想可能是拦截读取部分属性的时操作符。这个确实是拦截自身属性的读取操作:主要是以下四个操作:

  1. Object.getOwnPropertyNames()
  2. Object.getOwnPropertySymbols()
  3. Object.keys()
  4. for...in操作

     注意点:1、使用Objetc.keys()方法的时候,有三类属性会被自动过滤(a. 目标对象不存在的属性;b. 属性名为Symbol值;c. 不可遍历(enumberable为false)的属性)。

getOwnPropertyDescriptor(target, propKey)

        getOwnPropertyDescriptor()方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。 

defineProperty(target, propKey, propDesc)

        defineProperty()方法拦截了Object.defineProperty()操作。

getPrototypeOf(target)

       主要拦截的是获取对象原型。主要是以下操作:

  1. Object.prototype.__proto__
  2. Object.prototype.isPrototypeOf()
  3. Object.getPrototypeOf()
  4. Reflect.getPrototypeOf()
  5. instanceof

isExtensible(target)

       这个方法主要拦截的是Object.isExtensible()方法。

setPrototypeOf(target, proto)

       主要用来拦截Object.setPrototypeOf()方法。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值