Proxy详解

一.Proxy基础

1. 什么是Proxy?

Proxy是一个构造函数,可以通过它生成一个Proxy实例。

const proxy = new Proxy(target, handler);
// target: 目标对象,待操作对象(可以是普通对象,数组,函数,proxy实例对象)
// handler: 一个对象,最多可包含13个操作方法,也可以是空对象

2. Proxy的作用

1. Proxy是一个ES6语法,它的作用主要是通过handler对象中的拦截方法拦截目标对象target的

某些操作(如读取,赋值,函数调用,new等),然后过滤或者改写这些操作的默认行为,

或者只是在完成这些操作的默认行为的基础上,增加一些副作用(如打印前置log等)。

2. 生成的实例对象是针对target对象的“拦截器”。也可以叫做“代理器”。

3. 然后必须通过操作proxy,即“拦截器”(拦截器对象本身性质和目标对象target一样,比如:target是函数,

那么proxy也是函数)才能触发拦截方法,来完成对目标对象的操作和访问。

4. 如果handler是个空对象({}),那么操作拦截器相当于直接操作目标对象target。

3. Proxy构造函数的特征

1. Proxy函数没有prototype属性,所以也就不能使用instanceof判断是否是Proxy实例

2. Proxy实例的数据类型和target数据类型一致。

var proxy1 = new Proxy([], {});
proxy1 instanceof Array; // true
var proxy2 = new Proxy(function(){}, {});
typeof proxy2; //"function"

二. Proxy拦截器handler方法

一共有13个拦截方法(对应Reflect的13个方法),可以大体分为两个部分。

1. 新的方法名

返回值是布尔值的方法有:

1. has(target, propKey)

作用:  拦截判断target对象是否含有属性propKey的操作

拦截操作: propKey in proxy;   不包含for...in循环

对应Reflect: Reflect.has(target, propKey)

语法:

const handler = {
    has(target, propKey){
        // ...
        //默认操作
        return propKey in target;
    }
}
const proxy = new Proxy(target, handler)

示例: 过滤某些私有属性

  const handler = {
    has(target, propKey) {
      if (propKey[0] === ‘_‘) {
        return false;
      }
      return propKey in target;
    }
  }
  const target = {_myprop: 1, a: 2, c:3};
  const proxy = new Proxy(target, handler);
  ‘_myprop‘ in proxy; // false

特殊情况: 目标target是不可扩展或者某个属性不可配置,只能返回默认行为结果;否则报错

var obj = { a: 10 };
Object.preventExtensions(obj);

var p = new Proxy(obj, {
  has: function(target, prop) {
    return false; //只能是return prop in target;
  }
});
‘a‘ in p; //Uncaught TypeError: ‘has‘ on proxy: trap returned falsish for property ‘a‘ but the proxy target is not extensible

2. deleteProperty(target, propKey)

作用: 拦截删除target对象的propKey属性的操作

拦截操作: delete proxy[propKey]

语法:

const handler = {
    deleteProperty(target, propKey){
        // ...
        //默认操作
        delete target[propKey];
        return true; //!!!严格模式下必须有,否则报错;
    }
}
const proxy = new Proxy(target, handler)

示例:

var obj = { a: 10 };
var p = new Proxy(obj, {
  deleteProperty(target, prop) {
    console.log(‘delete propName ‘,prop);   if
    delete target[prop];
    return true;
  }
});
delete p.a;
console.log(obj);
// 运行结果如下:
‘delete propName a‘
{}

特殊情况: 属性是不可配置属性时,不能删除; 但是对象不可扩展的时候,可以删除属性。

var obj = { a: 10 };
Object.defineProperty(obj, ‘b‘, {
  value: 2, configurable: false
});
var p = new Proxy(obj, {
  deleteProperty(target, prop) {
    delete target[prop];
    return true;
  }
});
delete p.b; // Uncaught TypeError: Cannot delete property ‘b‘ of #<Object>
console.log(obj);

3. ownKeys(target)

返回: 数组(数组元素必须是字符或者Symbol,其他类型报错)

作用: 拦截获取键值的操作

拦截操作: Object.getOwnPropertyNames(proxy)

Object.getOwnPropertySymbols(proxy)

Object.keys(proxy)

for...in...循环

语法:

最后取到的结果不是return的值,而是会自动过滤

const handler = {
   ownKeys(target) {       // 所有的keys;也可以是其他的数组
       return Reflect.ownKeys(target);
   }
}

示例:

var obj = { a: 10, [Symbol.for(‘foo‘)]: 2 };
Object.defineProperty(obj, ‘c‘, {
  value: 3, enumerable: false
})
var p = new Proxy(obj, {
  ownKeys(target) {
    return [...Reflect.ownKeys(target), ‘b‘, Symbol.for(‘bar‘)];
  }
});
const keys = Object.keys(p);  // [‘a‘]
// 自动过滤掉Symbol/非自身/不可遍历的属性

