4

Proxy 用于修改某些操作的默认行为(基本操作有属性查找,赋值,枚举,函数调用等)。

/*
* target 目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
* handler 一个对象,其属性是操作对应的自定义代理函数
*/
let p = new Proxy(target, handler);

在TypeScript中定义了可被代理得基本操作,一共14个(Reflect对象也是具有这14个方法),如下:

interface ProxyHandler<T extends object> {
    getPrototypeOf? (target: T): object | null;
    setPrototypeOf? (target: T, v: any): boolean;
    isExtensible? (target: T): boolean;
    preventExtensions? (target: T): boolean;
    getOwnPropertyDescriptor? (target: T, p: PropertyKey): PropertyDescriptor | undefined;
    has? (target: T, p: PropertyKey): boolean;
    get? (target: T, p: PropertyKey, receiver: any): any;
    set? (target: T, p: PropertyKey, value: any, receiver: any): boolean;
    deleteProperty? (target: T, p: PropertyKey): boolean;
    defineProperty? (target: T, p: PropertyKey, attributes: PropertyDescriptor): boolean;
    enumerate? (target: T): PropertyKey[]; //废弃
    ownKeys? (target: T): PropertyKey[];
    apply? (target: T, thisArg: any, argArray?: any): any;
    construct? (target: T, argArray: any, newTarget?: any): object;
}

基本操作

get(target, propKey, receiver)

方法用于拦截对象的读取属性操作。

/*
* target 目标对象
* propKey 对象属性名
* receiver Proxy或者继承Proxy的对象
*/
get(target, propKey, receiver)

var obj = {name : 'Lucy'}
var p = new Proxy(obj,{
    get : function(target,key,receive){
        return key === 'name' ? 'Hello '+target[key] : target[key]
    }
})
p.name  //Hello Lucy

需要注意的是,如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性。

var obj = Object.defineProperties({}, {
  name: {
    value: 'Lucy',
    writable: false,
    configurable: false
  },
});
var p = new Proxy(obj, {
    get : function(target,key){
        return key === 'name' ? 'Hello '+target[key] : target[key]
    }
});
p.name //报错

set(obj, prop, value,receive)

拦截某个属性的赋值操作

/*
* target 目标对象
* key 属性名
* value 属性值
* receive Proxy或者继承Proxy的对象
*/
set: function(target, key, value,receive)

方法用于拦截设置属性值的操作

var obj = {age : 18}
var p = new Proxy(obj,{
    set : function(target,key,value){
      if(key === 'age'){
        target[key] = Math.min(value,100);    
      }else{
        target[key] = value;    
      }
    }
})
p.age = 101;
p.age // 100
obj.age // 100

需要注意的是Proxy不能进行深度代理,如:

var obj = {age : 18,borth: {month : 6}}
var p = new Proxy(obj,{
    set : function(target,key,value){
      console.log('exec proxy set')
      target[key] = value;    
    }
})
p.borth.month = 7
p.name = 'xuriliang' //可以对不存在的属性进行监听
obj.name = 'rlxu' //修改原始对象,代理对象也会同步数据

has(target, key)

拦截key in proxy的操作,返回一个布尔值

deleteProperty(target, propKey)

拦截delete proxy[propKey]的操作,返回一个布尔值。

ownKeys(target)

方法用来拦截对象自身属性的读取操作,返回一个数组。具体来说,拦截以下操作:

  • Object.getOwnPropertyNames() //返回所有属性
  • Object.getOwnPropertySymbols()
  • Object.keys(proxy) //返回可枚举属性
  • for...in循环
var obj = Object.create(null)
Object.defineProperties(obj, {
  '_id': {
    value: 1,
    configurable : true,
    writable : true,
    enumerable : true
  },
  'name': {
    value : 'Lucy',
    configurable : true,
    writable : true,
    enumerable : true
  }
});
Object.getOwnPropertyNames(obj) // ['_id','name']
for(let key in obj){
    console.log(key) //依次输出_id,name
}

var p = new Proxy(obj,{ 
    ownKeys : function(target){
        return Object.getOwnPropertyNames(target).filter(x => x[0] != '_')
    }
})
Object.getOwnPropertyNames(p) // ['name']
for(let key in p){
    console.log(key) //输出name
}

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

参考资料

Proxy MDN
ECMAScript 6入门
Reflect


xuriliang
245 声望6 粉丝