1. 概述

  • 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty ),放到 Reflect 对象上。

  • 修改某些 Object 方法的返回结果,让其变得更合理。比如, Object.defineProperty(obj, name, desc) 在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj, name, desc) 则会返回 false 。

  • 让 Object 操作都变成函数行为。某些 Object 操作是命令式,比如 name in obj 和 delete obj[name] ,而 Reflect.has(obj, name) 和 Reflect.deleteProperty(obj, name) 让它们变成了函数行为。

  • Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。

// 老写法
Function.prototype.apply.call(Math.floor, undefined, [8.75]) // // 新写法
Reflect.apply(Math.floor, undefined, [20.5]) //

2. 静态方法

Reflect对象一共哟13个静态方法:

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.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)

1.Reflect.get(target, name, receiver)

Reflect.get 方法查找并返回 target 对象的 name 属性,如果没有该属性,则返回 undefined 。

var obj = {
name: 'houfee',
age: 24,
get func() {
return this.name + this.age
}
}
console.log(Reflect.get(obj, 'name')) // houfee
console.log(Reflect.get(obj, 'age')) //
console.log(Reflect.get(obj, 'func')) // houfee24

如果 name 属性部署了读取函数(getter),则读取函数的 this 绑定 receiver 。

改变this指向

var obj = {
name: 'houfee',
age: 24,
get func() {
return this.name + this.age
}
}
var obj2 = {
name: 'houyue',
age: 14,
}
console.log(Reflect.get(obj, 'name')) // houfee
console.log(Reflect.get(obj, 'age')) //
console.log(Reflect.get(obj, 'func', obj2)) // houyue14

如果第一个参数不是对象, Reflect.get 方法会报错。

2.Reflect.set(target, name, value, receiver)

Reflect.set 方法设置 target 对象的 name 属性等于 value 。

var obj = {
name: 'houfee',
set func(value) {
return this.name = value
}
}
var obj2 = {
name: 'houyue'
}
console.log(Reflect.set(obj, 'name', 'houyue')) // true
console.log(Reflect.set(obj, 'func', 'houyue')) // true
console.log(obj) // {name: "houyue", age: 24}

如果 name 属性设置了赋值函数,则赋值函数的 this 绑定 receiver 。

var obj = {
name: 'houfee',
set func(value) {
return this.name = value
}
}
var obj2 = {
name: 'zhangsan'
}
console.log(Reflect.set(obj, 'func', 'houyue', obj2)) // true
console.log(obj) // {name: "houfee"}
console.log(obj2) // {name: "houyue"}

注意,如果 Proxy 对象和 Reflect 对象联合使用,前者拦截赋值操作,后者完成赋值的默认行为,而且传入了 receiver ,那么 Reflect.set 会触发 Proxy.defineProperty 拦截。

ProxyReflect 配合使用:

let p = {
a: 'a'
};
let handler = {
set(target, key, value, receiver) {
console.log('set');
Reflect.set(target, key, value, receiver)
},
defineProperty(target, key, attribute) {
console.log('defineProperty');
Reflect.defineProperty(target, key, attribute);
}
};
let obj = new Proxy(p, handler);
obj.a = 'A';
// set
// defineProperty

上面代码中, Proxy.set 拦截里面使用了 Reflect.set ,而且传入了 receiver ,导致触发 Proxy.defineProperty 拦截。这是因为 Proxy.set 的receiver 参数总是指向当前的 Proxy 实例(即上例的 obj ),而 Reflect.set 一旦传入 receiver ,就会将属性赋值到 receiver 上面(即 obj ),导致触发 defineProperty 拦截。如果 Reflect.set 没有传入 receiver ,那么就不会触发 defineProperty 拦截。

let p = {
a: 'a'
};
let handler = {
set(target, key, value, receiver) {
console.log('set');
Reflect.set(target, key, value)
},
defineProperty(target, key, attribute) {
console.log('defineProperty');
Reflect.defineProperty(target, key, attribute);
}
};
let obj = new Proxy(p, handler);
obj.a = 'A'; // set