for(let prop in p) { // 和Object.keys()过滤性质一样,只返回target本身的可遍历属性
  console.log(‘prop-‘,prop); // prop-a
}
const ownNames = Object.getOwnPropertyNames(p);// [‘a‘, ‘c‘, ‘b‘]
// 只返回拦截器返回的非Symbol的属性,不管是不是target上的属性

const ownSymbols = Object.getOwnPropertySymbols(p);// [Symbol(foo), Symbol(bar)]
// 只返回拦截器返回的Symbol的属性,不管是不是target上的属性

const ownKeys = Reflect.ownKeys(p);// [‘a‘,‘c‘,Symbol(foo),‘b‘,Symbol(bar)]
// 返回拦截器返回的所有值

特殊情况:

1)如果某个属性是不可配置的,那么该属性在拦截器中必须被返回,否则报错

2)如果target对象是不可扩展的,那么拦截器返回的数组必须是操作的默认返回结果。

即必须是被拦截的操作的默认行为,如getOwnPropertyNames()

4. apply(target, thisArg, args)--target是函数

返回:函数执行结果

作用: 拦截proxy作为函数调用时的操作

拦截操作:proxy()

proxy.call(obj, ...args)

proxy.apply(obj, [...args])

Reflect.apply(proxy, thisArg, args)

语法:

const handler = {
  apply(target, contextThis/*上下文对象this*/, args/*参数数组*/) {
    return Reflect.apply(...arguments);
  }
}

示例:

const handler = {
  apply(target, contextThis/*上下文对象this*/, args/*参数数组*/) {
    console.log(‘---apply拦截器---‘,contextThis,‘-‘,args);
    return Reflect.apply(...arguments)+‘end‘;
  }
}
let target = function(a,b) {
  return a+b;
}
const proxy = new Proxy(target, handler);
console.log(‘proxy-->‘,proxy(5,6));
console.log(‘proxy.call-->‘,proxy.call(null, 5, 6));
console.log(‘proxy.apply-->‘,proxy.apply(null, [5,6]));
console.log(‘Reflect-->‘,Reflect.apply(proxy, null, [5,6]));

// 运行结果如下:
/*
---apply拦截器---undefined-[5,6]
proxy-->11end
---apply拦截器---null-[5,6]
proxy.call-->11end
---apply拦截器---null-[5,6]
proxy.apply-->11end
---apply拦截器---null-[5,6]
Reflect-->11end
*/

5. construct(target, args, newTarget)--target是构造函数

返回: 实例对象, 不是对象会报错

作用: 拦截new命令

拦截操作: new proxy(...args)

语法:

const handler = {
  construct(target, args, newTarget){// args是参数数组, newTarget是生成的proxy实例
    console.log(‘----拦截new----‘,args,‘-‘, newTarget === proxy);
    return Reflect.construct(target, args);// 默认行为,也可以return {a: b},只要是对象就可以
  }
}
const target = function(a,b) {};
var proxy = new Proxy(target, handler);
console.log(‘proxy type-->‘, typeof proxy);
const result = new proxy(1,2); // 触发拦截器
console.log(‘result type-->‘,typeof result)
// 运行结果如下:
proxy type-->function
----拦截new----[1,2]-true
result type-->object

2. 方法名和对象原有方法名一样

6. get(target, propKey, receiver)

返回: 返回读取的属性

作用:拦截对象属性的读取

拦截操作:proxy[propKey]或者点运算符

语法:

const handler = {
  get(target, propKey, receiver) {// receiver是proxy实例
    return Reflect.get(target, propKey);
  }
}

示例: 实现函数的链式操作

const funsObj = {
  double(n) {return n*2},
  square(n) {return n**2}
}
var pipe = (value) => {
  const callStack=[];
  return new Proxy({}, {
    get(target, propKey, receiver) {
      console.log(propKey,callStack);
      if (propKey === ‘get‘) {
        return callStack.reduce((val, fn) => {
          return fn(val);
        }, value)
      } else {
        callStack.push(funsObj[propKey]);
      }
      return receiver; //返回proxy实例才能实现链式调用
    }
  })
}
pipe(3).double.square.get; //36

get方法可以继承,但是receiver的值会是直接触发的那个对象。

const proxy= new Proxy({}, {
  get(target, propKey, receiver) {
    return receiver;
  }
})
const p = Object.create(proxy);
console.log(p.a === p); // true p.a返回receiver

特殊情况:如果对象的属性writable和configurable同时为false, 则拦截器只能返回默认行为

