使用 Object.defineProperty()
模拟 Vue
的数据绑定
const _proxy = (data, callback) => {
const _data = {}
Object.keys(data).forEach(key => {
_data[key] = data[key]
Object.defineProperty(data, key, {
get() {
return _data[key]
},
set(value) {
_data[key] = value
callback?.(key, value)
}
})
})
return data
}
const observe = (key, value) => console.log(`${key} : ${value}`)
const v = _proxy({ num: 7 }, observe)
v.num = 11 // 控制台输出 => num : 11
使用 Proxy
封装一个可设置类型的数组 TypeArray
,并且对数组中不存在的值返回默认值
const TypeArray = (type, defaultValue = 'N/A', initArray = []) => {
return new Proxy(initArray, {
get(target, prop) {
return target[prop] || defaultValue
},
set(target, prop, value) {
if (typeof value === type || prop === 'length') {
target[prop] = value
return true
} else {
return false
}
}
})
}
const numArr = TypeArray('number')
numArr.push(1) // Proxy 代理数组 数组本身的方法属性不会被破坏
numArr.push('2') // Uncaught TypeError: 'set' on proxy: trap returned falsish for property '2' at Proxy.push (<anonymous>)
使用 has
方法捕获 in
操作符
const checkRange = new Proxy(
{
start: 0,
end: 100
},
{
has(target, prop) {
return prop >= target.start && prop <= target.end
}
}
)
console.log(50 in checkRange) // false
console.log(150 in checkRange) // true
使用 ownKeys
拦截 Reflect.ownKeys()
也可以拦截 Object.keys() ; for in Loop ; Object.getOwnPropertySymbols()
中的部分的 key
const obj = {
attr: 'attr',
_attr: '_attr',
[Symbol('attr')]: 'Symbol(attr)',
[Symbol('_attr')]: 'Symbol(_attr)'
}
// 未使用 Proxy 的 ownKeys 拦截
console.log(Object.keys(obj)) // ["attr", "_attr"]
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(attr), Symbol(_attr)]
console.log(Reflect.ownKeys(obj)) // ["attr", "_attr", Symbol(attr), Symbol(_attr)]
for (const key in obj) {
console.log(key) // attr _attr
}
// 使用 Proxy 的 ownKeys 拦截
const p = new Proxy(obj, {
ownKeys(target) {
const normalKeys = Object.keys(target)
const normalFilterKeys = normalKeys.filter(key => !key.startsWith('_')) // 拦截以下划线开头的 key
const symbolKeys = Object.getOwnPropertySymbols(target)
const symbolFilterKeys = symbolKeys.filter(key => !key.toString().startsWith('Symbol(_')) // 拦截以下划线开头的 key
return [...normalFilterKeys, ...symbolFilterKeys]
}
})
console.log(Object.keys(p)) // ["attr"]
console.log(Object.getOwnPropertySymbols(p)) // [Symbol(attr)]
console.log(Reflect.ownKeys(p)) // ["attr", Symbol(attr)]
for (const key in p) {
console.log(key) // attr
}
来个综合案例
const userInfo = {
name: 'Jack',
age: 17,
_address: 'London UK'
}
const p = new Proxy(userInfo, {
get(target, prop) {
if (prop.startsWith('_')) {
throw new Error(`${prop} 不可访问`)
} else {
return target[prop] || 'N/A'
}
},
set(target, prop, value) {
if (prop.startsWith('_')) {
throw new Error(`不能设置 ${prop}`)
} else {
target[prop] = value
return true
}
},
deleteProperty(target, prop) {
if (prop.startsWith('_')) {
throw new Error(`不能删除${prop}`)
} else {
delete target[prop]
return true
}
},
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
}
})
使用 apply
拦截函数的调用(包装函数)
const sum = (a, b) => {
return a + b
}
const sumText = new Proxy(sum, {
apply(target, thisArg, argArray) {
return `total number is ${target(...argArray)}`
}
})
sumText(10, 20)