一、ECMAScript-es6-Proxy
1. Proxy-代理
-
什么代理
在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写.
-
构造代理
// ES5的提供的代理方式 var obj = {} var newVal = '' Object.defineProperty(obj, 'name', { get(){ return newVal }, set(val){ newVal = val } }) obj.name = 'es' console.log(obj.name) // ES6 let proxy = new Proxy(target, handler); // new Proxy()表示生成一个Proxy实例 // target参数表示所要拦截的目标对象 // handler参数也是一个对象,用来定制拦截行为。如果handler没有设置任何拦截,那就等同于直接通向原对象。
2. 常用的拦截方法
拦截器 | 作用 |
---|---|
get | 拦截对象属性的读取,比如proxy.foo和proxy[‘foo’] |
set | 拦截对象属性的设置,返回一个布尔值,比如proxy.foo = v 或 proxy[‘foo’] = v |
has | 拦截propKey in proxy 的操作,返回一个布尔值 |
ownKeys | 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for …in循环,返回一个数组。 |
deleteProperty | 拦截delete proxy[propKey]的操作,返回一个布尔值 |
apply | 拦截函数的调用、call和apply操作 |
construct | 拦截new命令,返回一个对象 |
// get(target, propKey, receiver)
// get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
// 数组下标是否有对应的值,有则返回
let arr = [7, 8, 9]
arr = new Proxy(arr, {
get(target, prop, receiver) {
console.log(target, prop, receiver)
return prop in target? target[prop]: 'error'
}
})
console.log(arr[1]) // 8
console.log(arr[10]) // error
// set(target, propKey, value, receiver)
// set方法用来拦截某个属性的赋值操作,可以接受四个参数set(目标对象, 属性名, 属性值,Proxy 实例本身),其中最后一个参数可选。
// 往数组里面设置一个值
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])
// has(target, propKey)
// has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
// has方法可以接受两个参数,分别是目标对象、需查询的属性名。
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(target):拦截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)) // ["name"]
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(es)]
console.log(Object.keys(obj)) // ["name"]
for (const key in obj) {
console.log(key) // name
}
// 应用场景
// 隐藏一个对象的私有属性
let userInfo = {
username: 'zhangsan',
age: 34,
_pwd: '****'
}
userInfo = new Proxy(userInfo, {
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
for (const key in userInfo) {
console.log(key) // username age
}
// 综合案例
let user = {
username: 'zhangsan',
age: 34,
_pwd: '****'
}
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]
}
},
ownKeys(target) {
return Object.keys(target).filter( key => !key.startsWith('_'))
}
})
// console.log(user.age) // 34
// console.log(user._pwd) // Uncaught Error: 不可访问
// user._pwd = '123' // Uncaught Error: 不可访问
// delete user._pwd // 不可删除
// 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(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。
let User = class {
constructor(name) {
this.name = name
}
}
User = new Proxy(User, {
construct(target, args, newTarget){
return new target(...args)
}
})
console.log(new User('zhangsan'))