如果第一个参数不是对象, Reflect.set 会报错。

3.Reflect.has(obj, name)

Reflect.has 方法对应 name in obj 里面的 in 运算符。

var myObject = {
foo: 1,
};
// 旧写法
console.log('foo' in myObject); // true
// 新写法
console.log(Reflect.has(myObject, 'foo')); // true

该方法返回一个布尔值。如果删除成功,或者被删除的属性不存在,返回 true ;删除失败,被删除的属性依然存在,返回 false 。

4. Reflect.deleteProperty(obj, name)

Reflect.deleteProperty 方法等同于 delete obj[name] ,用于删除对象的属性。

const myObj = {
foo: 'bar'
};
// 旧写法
delete myObj.foo;
// 新写法
Reflect.deleteProperty(myObj, 'foo');

该方法返回一个布尔值。如果删除成功,或者被删除的属性不存在,返回 true ;删除失败,被删除的属性依然存在,返回 false 。

5. Reflect.construct(target, args)

Reflect.construct 方法等同于 new target(...args) ,这提供了一种不使用 new ,来调用构造函数的方法。

function Func(name) {
this.name = name
}
// new 的写法
const instance = new Func('张三')
// Reflect.construct 的写法
const instance = Reflect.construct(Func, ['张三'])

6. Reflect.getPrototypeOf(obj)

Reflect.getPrototypeOf 方法用于读取对象的 proto 属性,对应 Object.getPrototypeOf(obj) 。

function Func(name) {
this.name = name
}
// new 的写法
const instance = new Func('张三')
Object.getPrototypeOf(instance) === Func.prototype
// Reflect.construct 的写法
const instance = Reflect.construct(Func, ['张三'])
Reflect.getPrototypeOf(instance) === Func.prototype

Reflect.getPrototypeOf 和 Object.getPrototypeOf 的一个区别是:

如果参数不是对象, Object.getPrototypeOf 会将这个参数转为对象,然后再运行,而 Reflect.getPrototypeOf 会报错。

7. Reflect.setPrototypeOf(obj, newProto)

Reflect.setPrototypeOf 方法用于设置对象的 proto 属性,返回第一个参数对象,对应bject.setPrototypeOf(obj, newProto) 。

function Func(name) {
this.name = name
}
function Age(age) {
this.age = age
}
// new 的写法 将 Age 挂载到 instance 实例的原型上
var instance = new Func('张三')
Object.setPrototypeOf(instance, Age.prototype)
// Reflect.construct 的写法
var instance = Reflect.construct(Func, ['张三'])
Reflect.setPrototypeOf(instance, Age.prototype)

如果第一个参数不是对象, Object.setPrototypeOf 会返回第一个参数本身,而 Reflect.setPrototypeOf 会报错。

如果第一个参数是 undefined 或 null , Object.setPrototypeOf 和 Reflect.setPrototypeOf 都会报错。

8. Reflect.apply(func, thisArg, args)

Reflect.apply 方法等同于 Function.prototype.apply.call(func, thisArg, args) ,用于绑定 this 对象后执行给定函数。 一般来说,如果要绑定一个函数的 this 对象,可以这样写 fn.apply(obj, args) ,但是如果函数定义了自己的 apply 方法,就只能写成 Function.prototype.apply.call(fn, obj, args) ,采用 Reflect 对象可以简化这种操作。

const ages = [11, 33, 12, 54, 18, 96];
// 旧写法
const youngest = Math.min.apply(Math, ages);
const oldest = Math.max.apply(Math, ages);
const type = Object.prototype.toString.call(youngest);
// 新写法
const youngest = Reflect.apply(Math.min, Math, ages);
const oldest = Reflect.apply(Math.max, Math, ages);
const type = Reflect.apply(Object.prototype.toString, youngest, []);

9. Reflect.defineProperty(target, propertyKey, attributes)

Reflect.defineProperty 方法基本等同于 Object.defineProperty ,用来为对象定义属性。未来,后者会被逐渐废除,请从现在开始就使用 Reflect.defineProperty 代替它。

