Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
ES系列文章
代理
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
基本用法
在es5中使用Object.defineProperty对对象进行拦截,拦截对象本身和属性 。
let obj = {}
let newVal = ''
Object.defineProperty(obj, 'name', {
get(){
return newVal
},
set(val){
console.log('set')
// this.name = val
newVal = val
}
})
obj.name = 'es'
console.log(obj.name)
在es6中使用proxy对象进行拦截,Proxy对象由两个部分组成分别是:target,handler.通过Proxy构造函数生成对象时,需要提供这两个参数。
let obj = {}
let p = new Proxy(obj, {})
obj.name = 'imooc'
console.log(obj.name)
for(let key in obj){
console.log(key)
}
常用拦截方法
get方法
get方法用于拦截某个属性的读取操作。
//数组
let arr = [7, 8, 9]
arr = new Proxy(arr, {
get(target, prop) {
// console.log(target, prop)
return prop in target ? target[prop] : 'error'
//判断当前下标所在位置是否有值,有值既返回下标所对应的值,没有返回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方法
set方法用来拦截某个属性的赋值操作
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)
set方法一共有3个参数,第一个参数是目标的对象(target),第二个参数是当前目标属性的值(prop),第三个是目标属性设置值(val)。
has方法
has方法用来判断当前key是否在对象里面,返回对应的布尔型(Boolean)的值。
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方法
ownKeys方法用来拦截对象循环遍历进行拦截。
let obj = {
name: 'imooc',
[Symbol('es')]: 'es6'
//[Symbol('es')] 表示一个变量
}
console.log(Object.getOwnPropertyNames(obj
//getOwnPropertyNames用来返回不是Symbol的键名
console.log(Object.getOwnPropertySymbols(obj))
//getOwnPropertySymbols用来返回Symbol,没有普通是属性。
console.log(Object.keys(obj))
//keys只能取到不带Symbol的属性。
for(let key in obj){
console.log(key)
}
//for in 用来遍历对象,返回name属性,没有symbol。
let userinfo = {
username: 'xxx',
age: 18,
_password: '***'
}
userinfo = new Proxy(userinfo, {
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith('_'))
//根据拦截规则返回符合条件的
//startsWith 是用来检查字符串以什么开头
}
})
for (let key in userinfo) {
console.log(key)
}
console.log(Object.keys(userinfo))
综合使用
let user = {
name: 'xxx',
age: 18,
_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._password
} catch (e) {
console.log(e.message)
}
console.log(user.age)
for(let key in user){
console.log(key)
}
apply 方法
apply方法用来拦截函数的调用、call和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]))
//apply和call 作业类似,都是用来改变this指向。apply对应的是数组,call对应函数后面的参数。
前面代理的是对象,apply方法中代理的是函数。
apply方法有三个参数,第一个参数是目标的对象(target),第二个参数是当前目标的上下文(ctx),第三个参数是目标对象参数的数组(args)。
construct方法
construct方法用于拦截new命令
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('xxx'))
construct方法返回的必须是一个对象,否则会报错
construct方法有三个参数,第一个参数是目标的对象(target),第二个参数是当前构造函数的参数列表(args),第三个参数是创建实例的new命令作用的函数(newTarget)。
一个前端小白,若文章有错误内容,欢迎大佬指点讨论!