const target = {};
Object.defineProperty(target, ‘a‘, {
  value: 1,
  writable: false,
  configurable: false
})
const proxy = new Proxy(target, {
  get(target,propKey) {
    return 2;
    //应该return 1;不能返回其他值,否则报错
  }
})
proxy.a; // Uncaught TypeError

7.set(target,propKey, value,receiver)

作用: 拦截对象的属性赋值操作

拦截操作: proxy[propkey] = value

语法:

const proxy = new Proxy({}, {
  set(target, propKey, value,receiver) {// receiver时proxy实例
    Reflect.set(...arguments); //当Reflect.set传入的参数有receiver且属性writable=true时,会在receiver在定义属性,会触发defineProperty拦截
    return true; //!!!严格模式下必须有,否则报错;非严格模式下可以省略
  },
  defineProperty(target, propKey, propDesc) {
    console.log(‘defineProperty‘)
  }
})

set方法也可以继承,receiver也是最终调用的那个实例,和get方法一样。参照get的方法。

设置 target[propKey] = receiver;

当对象的属性writable为false时,该属性不能在拦截器中被修改;

const obj = {};
Object.defineProperty(obj, ‘foo‘, {
  value: ‘bar‘,
  writable: false,
  configurable: true,
});

const handler = {
  set: function(obj, prop, value, receiver) {
    Reflect.set(...arguments);
    return true;
  },
};
const proxy = new Proxy(obj, handler);
proxy.foo = ‘baz‘;
console.log(obj); // {foo: bar} 说明修改失败

8. defineProperty(target, propKey,PropDesc)

返回: 严格模式下必须返回true

作用:拦截定义属性或者重新定义属性操作

拦截操作: Object.defineProperty(proxy, propKey,propDesc)

Reflect.defineProperty(proxy, propKey,propDesc)

语法兼示例:

const proxy = new Proxy({}, {
  defineProperty(target, propKey, propDescriptor) {// 最后一个参数是属性描述对象
    Reflect.defineProperty(...arguments);
    return true;// !!严格模式下必须返回true,否则报错
  }
})
Object.defineProperty(proxy, ‘a‘, {value:5})

特殊情况:

如果对象是不可扩展的(preventExtensible(obj)),则不能添加属性;

如果对象的某属性writable和configurable同时为false, 则不能重新定义该属性的值;

如果上面的属性有其中一个是true,可以重新定义该属性的值。

9. getPrototypeOf(target)

返回: 返回对象或者null,否则报错

作用:拦截获取原型对象的操作

拦截操作:Object.getPrototypeOf(proxy)

proxy.__proto__

Object.isPrototypeOf(proxy)

Reflect.getPrototypeOf(proxy)

instanceof

语法:

var p = new Proxy({}, {
  getPrototypeOf(target) {
    return Reflect.getPrototypeOf(target);
  }
});

示例:

var proxy = new Proxy({}, {
  getPrototypeOf(target) {
    return {a:1}
  }
});
Object.getPrototypeOf(proxy); // {a:1} 

特殊情况:

如果目标对象是不可扩展的,那么只能返回默认的原型对象。

10.setPrototypeOf(target, proto)

返回:严格模式下返回true,否则报错;只能是布尔值

作用:拦截设置原型对象的操作

拦截操作:Object.setPrototypeOf(proxy, proto)

proxy.__proto__

Reflect.setPropertyOf(proxy,proto)

语法:

var proxy = new Proxy({}, {
  setPrototypeOf(target, proto) {
    Reflect.setPrototypeOf(...arguments);
    return true; // !!!严格模式下必须是true;否则报错
  }
})

特殊情况:

如果对象不可扩展,只能进行默认行为。不能修改。

11. isExtensible(target)---只能返回默认行为结果

返回:布尔值

作用: 拦截是否可扩展方法的调用

拦截操作: Object.isExtensible(proxy)

Reflect.isExtensible(proxy)

语法:

const proxy = new Proxy({}, {
  isExtensible(target) {
    return Reflect.isExtensible(target); //!!!返回结果只能是这个;即proxy和target的可扩展性必须是一致的
  }
})

12. preventExtensible(target)--遵循默认行为

返回:严格模式下返回true,否则报错;

拦截操作:Object.preventExtensible(proxy)

Reflect.preventExtentsible(proxy)

语法:

const proxy = new Proxy({}, {
  preventExtensions(target) {
    Reflect.preventExtensions(target); //严格模式下,必须存在
    return true; // 严格模式下必须存在
  }
})

13. getOwnPropertyDescriptor(target, propKey)

返回: 对象或者undefined

拦截操作: Object.getOwnPropertyDescriptor(proxy)

Reflect.getOwnPropertyDescriptor(proxy)

语法:

