概述
??Proxy用于修改某些操作的默認(rèn)行為,等同于在語言層面作出修改,所以屬于一種元編程,即對編程語言進(jìn)行編程。
??Proxy可以理解成在目標(biāo)對象前架設(shè)一個(gè)"攔截"層,外界對該對象的訪問都必須先通過這層攔截,因此提供了一種機(jī)制可以對外界的訪問進(jìn)行過濾和改寫,主要用于數(shù)組、對象、方法調(diào)用自身方法時(shí)改變它們的默認(rèn)行為,每一種方法都有對應(yīng)的攔截方法。
??ES6原生提供Proxy構(gòu)造函數(shù),用于生成Proxy實(shí)例。
let proxy = new Proxy(target, handler)
??Proxy對象的所有用法都是上面這種形式,不同的只是handler參數(shù)的寫法,其中,new Proxy()表示生成一個(gè)Proxy實(shí)例,target參數(shù)表示所要攔截的目標(biāo)對象,handler參數(shù)也是一個(gè)對象,用來定制攔截行為。
??handler如果是空對象,表示沒有攔截,訪問handler就等同于訪問target。
Proxy實(shí)例的方法
get()
get方法用于攔截某個(gè)屬性的讀取操作。
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方法用于攔截某個(gè)屬性的賦值操作。
let validator = {
set (obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new Error('不是整數(shù)')
}
if (value > 200) {
throw new Error('值不能大于200')
}
}
obj[prop] = value
}
}
let person = new Proxy({}, validator)
person.age = 100
person.age // 100
person.age = 'young' // 不是整數(shù)
person.age = 300 // 值不能大于200
apply()
apply方法攔截函數(shù)的調(diào)用、call和apply操作
apply方法可以接受3個(gè)參數(shù),分別是目標(biāo)對象、目標(biāo)對象的上下文和目標(biāo)對象的參數(shù)數(shù)組。
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
上面的代碼中,每當(dāng)執(zhí)行proxy函數(shù)(直接調(diào)用或call和apply調(diào)用)就會被apply方法攔截,直接調(diào)用Reflict.apply方法也會被攔截。
has()
has方法用來攔截HasProperty操作,即判斷對象是否具有某個(gè)屬性時(shí),這個(gè)方法會生效。典型的操作就是in運(yùn)算符。
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循環(huán)有效,對for...in循環(huán)不生效。
construct()
construct方法用于攔截new命令,下面是攔截對象的寫法。這個(gè)方法接收兩個(gè)參數(shù):
- target:目標(biāo)對象
- args:構(gòu)建函數(shù)的參數(shù)對象
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