es6之Proxy

677 阅读2分钟

前言

Proxy用于修改某些操作的默认行为。可以理解成在目标对象前设置一层拦截,外界对该对象的访问都必须通过这层拦截,提供一种机制可以对外界的访问进行过滤和改写。

var proxy = new Proxy(target, handler);

target表示所要拦截的目标,handler也是一个对象,用来定制拦截行为。例子如下:

var proxy = new Proxy({}, {
    get(target, key, receiver){
        console.log(`getting ${key}`);
        return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver){
        console.log(`setting ${key}`);
        return Reflect.set(target, key, value, receiver);
    }
});

proxy.name = 'lili';
//setting name

对空对象进行拦截,重新定义了属性的读取和设置。

注意以下几点:

  • 要使Proxy起作用,必须针对Proxy实例进行操作,而不是针对目标对象进行操作
  • 如果handler没有设置任何拦截,那就等同于直接通向原对象

可以拦截的操作

方法名称 描述 例如
get(target, propKey, receiver) 拦截属性读取 proxy.foo
set(target, propKey, value, receiver) 拦截对象属性的设置 proxy.foo = 'foo'
has(target, propKey) 拦截propKey in proxy操作 'foo' in proxy
deleteProperty(target, propKey) 拦截delete proxy[propKey]
ownKeys(target) 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy) ,返回一个数组,包含目标对象所有自身属性的属性名
getOwnPropertyDescriptor(target, propKey) 拦截Object.getOwnPropertyDescriptor(target, propKey), 返回属性的描述对象
defineProperty(target, propKey, propDesc) 拦截Object.defineProperty(target, propKey, propDesc)、Object.defineProperties(target, propDescs)
preventExtensions(target) Object.~
getPrototypeOf(target) Object.getPrototypeOf()、Reflect.getPrototypeOf() 、Object.prototype.proto、Object.prototype.isPrototypeOf()、instanceof
isExtensible(target) Object.~
setPrototypeOf(target, proto) Object.~
apply(target, object, args) 函数调用,直接、apply、call调用
contructor(target, args) 作为构造函数调用的操作,new proxy(...args)

如果一个属性不可配置(configurable)或不可写(writable),则改属性不能被代理。

如果属性不可配置或对象禁止扩展,has必须返回false

this 问题

虽然Proxy可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下也无法保证与目标的行为一致。

const target = {
    m(){
        console.log(this === proxy);
    }
}

const proxy = new Proxy(target, {});

target.m(); //false
proxy.m(); //true

应用

使用get拦截实现数组负数索引

function createArray(...elements){

    return new Proxy([...elements], {
    
        get(target, key, receiver){
            let index = Number(key);
            let maxIndex = target.length;

            while (index < 0) {
                index =  maxIndex + index;
            }
            
            return Reflect.get(target, String(index), receiver);
        }
    })
}
var arr = createArray('a', 'b', 'c');
console.log(arr[-7]); //'c'