const proxy = new Proxy({}, {
  getOwnPropertyDescriptor(target, propKey) {
    return Reflect.getOwnPropertyDescriptor(target,propKey);
  }
})

三. 静态方法-Proxy.revocable()

作用: 返回一个可取消的proxy实例

语法:

const {proxy, revoke} = Proxy.revocable(target,handler);
// proxy是实例
// revoke是个方法;直接调用后取消proxy实例

示例:

const {proxy, revoke} = Proxy.revocable({},{});
proxy.a=5;
revoke();
proxy.a // Uncaught TypeError: Cannot perform ‘get‘ on a proxy that has been revoked

应用:

用于一个只能访问一次的代理器

四. this指向

拦截器方法中的this指向proxy实例

五.Proxy应用

观察者模式:通过拦截对象的赋值操作,实时调用观察函数。

原文地址:https://www.cnblogs.com/lyraLee/p/11774482.html

时间: 2024-09-29 00:51:28

Proxy详解的相关文章

Java中InvocationHandler接口中第一个参数proxy详解

java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心: 1.InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序:在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法. 看下官方文档对InvocationHandler接口的描述: {@code Invocat

jQuery.proxy() 详解

jQuery.proxy(),接受一个函数,然后返回一个新函数,并且这个新函数始终保持了特定的上下文(context )语境. jQuery.proxy( function, context ) function将要改变上下文语境的函数. context函数的上下文语境(`this`)会被设置成这个 object 对象. jQuery.proxy( context, name ) context函数的上下文语境会被设置成这个 object 对象. name将要改变上下文语境的函数名(这个函数必须

设计模式 - 代理模式(proxy pattern) 未使用代理模式 详解

代理模式(proxy pattern) 未使用代理模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 部分代码参考: http://blog.csdn.net/caroline_wendy/article/details/37698747 如果需要监控(monitor)类的某些状态, 则需要编写一个监控类, 并同过监控类进行监控. 但仅仅局限于本地, 如果需要远程监控, 则需要使用代理模式(proxy pattern). 具体方法: 1. 类中需要提供

CentOS 7以yum方式安装zabbix3.2及配置文件详解

一.zabbix简介与环境准备 简介详见 ---> zabbix简介 环境准备: CentOS 7(node7):zabbix-server,web,mysql,agent mariadb:5.5.50 zabbix组件:3.2.1 apache:2.4.6 二.安装与配置 1.安装数据库(mariadb),可直接yum安装 [[email protected] ~]# vim /etc/yum.repos.d/MariaDB.repo [mariadb]  name = MariaDB  ba

[gitbook] Android框架分析系列之Android Binder详解

请支持作者原创: https://mr-cao.gitbooks.io/android/content/android-binder.html Android Binder详解 Table of Contents 1. binder简介 2. binder的实现 2.1. IBinder类简介 2.2. IInterface类简介 2.3. BpBinder和BBinder简介 2.4. ProcessState和IPCThreadState简介 2.5. ServiceManager简介 2.

高性能Web服务之nginx应用详解

一.Nginx特性 * *模块化,目前只能将模块编译进Nginx,暂时不支持动态装卸模块.(httpd优势) * *可靠性,一个主进程(master)控制多个工作进程(worker),工作进程响应用户多个请求(httpd劣势) * *低内存消耗,(httpd劣势) * *支持热部署,(httpd相同) * *支持事件驱动I/O,AI/O,支持mmap(httpd2.4才算支持event,劣势) 二.Nginx基本架构 Nginx由一个master进程生成多个worker进程,每个worker进程

Java--设计模式详解(23种)

一.设计模式的理解 刚开始“不懂”为什么要把很简单的东西搞得那么复杂.后来随着软件开发经验的增加才开始明白我所看到的“复杂”恰恰就是设计模式的精髓所在,我所理解的“简单”就是一把钥匙开一把锁的模式,目的仅仅是着眼于解决现在的问题,而设计模式的“复杂”就在于它是要构造一个“万能钥匙”,目的是提出一种对所有锁的开锁方案.在真正理解设计模式之前我一直在编写“简单”的代码.这个“简单”不是功能的简单,而是设计的简单.简单的设计意味着缺少灵活性,代码很钢硬,只在这个项目里有用,拿到其它的项目中就是垃圾,我

HTTP协议详解(真的很经典)

引言 HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展.目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且HTTP-NG(Next Generation of HTTP)的建议已经提出.HTTP协议的主要特点可概括如下:1.支持客户/服务器模式.2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径.请求方法常用的有GET.HEAD.POS

HTTP协议详解 (转)

转自 http://blog.csdn.net/gueter/article/details/1524447 引言 HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展.目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且HTTP-NG(Next Generation of HTTP)的建议已经提出.HTTP协议的主要特点可概括如下:1.支持客户/服务