关于Proxy代理,最喜欢阮一峰大佬的描述:
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
嗯,说的相当明白了,下面来看几个使用场景
let obj = {
user: 'admin',
password: '123456'
}
let _obj = obj
_obj.password = '******'
for (let [key] of Object.entries(_obj)) {
Object.defineProperty(_obj, key, {
writable: false
})
}
_obj.user = 'Admin' // 由于数据设置了不可写,此步无效
console.log(_obj.user, _obj.password) // admin ******
let obj = {
user: 'admin',
password: '123456'
}
let pro = new Proxy(obj, {
get (target, key) {
if (key === 'password') {
return '******'
} else {
return target[key] || ''
}
},
set (target, key, value) {
return false
}
})
pro.user = 'Admin' // 由于数据通过代理设置了保护因此无法修改,此步无效
console.log(pro.user, pro.password) // admin ******
// 监听错误
window.addEventListener('error', (e) => {
console.log(e.message)
// report('./')
}, true)
let obj2 = {
user: 'admin',
password: '123456'
}
let getter = (target, key) => {
if (key === 'password') {
return '******'
} else {
return target[key] || ''
}
}
let setter = (target, key, value) => {
if (Reflect.has(target, key)) {
if (key === 'password') {
if (value || value.length > 6) {
throw new TypeError('密码超出6位,无效')
// console.log('密码超出6位,无效')
// return false
}
} else {
target[key] = value
}
} else {
console.log('无此属性,无效')
return false
}
}
let pro2 = new Proxy(obj2, {
get: getter,
set: setter
})
pro2.user = 'Admin' // 有效操作
pro2.password = '1234567' // 密码超出6位,无效
pro2.age = 18 // 无此属性,无效
console.log(pro2.user, pro2.password) // Admin ******
class User {
constructor () {
this.proxy = new Proxy({
id: Math.random().toString(36).slice(-8) // 转为36进制并截取后八位
}, {})
}
get id () {
return this.proxy.id
}
}
let user1 = new User()
let user2 = new User()
for (let i = 0; i < 6; i++) {
console.log(user1.id, user2.id)
}
user1.id = 'abc'
console.log(user1.id, user2.id) // user1的id没有变,说明只读
可撤销的代理和普通代理不同,代理数据存放于它的proxy属性中,通过它的另一个属性revoke可执行代理撤销,其它使用方式不变
let obj = {
user: 'admin',
password: '123456'
}
// 可撤销的代理
let pro = Proxy.revocable(obj, {
get (target, key) {
if (key === 'password') {
return '******'
} else {
return target[key] || ''
}
},
set (target, key, value) {
return false
}
})
console.log(pro)
console.log(pro.proxy.user, pro.proxy.password) // admin ******
setTimeout(() => {
pro.revoke()
setTimeout(() => {
console.log(pro.proxy.user) // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
}, 100)
}, 1000)
Proxy 支持的拦截操作一览,一共 13 种:
- get(target, propKey, receiver):拦截对象属性的读取。
- set(target, propKey, value, receiver):拦截对象属性的设置。
- has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
- deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
- ownKeys(target):拦截Object.getOwnPropertyNames、Object.getOwnPropertySymbols、Object.keys、for…in循环,返回一个数组。
- getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor,返回属性的描述对象。
- defineProperty(target, propKey, propDesc):拦截Object.defineProperty、Object.defineProperties,返回一个布尔值。
- preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
- getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
- isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
- setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
- apply(target, object, args):拦截 Proxy 实例作为函数调用的操作。
- construct(target, args):拦截 Proxy 实例作为构造函数调用的操作。
拓展: