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

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 // 2001
car.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" property
proxy._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) // 6
proxy.call(null, 5, 6) // 22
proxy.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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值