Bootstrap

ES6 proxy 看这一篇就够啦!

Proxy 是 ES6 为了操作对象引入的 API 。
Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。

1.简单用法

let star = {
name: '我是小阳仔',
age: 18
}
let proxy = new Proxy(star,{
    get(targetObj, propoty, receiver) {
        console.log(`我是被代理的对象${targetObj}`)
        console.log(`我是你访问的被代理的属性${propoty}`)
        //receiver是代理对象proxy
        return targetObj[propoty]
    }
})
console.log(proxy.name)

这就是一个最简单的proxy的例子。 首先我们需要利用Proxy构造函数创建一个Proxy对象,我们需要向这个构造函数中传递两个参数,第一个参数是被代理的对象,第二个参数是一个捕获器对象。我们需要代理的所有行为都是在捕获器对象中定义的。
注意:只有当我们访问代理的时候才会被捕获,这时候我们仍然可以访问原来的对象,和设置捕获钱没有任何的变化。

2.Proxy代理另一个代理

Proxy是可以设置多层代理的,即代理之上还有代理。具体实现:

let proxyFirst = new Proxy(obj,{})
let proxySecond = new Proxy(proxyFirst,{})

我们通过proxySecond访问obj对象 的时候,不仅会触发他本身在捕获器对象中定义的捕获方法,还会触发proxyFirst 的。
我们通过proxyFirst 访问obj对象的时候,那么只会触发定义的在proxyFirst 身上的捕获方法。

3.捕获器中可以使用的方法

get()、set()、has、defineProperty()、getOwnPropertyDescriptor()、deleteProperty() 、ownKeys()、getPrototypeOf()、setPrototypeOf()、isExtensible()、preventExtensions()、construct()、apply()
捕获器有13种,这里我们重点介绍4种,即对象的增删改查。由于我们已经介绍过get()下面我们将介绍常用的3个。

· set()

set即设置,就是我们在设置对象属性的时候会捕获到。示例:

let proxy = new Proxy(star,{
    set(targetObj, property, value, receiver) {
        if(property == 'height') {
            if(typeof value === 'number') {
                return Reflect.set(targetObj, property, `${value+10}cm`)
            }else {
                throw '身高只能是数字类型的值'
            }
        }
    }
})
proxy.height = 180
console.log(proxy.height)

在这个简单的示例当中,我们给star对象设置了一个代理,这个代理就是捕获在给star设置身高的时候,多加10cm。set()方法接收三个参数(目标对象,设置的属性,设置的值,proxy对象)

· has()

has()即在查找的时候会被捕获,for…in ,has()代码如下:

let proxy = new Proxy(star,{
   has(targetobj, property) {
        return Reflect.has(...arguments)
    }
})
proxy.height = 180
console.log('fansNums' in proxy)

has()捕获器接收两个参数,第一个参数是目标对象,第二个参数是引用的目标对象上字符串的属性。利用has捕获器,我们可以把不想让别人访问到的对象的属性隐藏掉。

· deleteProperty()

在我们删除属性的时候detete()捕获器会被触发。直接上代码:

let proxy = new Proxy(star,{
    deleteProperty(targetobj, property) {
        if(property === 'name') {
            return false
        }else {
            return Reflect.deleteProperty(...arguments)
        }
    }
})
delete proxy.name
console.log(proxy.name)

4.创建可撤销的代理

const {proxy, revoke} = Proxy.revocable(star,{
    get(targetObj, propoty, receiver) {
        console.log(`我是被代理的对象${targetObj}`)
        console.log(`我是你访问的被代理的属性${propoty}`)
        //receiver是代理对象proxy
        return targetObj[propoty]
    }
})
// revoke()
console.log(proxy.name)

这和普通的创建proxy实例的方式有些区别,使用的是Proxy.revocable创建的代理。当我们不想使用此代理的时候可以使用revoke()直接将其删除即可。

5.Proxy使用场景:实现数据绑定

众所周知,VUE3之前,双向绑定是使用Object.defineProperty,而VUE3则是基于proxy来实现的。为什么呢?肯定是为了改进一些缺陷啊!

Object.defineProperty 不足

无法监听数组的变化: 数组的这些方法是无法触发set的:push, pop, shift, unshift,splice, sort, reverse.,vue中能监听是因为对这些方法进行了重写
只能监听属性,而不是监听对象本身,需要对对象的每个属性进行遍历。对于原本不在对象中的属性难以监听。

实现

可以很方便的使用 Proxy 来实现一个数据绑定和监听。

let onWatch = (obj, setBind, getLogger) => {
  let handler = {
    get(target, property, receiver) {
      getLogger(target, property)
      return Reflect.get(target, property, receiver);
    },
    set(target, property, value, receiver) {
      setBind(value);
      return Reflect.set(target, property, value);
    }
  };
  return new Proxy(obj, handler);
};

let dataObj = { a: 1 }
let value
let p = onWatch(dataObj, (v) => {
  value = v
}, (target, property) => {
  console.log(`Get '${property}' = ${target[property]}`);
})
p.a = 2 // bind `value` to `2`
p.a // -> Get 'a' = 2

 欢迎在评论区交流。

如果文章对你有所帮助,❤️关注+点赞❤️鼓励一下博主会持续更新。。。。

我的博客原文:ES6 proxy 看这一篇就够啦!

;