function MyDate() {
/*…*/
}
// 旧写法
Object.defineProperty(MyDate, 'now', {
value: () => Date.now()
});
// 新写法
Reflect.defineProperty(MyDate, 'now', {
value: () => Date.now()
});

如果 Reflect.defineProperty 的第一个参数不是对象,就会抛出错误,比如 Reflect.defineProperty(1, 'foo') 。

10. Reflect.getOwnPropertyDescriptor(target, propertyKey)

Reflect.getOwnPropertyDescriptor 基本等同于 Object.getOwnPropertyDescriptor ,用于得到指定属性的描述对象,将来会替代掉后者。

var myObject = {};
Object.defineProperty(myObject, 'hidden', {
value: true,
enumerable: false,
});
// 旧写法
var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden');
// 新写法
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');

Reflect.getOwnPropertyDescriptor 和 Object.getOwnPropertyDescriptor 的一个区别是,如果第一个参数不是对象, Object.getOwnPropertyDescriptor(1, 'foo') 不报错,返回 undefined ,

而 Reflect.getOwnPropertyDescriptor(1, 'foo') 会抛出错误,表示参数非法。

11.Reflect.isExtensible (target)

Reflect.isExtensible 方法对应 Object.isExtensible ,返回一个布尔值,表示当前对象是否可扩展。

const myObject = {};
// 旧写法
Object.isExtensible(myObject) // true
// 新写法
Reflect.isExtensible(myObject) // true

如果参数不是对象, Object.isExtensible 会返回 false ,因为非对象本来就是不可扩展的,而 Reflect.isExtensible 会报错。

12. Reflect.preventExtensions(target)

Reflect.preventExtensions 对应 Object.preventExtensions 方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。

var myObject = {};
// 旧写法
Object.preventExtensions(myObject) // Object {}
// 新写法
Reflect.preventExtensions(myObject) // true

如果参数不是对象, Object.preventExtensions 在 ES5 环境报错,在 ES6 环境返回传入的参数,而 Reflect.preventExtensions 会报错。

13. Reflect.ownKeys (target)

Reflect.ownKeys 方法用于返回对象的所有属性,基本等同于 Object.getOwnPropertyNames 与 Object.getOwnPropertySymbols 之和。

var myObject = {
foo: 1,
bar: 2,
[Symbol.for('baz')]: 3,
[Symbol.for('bing')]: 4,
};
// 旧写法
Object.getOwnPropertyNames(myObject)
// ['foo', 'bar']
Object.getOwnPropertySymbols(myObject)
//[Symbol(baz), Symbol(bing)]
Reflect.ownKeys(myObject)
// ['foo', 'bar', Symbol(baz), Symbol(bing)]

3. 实例:使用 Proxy 实现观察者模式

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。

const person = observable({
name: '张三',
age: 20
}); function print() {
console.log(`${person.name}, ${person.age}`)
}
observe(print);
person.name = '李四'; // 输出 // 李四, 20

上面代码中,数据对象 person 是观察目标,函数 print 是观察者。一旦数据对象发生变化, print 就会自动执行。

下面,使用 Proxy 写一个观察者模式的最简单实现,即实现 observable 和 observe 这两个函数。思路是 observable 函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数。

const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {
set
}); function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;
}

上面代码中,先定义了一个 Set 集合,所有观察者函数都放进这个集合。然后, observable 函数返回原始对象的代理,拦截赋值操作。拦截函数 set 之中,会自动执行所有观察者。

