es6学习笔记11--Proxy和Reflect
Proxy概述
Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
ES6原生提供Proxy构造函数,用来生成Proxy实例。
var proxy = new Proxy(target, handler);
Proxy对象的所有用法,都是上面这种形式,不同的只是handler
参数的写法。其中,new Proxy()
表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler
参数也是一个对象,用来定制拦截行为。
下面是另一个拦截读取属性行为的例子。
var proxy = new Proxy({}, {get: function(target, property) {return 35;} });proxy.time // 35 proxy.name // 35 proxy.title // 35
上面代码中,作为构造函数,Proxy接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy的介入,操作原来要访问的就是这个对象;第二个参数是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。比如,上面代码中,配置对象有一个get
方法,用来拦截对目标对象属性的访问请求。get
方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35
,所以访问任何属性都得到35
。
注意,要使得Proxy起作用,必须针对Proxy实例(上例是proxy对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。
如果handler
没有设置任何拦截,那就等同于直接通向原对象。
var target = {}; var handler = {}; var proxy = new Proxy(target, handler); proxy.a = 'b'; target.a // "b"
上面代码中,handler
是一个空对象,没有任何拦截效果,访问handeler
就等同于访问target
。
同一个拦截器函数,可以设置拦截多个操作。
var handler = {get: function(target, name) {if (name === 'prototype') return Object.prototype;return 'Hello, ' + name;},apply: function(target, thisBinding, args) { return args[0]; },construct: function(target, args) { return args[1]; } };var fproxy = new Proxy(function(x, y) {return x + y; }, handler);fproxy(1,2); // 1 new fproxy(1,2); // 2 fproxy.prototype; // Object.prototype fproxy.foo; // 'Hello, foo'
下面是Proxy支持的拦截操作一览。
对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
(1)get(target, propKey, receiver)
拦截对象属性的读取,比如proxy.foo
和proxy['foo']
,返回类型不限。最后一个参数receiver
可选,当target
对象设置了propKey
属性的get
函数时,receiver
对象会绑定get
函数的this
对象。
(2)set(target, propKey, value, receiver)
拦截对象属性的设置,比如proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值。
(3)has(target, propKey)
拦截propKey in proxy
的操作,返回一个布尔值。
has
方法可以隐藏某些属性,不被in
操作符发现。
(4)deleteProperty(target, propKey)
拦截delete proxy[propKey]
的操作,返回一个布尔值。
(5)enumerate(target)
拦截for (var x in proxy)
,返回一个遍历器。
注意与Proxy对象的has
方法区分,后者用来拦截in
操作符,对for...in
循环无效。
(6)ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
,返回一个数组。该方法返回对象所有自身的属性,而Object.keys()
仅返回对象可遍历的属性。
(7)getOwnPropertyDescriptor(target, propKey)
拦截Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。
(8)defineProperty(target, propKey, propDesc)
拦截Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。
(9)preventExtensions(target)
拦截Object.preventExtensions(proxy)
,返回一个布尔值。
(10)getPrototypeOf(target)
拦截Object.getPrototypeOf(proxy)
,返回一个对象。
(11)isExtensible(target)
拦截Object.isExtensible(proxy)
,返回一个布尔值。
(12)setPrototypeOf(target, proto)
拦截Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。
如果目标对象是函数,那么还有两种额外操作可以拦截。
(13)apply(target, object, args)
拦截Proxy实例作为函数调用的操作,比如proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。
apply
方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this
)和目标对象的参数数组。
(14)construct(target, args, proxy)
拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)
。
construct
方法用于拦截new
命令。
Reflect概述
Reflect
对象与Proxy
对象一样,也是ES6为了操作对象而提供的新API。Reflect
对象的设计目的有这样几个。
(1) 将Object
对象的一些明显属于语言内部的方法(比如Object.defineProperty
),放到Reflect
对象上。现阶段,某些方法同时在Object
和Reflect
对象上部署,未来的新方法将只部署在Reflect
对象上。
(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)
在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)
则会返回false
。
// 老写法 try {Object.defineProperty(target, property, attributes);// success } catch (e) {// failure }// 新写法 if (Reflect.defineProperty(target, property, attributes)) {// success } else {// failure }
(3) 让Object
操作都变成函数行为。某些Object
操作是命令式,比如name in obj
和delete obj[name]
,而Reflect.has(obj, name)
和Reflect.deleteProperty(obj, name)
让它们变成了函数行为。
// 老写法 'assign' in Object // true// 新写法 Reflect.has(Object, 'assign') // true
(4)Reflect
对象的方法与Proxy
对象的方法一一对应,只要是Proxy
对象的方法,就能在Reflect
对象上找到对应的方法。这就让Proxy
对象可以方便地调用对应的Reflect
方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy
怎么修改默认行为,你总可以在Reflect
上获取默认行为。
Reflect对象的方法
Reflect
对象的方法清单如下,共14个。
- Reflect.apply(target,thisArg,args)
- Reflect.construct(target,args)
- Reflect.get(target,name,receiver)
- Reflect.set(target,name,value,receiver)
- Reflect.defineProperty(target,name,desc)
- Reflect.deleteProperty(target,name)
- Reflect.has(target,name)
- Reflect.ownKeys(target)
- Reflect.enumerate(target)
- Reflect.isExtensible(target)
- Reflect.preventExtensions(target)
- Reflect.getOwnPropertyDescriptor(target, name)
- Reflect.getPrototypeOf(target)
- Reflect.setPrototypeOf(target, prototype)
上面这些方法的作用,大部分与Object
对象的同名方法的作用都是相同的,而且它与Proxy
对象的方法是一一对应的。下面是对其中几个方法的解释。
(1)Reflect.get(target, name, receiver)
查找并返回target
对象的name
属性,如果没有该属性,则返回undefined
。
(2)Reflect.set(target, name, value, receiver)
设置target
对象的name
属性等于value
。如果name
属性设置了赋值函数,则赋值函数的this
绑定receiver
。
(3)Reflect.has(obj, name)
等同于name in obj
。
(4)Reflect.deleteProperty(obj, name)
等同于delete obj[name]
。
(5)Reflect.construct(target, args)
等同于new target(...args)
,这提供了一种不使用new
,来调用构造函数的方法。
(6)Reflect.getPrototypeOf(obj)
读取对象的__proto__
属性,对应Object.getPrototypeOf(obj)
。
(7)Reflect.setPrototypeOf(obj, newProto)
设置对象的__proto__
属性,对应Object.setPrototypeOf(obj, newProto)
。
(8)Reflect.apply(fun,thisArg,args)
等同于Function.prototype.apply.call(fun,thisArg,args)
。一般来说,如果要绑定一个函数的this对象,可以这样写fn.apply(obj, args)
,但是如果函数定义了自己的apply
方法,就只能写成Function.prototype.apply.call(fn, obj, args)
,采用Reflect对象可以简化这种操作。
另外,需要注意的是,Reflect.set()
、Reflect.defineProperty()
、Reflect.freeze()
、Reflect.seal()
和Reflect.preventExtensions()
返回一个布尔值,表示操作是否成功。它们对应的Object方法,失败时都会抛出错误。
// 失败时抛出错误 Object.defineProperty(obj, name, desc); // 失败时返回false Reflect.defineProperty(obj, name, desc);
上面代码中,Reflect.defineProperty
方法的作用与Object.defineProperty
是一样的,都是为对象定义一个属性。但是,Reflect.defineProperty
方法失败时,不会抛出错误,只会返回false
。
es6学习笔记11--Proxy和Reflect相关推荐
- 从零写一个具有IOC-AOP-MVC功能的框架---学习笔记---11. MVC功能之http请求处理器的编写---简易框架最后一公里!
从零写一个具有IOC-AOP-MVC功能的框架-学习笔记 专栏往期文章链接: IOC功能相关章节: 从零写一个具有IOC-AOP-MVC功能的框架-学习笔记-01.项目初始化 从零写一个具有IOC-A ...
- SpringMVC:学习笔记(11)——依赖注入与@Autowired
SpringMVC:学习笔记(11)--依赖注入与@Autowired 使用@Autowired 从Spring2.5开始,它引入了一种全新的依赖注入方式,即通过@Autowired注解.这个注解允许 ...
- es6学习笔记-字符串的扩展_v1.0_byKL
es6学习笔记-字符串的扩展_v1.0 字符的Unicode表示法 JavaScript 允许使用uxxxx的形式表示一个字符,但在 ES6 之前,单个码点仅支持u0000到uFFFF,超出该范围的必 ...
- Hadoop学习笔记—11.MapReduce中的排序和分组
Hadoop学习笔记-11.MapReduce中的排序和分组 一.写在之前的 1.1 回顾Map阶段四大步骤 首先,我们回顾一下在MapReduce中,排序和分组在哪里被执行: 从上图中可以清楚地看出 ...
- 设计模式学习笔记——代理(Proxy)模式
设计模式学习笔记--代理(Proxy)模式 @(设计模式)[设计模式, 代理模式, proxy] 设计模式学习笔记代理Proxy模式 基本介绍 代理案例 类图 实现代码 Printable接口 Pri ...
- ES6学习笔记(五):轻松了解ES6的内置扩展对象
前面分享了四篇有关ES6相关的技术,如想了解更多,可以查看以下连接 <ES6学习笔记(一):轻松搞懂面向对象编程.类和对象> <ES6学习笔记(二):教你玩转类的继承和类的对象> ...
- ES6学习笔记(三):教你用js面向对象思维来实现 tab栏增删改查功能
前两篇文章主要介绍了类和对象.类的继承,如果想了解更多理论请查阅<ES6学习笔记(一):轻松搞懂面向对象编程.类和对象>.<ES6学习笔记(二):教你玩转类的继承和类的对象>, ...
- ES6学习笔记04:Set与Map
ES6学习笔记04:Set与Map JS原有两种数据结构:Array与Object,ES6新增两种数据结构:Set与Map 一.Set数据结构 Set类似于数组,但是成员值不允许重复,因此主要用于数据 ...
- ES6学习笔记03:变量的解构赋值
ES6学习笔记03:变量的解构赋值 如果想从复杂数据结构(数组.对象)中获取某一个数据,可能需要大量的遍历操作才能完成.通过解构赋值,这一过程可以得到简化. 1.字符串的解构赋值 其实,Python也 ...
- ES6学习笔记02:let 与 const
ES6学习笔记02:let 与 const 用var声明的变量会造成全局污染,于是就产生了新的声明方式. 1.let 用let声明变量,必须先声明后使用. 在for循环头里用let定义循环变量i,那么 ...
最新文章
- create-react-app my-app 报错解决方法
- 解析广泛应用于NLP的自注意力机制(附论文、源码)
- 国内整车厂“造芯”还缺什么?
- 算法导论吃透后的水平_初学算法,你应该这么玩
- app网站换服务器,app切换服务器
- UPYUN CDN 高可用架构实践
- 自定义checkbox样式
- 2754. [SCOI2012]喵星球上的点名【后缀数组】
- Docker 以 docker 方式运行 jenkins
- android sdk system images,ADT中使用Android SDK Manager安装X86的System Image | 在路上
- Linux命令解释之grep
- 与孩子一起学编程07章
- 搞深度学习框架的那帮人,不是疯子,就是骗子
- 电机不动 米兔机器人_深度揭秘米兔积木机器人八大黑科技
- 学生想学信息学奥赛: DEV-C++的安装与介绍
- 海南自贸区电信行业环境分析
- PD3.1 140W双C快充解决方案
- 美国国土安全部2.5万雇员隐私信息遭黑客窃取
- 小型固定翼无人机集群仿真演示平台
- swagger 源代码_我们如何使用swagger代码生成器从Angular 4更新到Angular 5
热门文章
- git查看相对于最新的push改动内容
- linux下批量修改文件名的方法
- tfrecord可以以列表的形式传入多个路径
- Shorten command line 解决方案
- 自编码器参数是否需要相称呢
- 测试的时候数据库外键导致死锁_Oracle外键不加索引会引起死锁问题
- linux java查看进程命令_linux一些查看进程情况的命令
- python列表、元组、字典和集合的算法时间_27.Python列表(list)、元组(tuple)、字典(dict)和集合(set)详解...
- php取消mysql警告_mysql登录警告问题的解决方法
- 信息系统项目管理师考试公式都在这里了