欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第十二章 Proxy

程序员文章站 2022-07-04 09:07:23
...

概述

  Proxy用于修改某些操作的默认行为,等同于在语言层面作出修改,所以属于一种元编程,即对编程语言进行编程。
  Proxy可以理解成在目标对象前架设一个"拦截"层,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写,主要用于数组、对象、方法调用自身方法时改变它们的默认行为,每一种方法都有对应的拦截方法。
  ES6原生提供Proxy构造函数,用于生成Proxy实例。

let proxy = new Proxy(target, handler)

  Proxy对象的所有用法都是上面这种形式,不同的只是handler参数的写法,其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
  handler如果是空对象,表示没有拦截,访问handler就等同于访问target。

Proxy实例的方法

get()

get方法用于拦截某个属性的读取操作。

let person = {
    name: '张三'
}
let proxy = new Proxy(person, {
    get (target, property) {
         if (property in target) {
             return target[property]
         } else {
              throw new Error('属性不存在')
         }
     }
})
proxy.name      // '张三'
proxy.age       // '属性不存在'
set()

set方法用于拦截某个属性的赋值操作。

let validator = {
    set (obj, prop, value) {
        if (prop === 'age') {
            if (!Number.isInteger(value)) {
                throw new Error('不是整数')
            }
            if (value > 200) {
                throw new Error('值不能大于200')
            }
        }
        obj[prop] = value
    }
}
let person = new Proxy({}, validator)
person.age = 100
person.age  // 100
person.age = 'young'    // 不是整数
person.age = 300   // 值不能大于200
apply()

apply方法拦截函数的调用、call和apply操作
apply方法可以接受3个参数,分别是目标对象、目标对象的上下文和目标对象的参数数组。

let twice = {
    apply (target, ctx, args) {
       return Reflict.apply(...arguments) * 2
   }
}
function sum (left, right) {
    return left + right
}
let proxy = new Proxy(sum, twice)
proxy(1, 2)    // 6
proxy.call(null, 5, 6)   // 22
proxy.apply(null, [7, 8])  // 30
Reflict.apply(proxy, null, [9, 10])   // 38

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

has()

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

let stu1 = {name: '张三', score: 59}
let stu2 = {name: '李四', score: 99}
let handler = {
    has (target, prop) {
        if (prop === 'score' && target[prop] < 60) {
            console.log(`${target.name}不及格`)
            return false
        }
        return prop in target
    }
}
let oproxy1 = new Proxy(stu1, handler)
let oproxy2 = new Proxy(stu2, handler)
'score' in oproxy1
// 张三不及格
// false

'score' in oproxy2
// true
for (let a in oproxy1) {
    console.log(oproxy1[a])
}
// 张三  59

for (let b in oproxy2) {
    console.log(oproxy2[b])
}
// 李四   99

上面代码中,has拦截只对in循环有效,对for...in循环不生效。

construct()

construct方法用于拦截new命令,下面是拦截对象的写法。这个方法接收两个参数:

  • target:目标对象
  • args:构建函数的参数对象
let P = new Proxy(function () {}, {
    construct: function (target, args) {
        console.log('called:+args.join(',')')
         return {value: args[0] * 10}
      }
   }
)
(new p(1)).value
// 'called: 1'
// 10