ES6| 代理Proxy

491 阅读3分钟

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)。

一个前端小白,若文章有错误内容,欢迎大佬指点讨论!