【ES6】Proxy代理


一、Proxy

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

  • 代理
  • 常用拦截方式

二、用法详解

1.声明方式

ES5中对属性进行拦截:

// ES5
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)
// es

ES6的 Proxy(代理目标, {对目标的操作})

// proxy
let obj = {}
let p = new Proxy(obj, {})
obj.name = 'kaka'
console.log(obj.name) // kaka
for(let key in obj){
    console.log(key) // name
}

2. get钩子函数

用于拦截对属性的访问操作

// get
let arr = [7, 8, 9]
arr = new Proxy(arr, { // 给arr数组加上代理拦截
    get(target, prop) {
        // console.log(target, prop)
        // 判断传入的参数是否有对应的值在对象中
        return prop in target ? target[prop] : 'error'
    }
})
console.log(arr[1]) // 8, 即数组下标1的位置的值
console.log(arr[10]) // error

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['kaka']) // kaka

3. set钩子函数

用于拦截对属性的设置操作

// set
let arr = []
arr = new Proxy(arr, {
    set(target, prop, val) {
        if (typeof val === 'number') {
            target[prop] = val
            return true
        } else {
            return false
        }
    }
})
// proxy代理并不会影响对象原有的方法 它只是在中间加上一层拦截处理
arr.push(5)
arr.push(6)
console.log(arr[0], arr[1], arr.length)
 // 5 6 2

4. has

判断传入的prop是否在对象中,返回布尔值

//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) // true
console.log(9 in range) // false

5. ownKeys

用于对当前对象的循环遍历方法进行拦截操作

let obj = {
    name: 'kaka',
    [Symbol('es')]: 'es6'
}
// getOwnPropertyNames不会返回Symbol类型的键
console.log(Object.getOwnPropertyNames(obj)) //["name"]

// getOwnPropertyNames只会返回Symbol类型的键
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(es)]

// keys不会返回Symbol类型的键
console.log(Object.keys(obj)) //["name"]

// for...in不会返回Symbol类型的键
for(let key in obj){
    console.log(key) //name
}

// ownKeys
let userinfo = {
    username: 'kakaDorothy',
    age: 20,
    _password: '***' // 使用下划线,表示当前的属性是一个私有属性
}
userinfo = new Proxy(userinfo, {
    ownKeys(target) {
        return Object.keys(target).filter(key => !key.startsWith('_')) // 返回不以_开头的key
    }
})
for (let key in userinfo) {
    console.log(key) // username age
}
console.log(Object.keys(userinfo)) // username age

6. 基本方法综合应用

// 对于私有属性,设为不可访问不可操作
let user= {
    name: 'kaka',
    [Symbol('es')]: 'es6'
}
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
            // 对于set方法要返回Boolean的值
            return true
        }
    },
    // 删除操作 用于拦截delete方法
    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.age
//   delete user._password
// } catch (e) {
//   console.log(e.message) // 不可删除
// }
// console.log(user.age) // undefined

for(let key in user){
    console.log(key) // name age
}

7.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)) // 6
//call、apply都可以改变this指向
// 传入的目标对象为null,后面都为参数
console.log(sum.call(null, 1, 2, 3)) // 12
// apply的参数为一个数组
console.log(sum.apply(null, [1, 2, 3])) // 12

8. construct

用于拦截new命令

// construct -> new
let User = class {
    constructor(name) {
        this.name = name
    }
}
User = new Proxy(User, {
    //construct(目标函数,构造函数的参数列表,创建实例时new命令作用的构造函数)
    construct(target, args, newTarget) {
        console.log('construct')
        // construct必须返回一个对象
        return new target(...args)
    }
})
console.log(new User('kaka')) // User {name: "kaka"}

总结

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值