这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战
最近因为机缘巧合之下,发现了这个早有耳闻的JS原生对象,于是特意花时间去进行了一番了解。
介绍
首先,Proxy是一个用于创建一个指定对象的代理,从而实现针对这个对象进行拦截或重新定义基本操作。使用方式如下:
const proxy = new Proxy(target, handler)
其中,proxy是代理;target是一个需要被代理的对象;handler是一个对象,里面包含定义代理行为逻辑的一系列函数。
Target对象
target 是对象类型,可以是普通对象(甚至是空对象)、数组以及函数等。
Handler的处理函数
开始之前需要说明一点是,这些处理函数都不是必须要实现的,不实现的将维持默认处理的方式。
先说2个最常用的:handler.get()和handler.set()。正如字面意思,就是劫持proxy的属性的get和set操作。
var foo = {
b: 'lol'
}
var handler = {
set(obj, prop, val) {
console.log(`set foo.${prop} = ${val}`)
if (/^p\_/.test(prop) || (prop in obj)) {
obj[prop] = val
} else {
console.log(`failed to set prop '${prop}' to object 'foo'`)
}
},
get(obj, prop) {
console.log(`get foo.${prop}`)
if (prop in obj) {
return obj[prop]
} else {
console.log(`prop '${prop}' is undefined in 'foo' and illegal to read`)
}
}
}
var proxy = new Proxy(foo, handler);
proxy.a = 1
// output: set foo.a = 1
// output: failed to set prop 'a' to object 'foo'
proxy.b = 'Zzz'
// output: set foo.b = Zzz
proxy.p_a = 100
// output: set foo.p_a = 100
foo.p_a = 200
console.log(proxy.p_a)
// output: get foo.p_a
console.log(foo.a, foo.b, foo.p_a)
// output: undefined "Zzz" 200
console.log(proxy.c)
// output: get foo.c
// output: prop 'c' is undefined in 'foo' and illegal to read
一些相对少用的:
handler.apply(func, thisArg, argumentsList)
劫持函数执行操作
// 代码例子
function sum(a, b) {
return a + b
}
const handler = {
apply: function(func, thisArg, argumentsList) {
console.log(`sum: ${argumentsList}`)
let sum = 0
for (let i = 0; i < argumentsList.length; i++) {
sum = func(sum, argumentsList[i])
}
return sum
}
}
const proxy = new Proxy(sum, handler)
console.log(sum(1, 2))
// output: 3
console.log(proxy(1, 2, 3, 10))
// output: 16
handler.construct(func, argumentsList)
劫持new操作符的执行
function Person(a, b) {
this.name = a
this.age = b
}
const handler = {
id_count: 12340,
construct: function(func, argumentsList) {
console.log(`args: ${argumentsList}`)
const ppl = new func(...argumentsList)
ppl.id = this.id_count++
return ppl
}
}
const proxy = new Proxy(Person, handler)
console.log(new Person('Jack', 2))
// output: {name: "Jack", age: 2}
console.log(new proxy('Ben', 10))
// output: {name: "Ben", age: 10, id: 12340}
console.log(new proxy('David', 8))
// output: {name: "David", age: 8, id: 12341}
handler.defineProperty(obj, prop, descriptor)
劫持属性赋值操作
// 代码例子
var foo = {
b: 'lol'
}
var handler = {
defineProperty(obj, prop, descriptor) {
console.log(`define foo.${prop} = ${descriptor.value}`)
obj[prop] = descriptor.value
return true
}
}
var proxy = new Proxy(foo, handler);
proxy.a = 1
// output: define foo.a = 1
proxy.b = 'Zzz'
// output: define foo.b = Zzz
Object.defineProperty(proxy, 'num', {
value: 666
})
// output: define foo.num = 666
handler.deleteProperty(obj, prop)
劫持delete操作符的操作
// 代码例子
var foo = {
b: 'lol'
}
var handler = {
deleteProperty(obj, prop) {
console.log(`delete foo.${prop}`)
delete foo[prop]
return true
}
}
var proxy = new Proxy(foo, handler);
delete proxy.b
// output: delete foo.b
console.log(foo)
// output: {}
handler.getOwnPropertyDescriptor(obj, prop)
劫持Object.getOwnPropertyDescriptor()方法的执行
// 代码例子
var foo = {
b: 'lol'
}
var handler = {
getOwnPropertyDescriptor(obj, prop) {
console.log(`getProp foo.${prop}`)
if (prop in obj) {
return Object.getOwnPropertyDescriptor(obj, prop)
} else {
return { configurable: true, enumerable: false, value: ''}
}
}
}
var proxy = new Proxy(foo, handler);
console.log(Object.getOwnPropertyDescriptor(proxy, 'a'))
// output: getProp foo.a
// output: {value: "", writable: false, enumerable: false, configurable: true}
console.log(Object.getOwnPropertyDescriptor(proxy, 'b'))
// output: getProp foo.b
// output: {value: "lol", writable: true, enumerable: true, configurable: true}
handler.getPrototypeOf(obj)
劫持Object.getPrototypeOf()方法的执行
// 代码例子
var foo = {
b: 'lol'
}
var fooProto = {
b: 'Zzz'
}
var handler = {
getPrototypeOf(obj) {
console.log(`getProto foo`)
return fooProto
}
}
var proxy = new Proxy(foo, handler);
console.log(Object.getPrototypeOf(proxy))
// output: getProto foo
// output: {b: "Zzz"}
handler.setPrototypeOf(obj, proto)
劫持Object.setPrototypeOf()方法的执行
// 代码例子
var foo = {
}
var fooProto = {
b: 'Zzz'
}
var handler = {
setPrototypeOf(obj, proto) {
console.log(`setProto foo`)
Object.setPrototypeOf(obj, proto)
obj.hasProto = true
return true
}
}
var proxy = new Proxy(foo, handler);
Object.setPrototypeOf(proxy, fooProto)
// output: setProto foo
console.log(foo)
// output: {hasProto: true}
console.log(foo.b)
// output: "Zzz"
handler.has(obj, prop)
劫持in操作符的执行
// 代码例子
var foo = {
__a: 1,
b: 'lol'
}
var handler = {
has(obj, prop) {
console.log(`has foo.${prop}`)
if (/^__/.test(prop)) {
return false;
} else {
return prop in obj
}
}
}
var proxy = new Proxy(foo, handler);
console.log('__a' in proxy)
// output: has foo.__a
// output: false
console.log('b' in proxy)
// output: has foo.b
// output: true
console.log('c' in proxy)
// output: has foo.c
// output: false
handler.isExtensible(obj) 和 handler.preventExtensions(obj)
劫持Object.isExtensible()和Object.preventExtensions()方法的执行
// 代码例子
var foo = {
canExtend: true,
b: 'lol'
}
var handler = {
isExtensible(obj) {
console.log(`isExtensible foo`)
return Object.isExtensible(obj)
},
preventExtensions(obj) {
console.log(`preventExtensions foo`)
obj.canExtend = false
return Object.preventExtensions(obj)
}
}
var proxy = new Proxy(foo, handler);
console.log(Object.isExtensible(proxy))
// output: isExtensible foo
// output: true
console.log(proxy.canExtend)
// output: true
Object.preventExtensions(proxy)
// output: preventExtensions foo
console.log(Object.isExtensible(proxy))
// output: isExtensible foo
// output: false
console.log(proxy.canExtend)
// output: false
实践
从上面的介绍可以看出,Proxy对象能做很多以前无法想象的事情。
限制&校验
把目标对象封装隐藏起来,对操作行为进行监听和限制,也可以对属性处理进行校验,判断操作、赋值数据和类型是否合法。
例如qiankun微前端框架在构建JS沙箱就利用Proxy
拓展
针对原来的目标对象,进行额外的拓展处理,例如构建出有更多初始数据和方法的对象、在方法调用时赋予更强大的功能等。