概述
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