ES6 -- 最佳代理Proxy:实例方法

来源:互联网 发布:vscode mysql 编辑:程序博客网 时间:2024/06/06 00:06

get方法

该方法用来代理属性的读取操作。当用户希望访问对象的某个属性时,会直接触发这个方法,而不是默认的读取属性方法。

栗子:

var car = {  brank: "Benz"};var proxy = new Proxy(car, {  get: function(target, property) {    if (property in target) {      return target[property];    } else {      throw new ReferenceError("Property \"" + property + "\" does not exist.");    }  }});proxy.brank // "Benz"proxy.year // Error:Property year does not exist.

本栗中,如果访问的属性存在,将会返回属性值,但是如果属性不存在,则会抛出错误。在平常的情况下,如果访问的属性不存在,只是会返回undefined,不会报错。

利用proxy,可以实现函数的链式调用:(代码来自阮一峰大神ES6博客)

var pipe = (function () {  return function (value) {    var funcStack = [];    var oproxy = new Proxy({} , {      get : function (pipeObject, fnName) {        if (fnName === 'get') {          return funcStack.reduce(function (val, fn) {            return fn(val);          },value);        }        funcStack.push(window[fnName]);        return oproxy;      }    });    return oproxy;  }}());var double = n => n * 2;var pow    = n => n * n;var reverseInt = n => n.toString().split("").reverse().join("") | 0;pipe(3).double.pow.reverseInt.get; // 63

代码分析:如果想要实现函数的链式调用,就需要将可以调用某函数的对象作为函数的返回值。在上面这个栗子中,对oproxy的属性(方法其实也可以看做属性)的访问总会被代理函数get拦截,并根据我们部署的代码,在函数的最后返回oproxy对象。这样,对于这个对象的方法的访问,就可以链式不断的调用下去,因为每个方法调用后,都会返回原对象。

限制:如果对象的属性被设置为configurable:false或者writable:false,那么这时候,get属性将不能被代理,通过Proxy访问该属性会报错。

const cantChangeObj = Object.defineProperties({}, {  foo: {    value: 123,    writable: false,    configurable: false  },});const handler = {  get(target, propKey) {    return 'geting'+propKey+'from'+target;  }};const proxy = new Proxy(cantChangeObj, handler);proxy.foo// TypeError: Invariant check failed

set方法

set方法用来代理对象某个属性的赋值操作。通过set,我们就可以获得对属性值的类型、大小等等的掌控能力。

let validate = {  set: function(obj, prop, value) {    if (prop === 'year') {      if (!Number.isInteger(value)) {        throw new TypeError('The year must be an integer');      }      if (year < 2000) {        throw new RangeError('The year seems invalid');      }    }    // 对于year以外的属性,可以直接保存,或使用if增加其他判断    obj[prop] = value;  }};let car = new Proxy({}, validate);car.year = 2001;car.year // 2001car.year = 'Benz' // 报错car.year = 1999 // 报错

上述代码对car对象的year属性设置了范围:首先必须是数字,其次,必需要在2000以后(太老旧的车不能通过验证)。

应用1: 对于set方法我们还可以将数据与DOM元素绑定,每当元素值发生变化,DOM也就相应的发生变化。

应用2: 有些对象的内部属性,是我们不希望他人有意或无意修改的,通常会用下划线“_”开头,set方法可以帮助我们切实的阻止他人对此类对象的访问。

var handler = {  get (target, key) {    invariant(key, 'get');    return target[key];  },  set (target, key, value) {    invariant(key, 'set');    target[key] = value;    return true;  }};function invariant (key, action) {  if (key[0] === '_') {    throw new Error(`Invalid attempt to ${action} private "${key}" property`);  }}var target = {};var proxy = new Proxy(target, handler);proxy._prop// Error: Invalid attempt to get private "_prop" propertyproxy._prop = 'c'// Error: Invalid attempt to set private "_prop" property

限制:同get一样,如果目标对象自身的某个属性,不可写也不可配置,那么set不得改变这个属性的值,只能返回同样的值,否则报错。

apply方法

apply可以代理call和apply两个操作(因为这两个操作本质上做的事情都一样嘛)。

apply方法的三个参数:目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

一个最简单的栗子:

var target = function () { return 'I am the target'; };var handler = {  apply: function () {    return 'I am the proxy';  }};var p = new Proxy(target, handler);p()// "I am the proxy"

当p被作为函数调用的时候(p()),代理方法apply就会生效,返回的是apply函数的字符串。

另一个稍微复杂的栗子:

var twice = {  apply (target, ctx, args) {    return Reflect.apply(...arguments) * 2;  }};function sum (left, right) {  return left + right;};var proxy = new Proxy(sum, twice);proxy(1, 2) // 6proxy.call(null, 5, 6) // 22proxy.apply(null, [7, 8]) // 30

总上面几个代码我们可以总结出——apply代理的途径包括:1、当直接执行proxy函数时,2、proxy调用call或者apply时。

has方法

has方法将作为方法HasProperty的代理,HasProperty的功能是判断对象是否具有某个属性。

应用1: 使用has方法,可以帮助我们隐藏某些属性,不被in运算符发现。

var handler = {  has (target, key) {    if (key === 'privateProp') {      return false;    }    return key in target;  }};var target = { privateProp: 'myPassword', prop: 'foo' };var proxy = new Proxy(target, handler);'privateProp' in proxy // false

需要注意的是,has方法代理的方法是HasProperty(在没有任何其他配置的前提下,一个对象自身的属性和继承的属性都可以被此方法找到),而不是HasOwnProperty(此方法尽可以找到属于对象自身的属性)。还需要注意,has只对in运算符有效,而不是for…in…。

限制:如果原对象被设置为不可配置/禁止扩展,那么,该操作就会报错:

var obj = { a: 10 };Object.preventExtensions(obj);var p = new Proxy(obj, {  has: function(target, prop) {    return false;  }});'a' in p // TypeError is thrown
0 0
原创粉丝点击