ECMAScript6(12):Proxy 和 Reflect

Proxy 对象

Proxy 用来修正某些默许操纵,同等于在言语层面做出修正。所以属于一种元编程(meta programming), 即对编程言语举行编程。字面理解为Proxy代办了某些默许的操纵。
其运用花样以下:

var proxy = new Proxy(target, handler);

target是被代办的目的对象,handler也是个对象,用来定制阻拦行动,内部定义每一个被代办的行动。
注重:

  • 假如愿望这个代办有用,需要在 proxy 对象上挪用属性要领,而不是在 target 上挪用
  • 假如指定 handler 为空对象,那末获得对象和原对象一样
  • 获得的 proxy 是 target 的援用,假如没有代办,在 proxy 上的修正和在 target 上的修正同等

看一个简朴的实例

var proxy = new Proxy({},{
  get: function(target, key){
    return 35;
  }
});
console.log(proxy.time);    //35
console.log(proxy.name);    //35
console.log(proxy.title);    //35
//被代办的对象不管输入什么属性都返回35

实际上,proxy 对象也能够被继续:

var proxy = new Proxy({},{
  get: function(target, key){
    return 35;
  }
});
var obj = Object.create(proxy);
obj.time = 20;
console.log(obj.time);    //20
console.log(obj.name);    //35

感受一下它的威力:

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

obj.count = 1;            //setting count ...
++obj.count;              //getting count ...
                          //setting count ...
console.log(obj.count);   //getting count ...
                          //2

能够看出来,handler对象中 get 要领示意属性的接见要求,set 要领示意属性的写入要求。
固然不单单议 get 和 set, 我们能够定义以下阻拦函数:

  • get(target, propKey, receiver = target)

阻拦对象的读取属性。当 target 对象设置了 propKey 属性的 get 函数时,receiver 绑定 get 函数的 this。返回值恣意

  • set(target, propKey, value, receiver = target)

阻拦对象的写入属性。返回一个布尔值

  • has(target, propKey)

阻拦 propKey in proxy 操纵符,返回一个布尔值

  • deleteProperty(target, propKey)

阻拦 delete proxy[propKey] 操纵符,返回一个布尔值

  • enumerate(target)

阻拦 for(let i in proxy) 遍历器,返回一个遍历器

  • hasOwn(target, propKey)

阻拦 proxy.hasOwnProperty(‘foo’),返回一个布尔值

  • ownKeys(target)

阻拦 Object.getOwnPropertyNames(proxy), Object.getOwnPropertySymbols(proxy), Object.keys(proxy),返回一个数组。该要领返回对象一切本身属性,包含不可遍历属性,不包含 Symble属性,然则Object.keys(proxy)不应该包含不可遍历属性

  • getOwnPropertyDescriptor(target, propKey)

阻拦 Object.getOwnPropertyDescriptor(proxy, propKey),返回其属性描述符

  • defineProperty(target, propKey, propDesc)

阻拦 Object.defineProperty(proxy, propKey, propDesc), Object.defineProperties(proxy, propDesc),返回一个布尔值

  • 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(object, args)

  • construct(target, args, proxy)

阻拦用 new 挪用 proxy 函数的操纵,construct()返回的不是对象会报错

以下枚举一些 Proxy 的实例

接见对象不存在的属性报错

var obj = new Proxy({}, {
  get: function(target, key){
    if(key in target){
      return Reflect.get(target, key);
    } else {
      throw new ReferenceError(`"${key}" is not in object`);
    }
  }
});
obj.look = "picture";
console.log(obj.look);     //"picture"
console.log(obj.sleep);    //ReferenceError: "sleep" is not in object

数组索引为负时返回倒数位置的值

var origin = [10,20];
var arr = new Proxy(origin, {
  get(target, key){
    let index = parseInt(key);
    if(index < 0){
      index = target.length + index;
      if(index < 0) return undefined;
    }
    return Reflect.get(target, index);
  }
});
console.log(arr[0]);     //10
console.log(arr[1]);     //20
console.log(arr[2]);     //undefined
console.log(arr[-1]);    //20
console.log(arr[-4]);    //undefined

庇护对象内以 “_” 开首的属性为私有属性:

var o = {
  "_name": "Bob",
  "age": 13,
  "_fun": function(){
    console.log("_fun is called");
  }
};
var obj = new Proxy(o, {
  get(target, key){
    if(key.charAt(0) === '_'){
      return undefined;
    }
    return Reflect.get(target, key);
  },
  set(target, key, value){
    if(key.charAt(0) === '_'){
      throw new Error('Cannot define a property begin with "_"');
    }
    return  Reflect.set(target, key, value);
  },
  has(target,key){
    if(key.charAt(0) === '_'){
      return false;
    }
    return Reflect.has(target, key);
  },
  deleteProperty(target,key){
    if(key.charAt(0) === '_'){
      return false;
    } else {
      Reflect.deleteProperty(..arguments);
    }
  },
  apply(target,ctx,args){
    if(target.name.charAt(0) === '_'){
      throw new TypeError(`${target.name} is not defined`);
    } else {
      Reflect apply(...arguments);
    }
  },
  defineProperty(target,key,desc){
    if(key.charAt(0) === '_'){
      return new Error(`cannot define property begin with "_"`);
    } else {
      Reflect.defineProperty(..arguments);
    }
  },
  setPrototypeOf(target,proto){
    throw new TypeError(`Cannot change the proto of ${target}`);
  },
  construct(target,ctx,args){
    if(target.name.charAt(0) === '_'){
      throw new TypeError(`${target.name} is not defined`);
    } else {
      Reflect construct(...arguments);
    }
  }
});

