赞
踩
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
- var obj = new Proxy({}, {
- get: function (target, propKey, receiver) {
- console.log(target); // 目标对象:拦截和改写的对象
- console.log(prop); // 对象属性
- console.log(receiver); // proxy对象本身
- return Reflect.get(target, propKey, receiver);
- },
- set: function (target, propKey, value, receiver) {
- console.log(value); // 属性设置值
- return Reflect.set(target, propKey, value, receiver);
- }
- });
-
- obj.count = 1
- // setting count!
- ++obj.count
- // getting count!
- // setting count!
- // 2
同一个拦截器函数,可以设置拦截多个操作。proxy
对象是obj
对象的原型,obj
对象本身并没有time
属性,所以根据原型链,会在proxy
对象上读取该属性,导致被拦截。
- var proxy = new Proxy({}, {
- get: function(target, propKey) {
- return 35;
- }
- });
-
- let obj = Object.create(proxy);
- obj.time // 35
拦截操作:
proxy.foo
和proxy['foo']
。proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值。propKey in proxy
的操作,返回一个布尔值。delete proxy[propKey]
的操作,返回一个布尔值。Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。Object.preventExtensions(proxy)
,返回一个布尔值。Object.getPrototypeOf(proxy)
,返回一个对象Object.isExtensible(proxy)
,返回一个布尔值。Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。new proxy(...args)
。get:利用 Proxy,可以将读取属性的操作(get
),转变为执行某个函数,从而实现属性的链式操作。
- var pipe = 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
set:假定Person
对象有一个age
属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy
保证age
的属性值符合要求。
- let validator = {
- set: function(obj, prop, value) {
- if (prop === 'age') {
- if (!Number.isInteger(value)) {
- throw new TypeError('The age is not an integer');
- }
- if (value > 200) {
- throw new RangeError('The age seems invalid');
- }
- }
-
- // 对于满足条件的 age 属性以及其他属性,直接保存
- obj[prop] = value;
- }
- };
-
- let person = new Proxy({}, validator);
-
- person.age = 100;
-
- person.age // 100
- person.age = 'young' // 报错
- person.age = 300 // 报错
apply: 每当执行proxy
函数(直接调用或call
和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
has:用来拦截HasProperty
操作,即判断对象是否具有某个属性时(但是has()
拦截对for...in
循环不生效。)
- var handler = {
- has (target, key) {
- if (key[0] === '_') {
- return false;
- }
- return key in target;
- }
- };
- var target = { _prop: 'foo', prop: 'foo' };
- var proxy = new Proxy(target, handler);
- '_prop' in proxy // false
construct: 用于拦截new
命令
- const handler = {
- construct (target, args, newTarget) {
- return new target(...args);
- }
- };
-
- 1-construct()方法返回的必须是一个对象,否则会报错。
- 2-由于construct()拦截的是构造函数,所以它的目标对象必须是函数,否则就会报错。
-
- const p = new Proxy(function () {}, {
- construct: function(target, args) {
- console.log('called: ' + args.join(', '));
- return { value: args[0] * 10 };
- }
- });
-
- (new p(1)).value
- // "called: 1"
- // 10
deleteProperty: 用于拦截delete
操作,如果这个方法抛出错误或者返回false
,当前属性就无法被delete
命令删除。
- var handler = {
- deleteProperty (target, key) {
- invariant(key, 'delete');
- delete target[key];
- return true;
- }
- };
- function invariant (key, action) {
- if (key[0] === '_') {
- throw new Error(`Invalid attempt to ${action} private "${key}" property`);
- }
- }
-
- var target = { _prop: 'foo' };
- var proxy = new Proxy(target, handler);
- delete proxy._prop
- // Error: Invalid attempt to delete private "_prop" property
defineProperty()
方法拦截了Object.defineProperty()
操作。
- defineProperty()方法内部没有任何操作,只返回false,导致添加新属性总是无效。
- 注意,这里的false只是用来提示操作失败,本身并不能阻止添加新属性
-
- var handler = {
- defineProperty (target, key, descriptor) {
- return false;
- }
- };
- var target = {};
- var proxy = new Proxy(target, handler);
- proxy.foo = 'bar' // 不会生效
getOwnPropertyDescriptor()
方法拦截Object.getOwnPropertyDescriptor()
,返回一个属性描述对象或者undefined
。
- var handler = {
- getOwnPropertyDescriptor (target, key) {
- if (key[0] === '_') {
- return;
- }
- return Object.getOwnPropertyDescriptor(target, key);
- }
- };
- var target = { _foo: 'bar', baz: 'tar' };
- var proxy = new Proxy(target, handler);
- Object.getOwnPropertyDescriptor(proxy, 'wat')
- // undefined
- Object.getOwnPropertyDescriptor(proxy, '_foo')
- // undefined
- Object.getOwnPropertyDescriptor(proxy, 'baz')
- // { value: 'tar', writable: true, enumerable: true, configurable: true }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。