学习内容:
ES 5当中实现拦截的方法:
let obj = {}
let newValue = ''
Object.defineProperty(obj, 'name', {
get() {
return newValue
},
set(val) {
console.log('set')
newValue = val
}
})
obj.name = 'test'
console.log(obj.name)
-------
set
test
ES6 Proxy
// 第一个参数是拦截或是要包装的对象或方法,
// 第二个参数是代理的配置,会对应一些钩子函数,
// 比如上面的get/set方法都可以称为钩子函数,
// 或者说在某些操作执行之前,要执行的函数称为钩子函数。
let obj = {}
let p = new Proxy(obj, {})
p.name = 'test'
console.log(obj.name);
for(let key in obj) {
console.log(key);
}
----------
test
name
拦截器 |
作用 |
get |
拦截对象属性的读取,比如proxy.name和proxy[name] |
set |
拦截对象属性的设置,返回一个布尔值,比如proxy.name=v或proxy['name'] = 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命令,返回一个对象 |
下面这个例子是对数组的代理,下标位置有值存在则返回对应的值,没有返回error:
let arr = [1, 2, 3]
arr = new Proxy(arr, {
get(target, prop) {
console.log(target, prop)
return prop in target ? target[prop] : 'error'
}
})
console.log(arr[1]);
---------------------
(3) [1, 2, 3] '1'
2
再来一个对象代理的例子:
let dict = {
'Good' : '好',
'morning' : '早上'
}
dict = new Proxy(dict, {
get(target, prop) {
return prop in target ? target[prop] : prop
}
})
console.log(dict['Good'])
console.log(dict['haha']);
----------
Good
haha
拦截方式 : 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(10 in range)
--------
true
false
拦截方式:ownKeys
对象的循环遍历时拦截
let userinfo = {
username : 'Sure',
age : 36,
_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));
-----------
username
age
(2) ['username', 'age']
拦截方式deleteProperty()
对象删除属性时拦截
let obj = {
k : 'k',
v : 'v',
_p : '**'
}
obj = new Proxy(user, {
deleteProperty(target, prop) {
if(prop.startsWith('_') {
throw new Error('不可删除')
} else {
delete target[prop]
return true // 不要忘了返回boolean
}
}
})
拦截方式apply()
用于拦截函数、call、apply等
// 例子:对于参数不固定的求和
let sum = (...args) => {
let num = 0
args.forEach(item => {
num += item
})
return num
}
sum = new Proxy(sum, {
// 第一个参数,是被拦截对象,第二个参数是上下文,
// 第三个参数是目标对象参数的数组
apply(target, ctx, arg) {
return target(...arg) * 2
}
})
console.log(sum(1, 2));
console.log((sum.call(null, 1, 2, 3))) // 不需要改变this指向,所以第一个参数为null
// apply后面的参数是数组
console.log(sum.apply(null, [1, 2, 3])) // 同样,不需要改变this指向,所以第一个参数为null
---------
6
12
12
拦截方式construct()
用于拦截new()
let User = class {
constructor(name) {
this.name = name
}
}
User = new Proxy(User, {
// 这个拦截操作必需要返回一个对象
construct(target, args, newTartget) {
console.log('construct');
return new target(...args)
}
})
console.log(new User('test'));
--------------------
construct
User {name: 'test'}