es6 Proxy

51 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

过去

Vue 利用 object.defineProperty来监测数据的读取和设置,在get中记录被依赖关系,在set时,通过依赖关系,触发更新,缺点是,在程序运行前,要将data属性全部递归遍历设置这个拦截规则,相当浪费性能,数组的检测更是要,重写Array.prototype上所有的变异方法,在方法执行之前做点事,执行后做点事,相当麻烦,es6Proxy的方法的出现,解决了Object.definePropery的痛点,在目标对象之前设一层"拦截",外界对该对象的访问,都必须通过这层拦截,可以对外界的访问进行过滤和改写,而且proxy根本不管对象内部嵌套多少层,因为他会在对象属性被读取时才处理,所以避免了遍历递归的性能问题。新增属性还要特殊处理。

get

使用 new Proxy 来创建proxy实例,这个构造函数接收两个参数,一个是target(目标对象,源对象,), 一个是handler(处理对象,常用的方法有get,set,deleteProperty,主要逻辑来自这里)

程序在读取对象属性的时候会执行get方法,get方法接收两个参数 target(目标对象),key(程序读取的属性),下面读取了代理对象三个属性,所以get方法执行了三次,target是obj,key是对应的属性名,所以结果和直接读原来对象的属性结果一样。

const obj = {
    name: "tom",
    age: 19,
    sex: 12,
}
const p1 = new Proxy(obj, {
    get(target, key) {
    
        console.log(target, key);
        return target[key];
    },
})

console.log(p1.name,p1.age,p1.sex)

当然啥也不干就没必要劫持了,假设要给每个属性一个所属对象的前缀,"obj---",可以在get里加一个拼接就轻松实现了。这样一搞,所有读的属性都带前缀 obj---

 ...
  get(target, key) {
        console.log(target === obj, key);
        return "obj---" + target[key];
    },

set

当代理对象的属性被赋值时,会调用set方法,set方法接收三个参数,target(目标对象),key所设置的属性名,value设置的新的值

const obj = {
    name: "tom",
    age: 19,
    sex: 12,
}
const p1 = new Proxy(obj, {
 
    set(target, key, value) {
        console.log(target, key, value)
        target[key] = value
    }
})

p1.age = '123'

可以拦截一下禁止设置对象,不能给属性设置为对象类型,

const obj = {
    name: "tom",
    age: 19,
    sex: 12,
}
const p1 = new Proxy(obj, {
    get(target, key) {
        console.log(target === obj, key);
        return "obj---" + target[key];
    },
    set(target, key, value) {
        if (typeof value == 'object') {
            console.log('不能赋值对象')
            return;
        }
        target[key] = value
    }
})

p1.age = {la:'xx'}
console.log(p1.age)

主要用的一般都这两,剩下的不怎么用

  • get(target, propKey, receiver) :拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver) :拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey) :拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey) :拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target) :拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey) :拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc) :拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target) :拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target) :拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target) :拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto) :拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args) :拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args) :拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)