console.log(obj.age);    //13
obj.age = 20;
console.log(obj.age);    //20
console.log(obj._name);  //undefined
obj._hobby = "Coding";   //Error: Cannot define a property begin with "_"
_name in key             //false
delete obj._name;
Object.defineProperty(obj,"_hobby",{
  value: "Coding"
});
Object.defineProperties(obj,{
  '_hobby': {
    value: "Coding"
  }
});
obj._fun();
var a = new obj._fun();
obj.__proto__ = {};     //Cannot define a property begin with "_"
Object.setPrototypeOf(obj,{})    //Cannot change the proto of obj

固然不是一切 proxy 代办都不可作废,下面要领设置的代办是能够经由过程定义代办时返回的revoke函数作废:

var a = {
  name:"Bob"
};
var {proxy, revoke} = Proxy.revocable(a, {
  get(target,key){
    return undefined;
  }
});
proxy.name;   //undefined;
revoke();
proxy.name;   //TypeError: Cannot perform 'get' on a proxy that has been revoked

Reflect 对象

Reflect 对象有一下作用:

  1. 将 Object对象的一些显著属于言语层面的要领布置在 Reflect 上
  2. 修正某些 Object 对象的要领使其更合理。比方 Object.defineProperty 碰到没法定义属性时会抛出毛病,而 Reflect.defineProperty 会返回 false
  3. 把所以 object 的操纵都替代成函数行动,比方用 Reflect.has(obj,name) 替代 name in obj
  4. 保证只如果 Proxy 有的要领就肯定能够在 Reflect 上找到雷同的要领,如许能够在完成 proxy 时轻易的完成默许行动。换言之,不管 proxy 怎样修正默许行动,你总能够在 Reflect 上找到真正默许的行动

代办在增加分外的功用时,应用 Reflect 保证了原始功用的完成。举个例子:

var loggedObj = new Proxy({}, {
  get(target,propKey){
    console.log(`getting ${target}.${propKey}`);  //固然你最好把操纵记录到一个 log 中
    return Reflect.get(target,propKey);
  }
});

Reflect有以下要领:

  • Reflect.getOwnPropertyDescriptor(target, propKey)

同等于 ObjectgetOwnPropertyDescriptor(target, propKey)

  • Reflect.defineProperty(target,propKey,desc)

同等于 Object.defineProperty(target,propKey,desc)

  • Reflect.getOwnPropertyNames(target)

同等于 Object.getOwnPropertyNames(target)

  • Reflect.getPrototypeOf(target)

同等于 Object.getPrototypeOf(target)

  • Reflect.setPrototypeOf(target, proto)

同等于 Object.setPrototypeOf(target, proto)

  • Reflect.deleteProperty(target, propKey)

同等于 delete target.propKey

  • Reflect.enumerate(target)

同等于 for ... in target

  • Reflect.freeze(target)

同等于 Object.freeze(target)

  • Reflect.seal(target)

同等于 Object.seal(target)

  • Reflect.preventExtensions(target)

同等于 Object.preventExtensions(target)

  • Reflect.isFrozen(target)

同等于 Object.isFrozen(target)

  • Reflect.isSealed(target)

同等于 Object.isSealed(target)

  • Reflect.isExtensible(target)

同等于 Object.isExtensible(target)

  • Reflect.has(target, propKey)

同等于 propkey in object

  • Reflect.hasOwn(target, propKey)

同等于 target.hasOwnProperty(propKey)

  • Reflect.ownKeys(target)

遍历获得target本身一切属性,包含不可枚举属性,不包含 Symbol 属性

  • Reflect.get(target,propKey, receiver = target)

假如 propKey 是个读取器,则读取器中的 this 绑定到 receiver

var per = {
  bar: function(){console.log("per-bar")}
}
var obj = {
  get foo(){ this.bar(); },
  bar: function (){console.log("obj-bar")}
};
Reflect.get(obj, "foo", per);    //"per-bar"
  • Reflect.set(target,propKey, value, receiver = target)

假如 propKey 是个读取器,则读取器中的 this 绑定到 receiver

  • Reflect.apply(target, thisArg, args)

同等于 Function.prototype.apply.call(target, thisArg, args)thisArg.target(args)

  • Reflect.construct(target,args)

同等于 new target(...args)

注重以上要领中,Reflect.set(), Reflect.defineProperty(), Reflect.freeze(), Reflect.seal(), Reflect.preventExtensions() 在胜利时返回 true, 失利时返回 false。对应的 Object 要领失利时会抛出毛病。

    原文作者:Faremax
    原文地址: https://segmentfault.com/a/1190000016286264
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