Proxy
在 ES6 标准中新增的一个非常强大的功能是 Proxy,它可以自定义一些常用行为如查找、赋值、枚举、函数调用等。通过 Proxy 这个名称也可以看出来它包含了“代理”的含义,只要有“代理”的诉求都可以考虑使用 Proxy 来实现。
基本语法
语法
let p = new Proxy(target, handler)
解释
MDN 给出的解释偏官方,通俗的讲第一个参数 target 就是用来代理的“对象”,被代理之后它是不能直接被访问的,而 handler 就是实现代理的过程。
常用拦截操作
get
拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]。
let arr = [7, 8, 9]
arr = new Proxy(arr, {
get(target, prop) {
// console.log(target, prop)
return prop in target ? target[prop] : 'error'
}
})
console.log(arr[1])
console.log(arr[10])
let dict = {
'hello': '你好',
'world': '世界'
}
dict = new Proxy(dict, {
get(target, prop) {
return prop in target ? target[prop] : prop
}
})
console.log(dict['world'])
console.log(dict['imooc'])
set
拦截对象属性的设置,比如proxy.foo = v或proxy[‘foo’] = v,返回一个布尔值。
let arr = []
arr = new Proxy(arr, {
set(target, prop, val) {
if (typeof val === 'number') {
target[prop] = val
return true
} else {
return false
}
}
})
arr.push(5)
arr.push(6)
console.log(arr[0], arr[1], arr.length)
has
拦截propKey in proxy的操作,返回一个布尔值。
let range = {
start: 1,
end: 5
}
range = new Proxy(range, {
has(target, prop) {
return prop >= target.start && prop <= target.end
}
})
console.log(2 in range)
console.log(9 in range)
ownKeys
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
let obj = {
name: 'imooc',
[Symbol('es')]: 'es6'
}
console.log(Object.getOwnPropertyNames(obj))
console.log(Object.getOwnPropertySymbols(obj))
console.log(Object.keys(obj))
for (let key in obj) {
console.log(key)
}
let userinfo = {
username: 'xiecheng',
age: 34,
_password: '***'
}
userinfo = new Proxy(userinfo, {
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
// for (let key in userinfo) {
// console.log(key)
// }
console.log(Object.keys(userinfo))
deleteProperty
拦截delete proxy[propKey]的操作,返回一个布尔值。
let user = {
name: 'xiecheng',
age: 34,
_password: '***'
}
user = new Proxy(user, {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error('不可访问')
} else {
return target[prop]
}
},
set(target, prop, val) {
if (prop.startsWith('_')) {
throw new Error('不可访问')
} else {
target[prop] = val
return true
}
},
deleteProperty(target, prop) { // 拦截删除
if (prop.startsWith('_')) {
throw new Error('不可删除')
} else {
delete target[prop]
return true
}
},
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
console.log(user.age)
console.log(user._password)
user.age = 18
console.log(user.age)
try {
user._password = 'xxx'
} catch (e) {
console.log(e.message)
}
try {
// delete user.age
delete user._password
} catch (e) {
console.log(e.message)
}
console.log(user.age)
for (let key in user) {
console.log(key)
}
apply
拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。
let sum = (...args) => {
let num = 0
args.forEach(item => {
num += item
})
return num
}
sum = new Proxy(sum, {
apply(target, ctx, args) {
return target(...args) * 2
}
})
console.log(sum(1, 2))
console.log(sum.call(null, 1, 2, 3))
console.log(sum.apply(null, [1, 2, 3]))
construct
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。
let User = class {
constructor(name) {
this.name = name
}
}
User = new Proxy(User, {
construct(target, args, newTarget) {
console.log('construct')
return new target(...args)
}
})
console.log(new User('imooc'))