ES6 之 Reflect 的方法总结的更多相关文章

  1. ES6使用的一些方法

    查找数组中符合条件的所有记录 var list=[ {id:1,name:"张三"}, {id:2,name:"李四"}, {id:3,name:"王 ...

  2. ES6中Object.assign() 方法

    ES6中Object.assign() 方法 1. 对象合并Object.assign 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象上.如下代码演示: var targ ...

  3. JavaScript(ES6之前)数组方法总结

    一.数组的创建 1.使用 Array 构造函数 var arr1 = new Array(); // 创建一个空数组 var arr2 = new Array(20); // 创建一个包含20项的数组 ...

  4. 认识一下ES6的Reflect和Proxy

    Reflect Reflect要替代Object的很多方法, 将Object对象一些明显属于言内部的方法放到了Reflect对象上,有13个方法 Reflect.apply(target, thisA ...

  5. ES6数组对象新增方法

    1. Array.from() Array.from方法用于将两类对象转为真正的数组:类数组的对象( array-like object )和可遍历( iterable )的对象(包括 ES6 新增的 ...

  6. ES6之数组扩展方法【一】(相当好用)

    form 转化为真正的数组 先说一下使用场景,在Js中,我们要经常操作DOM,比如获取全部页面的input标签,并且找到类型为button的元素,然后给这个按钮注册一个点击事件,我们可能会这样操作: ...

  7. ES6之字符串扩展方法(常用)

    es6这个String对象倒是扩展了不少方法,但是很多都是跟字符编码相关,个人选了几个感觉比较常用的方法: includes 搜索字符的神器 还记得我们之前如何判断某个字符串对象是否包含特地字符的吗? ...

  8. ES6数组及数组方法

    ES6数组可以支持下面的几种写法: (1)var [a,b,c] = [1,2,3]; (2)var [a,[[b],c]] = [1,[[2],3]]; (3)let [x,,y] = [1,2,3 ...

  9. ES6(es2015)新增实用方法汇总

    Array 1.map() [1,2,3,4].map(function(item, index, array){ return  item * 2; }) 对数组中的每一项执行一次回调函数,三个参数 ...

随机推荐

  1. python之函数名称空间,作用域,嵌套函数

    目录 嵌套函数 定义 名称空间的三大类(只存变量名) 名称空间加载顺序 变量名的查找顺序为 作用域 嵌套函数 定义 函数内部定义的函数,无法在函数外部使用内部定义的函数. def f1(): def ...

  2. SVN commit,update用法

    https://blog.csdn.net/studyvcmfc/article/details/4528896

  3. 吴裕雄--天生自然python机器学习实战:K-NN算法约会网站好友喜好预测以及手写数字预测分类实验

    实验设备与软件环境 硬件环境:内存ddr3 4G及以上的x86架构主机一部 系统环境:windows 软件环境:Anaconda2(64位),python3.5,jupyter 内核版本:window ...

  4. phpQuery的使用

    前言 为什么使用phpQuery phpQuery是基于php5新添加的DOMDocument.而DOMDocument则是专门用来处理html/xml.它提供了强大的xpath选择器及其他很多htm ...

  5. MariaDB——日志文件

    数据库各类日志  查询日志:   记录每一条sql语句,建议不开启,因为如果访问量过大,会占用相当大的资源,影响数据库的性能.    vim /etc/my.cnf.d/server.cnf    g ...

  6. PLSQL Developer配置Oralce11g连接

    您的位置:首页 → 资讯教程 → 编程开发 → PLSQL Developer配置Oralce11g连接 PLSQL Developer配置Oralce11g连接 时间:2015/2/3 8:36:2 ...

  7. html5和html4.0.1的<html>标记的区别

    html5新增了一个 manifest属性,定义了缓存信息. html5中废弃了xmlns属性,这个属性在html转换成xhtml是非常有用的,但是html5中并不需要,因此不用写. 但是如果非要写那 ...

  8. MySQL 加密 压缩

    许多MySQL加密和压缩函数返回结果可能包含任意字节值的字符串,如果要存储这些结果,请使用VARBINARY或BLOB二进制字符串数据类型.这将避免使用非二进制字符串数据类型(CHAR, VARCHA ...

  9. Echarts学习宝典

    1,可以使用百度图说中的图表代码复制到option中,还可在百度图说中以可视化的方式编辑数据,调整参数和编辑样式.(也可借助百度图说更改部分样式) 2,借助网站 https://echarts.bai ...

  10. swoole之创建子进程

    一.代码 <?php /** * 进程就是正在运行的程序的一个实例 * 比如,在某个终端中执行一个PHP脚本,可以认为就是开启了一个进程,会有对应的进程id(pid) * * swoole进程与 ...