JavaScript中的Reflect对象详解(ES6新特性)


Posted in Javascript onJuly 22, 2016

Reflect介绍:

Reflect这个对象在我的node(v4.4.3)中还没有实现, babel(6.7.7)也没有实现 ,新版本的chrome是支持的, ff比较早就支持Proxy和Reflect了,要让node支持Reflect可以安装harmony-reflect ;

Reflect不是构造函数, 要使用的时候直接通过Reflect.method()调用, Reflect有的方法和Proxy差不多, 而且多数Reflect方法原生的Object已经重新实现了。

什么要使用Reflect

这里列举几个为什么要使用Reflect的原因, 译文地址:Reflect , 大概翻译了一遍:

1:更加有用的返回值: Reflect有一些方法和ES5中Object方法一样样的, 比如: Reflect.getOwnPropertyDescriptor和Reflect.defineProperty, 不过, Object.defineProperty(obj, name, desc)执行成功会返回obj, 以及其它原因导致的错误, Reflect.defineProperty只会返回false或者true来表示对象的属性是否设置上了, 如下代码可以重构:

try {
Object.defineProperty(obj, name, desc);
// property defined successfully
} catch (e) {
// possible failure (and might accidentally catch the wrong exception)
}

重构成这样:

if (Reflect.defineProperty(obj, name, desc)) {
// success
} else {
// failure
}

其余的方法, 比如Relect.set , Reflect.deleteProperty, Reflect.preventExtensions, Reflect.setPrototypeOf, 都可以进行重构;

2:函数操作, 如果要判断一个obj有定义或者继承了属性name, 在ES5中这样判断:name in obj ;

或者删除一个属性 :delete obj[name], 虽然这些很好用, 很简短, 很明确, 但是要使用的时候也要封装成一个类;

有了Reflect, 它帮你封装好了, Reflect.has(obj, name), Reflect.deleteProperty(obj, name);

3:更加可靠的函数式执行方式: 在ES中, 要执行一个函数f,并给它传一组参数args, 还要绑定this的话, 要这么写:

f.apply(obj, args)

但是f的apply可能被重新定义成用户自己的apply了,所以还是这样写比较靠谱:

Function.prototype.apply.call(f, obj, args)

上面这段代码太长了, 而且不好懂, 有了Reflect, 我们可以更短更简洁明了:

Reflect.apply(f, obj, args)

4:可变参数形式的构造函数: 想象一下, 你想通过不确定长度的参数实例化一个构造函数, 在ES5中, 我们可以使用扩展符号, 可以这么写:

var obj = new F(...args)

不过在ES5中, 不支持扩展符啊, 所以, 我们只能用F.apply,或者F.call的方式传不同的参数, 可惜F是一个构造函数, 这个就坑爹了, 不过有了Reflect,

我们在ES5中能够这么写:

var obj = Reflect.construct(F, args)

5:控制访问器或者读取器的this: 在ES5中, 想要读取一个元素的属性或者设置属性要这样:

var name = ... // get property name as a string
obj[name] // generic property lookup
obj[name] = value // generic property

updateReflect.get和Reflect.set方法允许我们做同样的事情, 而且他增加了一个额外的参数reciver, 允许我们设置对象的setter和getter的上下this:

var name = ... // get property name as a string
Reflect.get(obj, name, wrapper) // if obj[name] is an accessor, it gets run with `this === wrapper`
Reflect.set(obj, name, value, wrapper)访问器中不想使用自己的方法,而是想要重定向this到wrapper:
var obj = {
set foo(value) { return this.bar(); },
bar: function() {
alert(1);
}
};
var wrapper = {
bar : function() {
console.log("wrapper");
}
}
Reflect.set(obj, "foo", "value", wrapper);

6:避免直接访问 __proto__ :

ES5提供了 Object.getPrototypeOf(obj),去访问对象的原型, ES6提供也提供了

Reflect.getPrototypeOf(obj) 和 Reflect.setPrototypeOf(obj, newProto), 这个是新的方法去访问和设置对象的原型:

Reflect.apply的使用

Reflect.apply其实就是ES5中的 Function.prototype.apply() 替身, 执行Reflect.apply需要三个参数

第一个参数为: 需要执行的函数;

第二个参数为: 需要执行函数的上下文this;

第三个参数为: 是一个数组或者伪数组, 会作为执行函数的参数;

<script>
let fn = function() {
this.attr = [0,1,2,3];
};
let obj = {};
Reflect.apply(fn, obj, [])
console.log(obj); 
</script>
Reflect.apply的DEMO:
<script>
Reflect.apply(Math.floor, undefined, [1.75]); // 输出:1;
Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]); // 输出:"hello"
Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index; //输出: 4
Reflect.apply("".charAt, "ponies", [3]); // 输出:"i"
</script>Reflect可以与Proxy联合使用:
{
var Fn = function(){
};
Fn.prototype.run = function() {
console.log( "runs out" );
};
var ProxyFn = new Proxy(Fn, {
construct (target ,arugments) {
console.log("proxy constructor");
var obj = new target(...arugments);
//Reflect.apply的使用方法;
Reflect.apply(target.prototype.run, obj, arugments);
return obj;
}
});
new ProxyFn (); //会先输出: "proxy constructor" ; 再输出: runs out
}Reflect.construct()的使用:
Reflect.construct其实就是实例化构造函数,通过传参形式的实现, 执行的方式不同, 效果其实一样, construct的第一个参数为构造函数, 第二个参数由参数组成的数组或者伪数组, 基本的使用方法为:
var Fn = function(arg) {
this.args = [arg]
};
console.log( new Fn(1), Reflect.construct(Fn,[1]) ); // 输出是一样的
var d = Reflect.construct(Date, [1776, 6, 4]);
d instanceof Date; // true
d.getFullYear(); // 1776
//所以Reflect.consturct和new 构所以Reflect.consturct和new 构造函数是一样, 至少到目前为止..
我们可以给Reflect.construct传第三个参数 , 第三个参数为一个超类, 新元素会继承这个超类;
<script>
function someConstructor() {}
var result = Reflect.construct(Array, [], someConstructor);
Reflect.getPrototypeOf(result); // someConstructor.prototype
Array.isArray(result); // true
//or
var Fn = function() {
this.attr = [1];
};
var Person = function() {
};
Person.prototype.run = function() {
};
console.log( Reflect.construct(Fn, [], Person) );
</script>
所以我们可以用这个实现一个特殊的的数组, 继承数组, 但是也有自己的方法;
var Fn = function() {
Array.apply(this, arguments);
this.shot = ()=> {
console.log("heheda");
};
};
var arr = Reflect.construct(Fn, [])Reflect.defineProperty的使用;
Reflect.defineProperty返回的是一个布尔值, 通过直接赋值的方式把属性和属性值添加给对象返回的是一整个对象, 如果添加失败会抛错;
var obj = {};
obj.x = 10;
console.log(obj.x) //输出:10;使用Reflect.defineProperty的方式添加值;
<script>
var obj = {};
if( Reflect.defineProperty(obj, "x", {value : 7 }) ) {
console.log("added success");
}else{
console.log("添加失败");
};
</script>如果我们执行preventExtensions, 通过Object.defindProperty定义新属性报错了, 但是通过Reflect.defineProperty没有报错, 返回了一个false的值:
var obj = {};
Object.preventExtensions(obj);
Object.defineProperty(obj, "x" , {
value: 101,
writable: false,
enumerable: false,
configurable: false
});// 直接抛错了;
console.log( Reflect.defineProperty(obj, "x", {value:101}) ) //返回false:如果通过直接赋值的方式, 无论是否正确赋值, 都返回设置的值, 除非我们手动确认对象的属性值是否设置成功;
<script>
var obj = {};
Object.preventExtensions(obj);
console.log( obj.aa = 1 ); //输出:1;
console.log(obj.aa) //输出:undefined;
</script>Reflect.deleteProperty的使用:
Reflect.deleteProperty和Reflect.defineProperty的使用方法差不多, Reflect.deleteProperty和 delete obj.xx的操作结果是一样, 区别是使用形式不同:一个是操作符,一个是函数调用;
Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false
delete Object.freeze({foo: 1}).foo; //输出:false;Reflect.get()方法的使用
这个方法的有两个必须的参数: 第一个为obj目标对象, 第二个为属性名对象, 第三个是可选的,是作为读取器的上下文(this);
var obj = {};
obj.foo = 1;
console.log( obj.foo ); //输出:1;
console.log( Reflect.get(obj, "foo") ) //输出:1;如果Reflect.get有第三个参数的话, 第三个参数会作为读取器的上下文:
var Reflect = require('harmony-reflect');
var obj = {
"foo" : 1,
get bar() {
return this.foo;
}
};
var foo = {};
foo.foo = "heheda";
console.log(Reflect.get(obj, "bar", foo));
Reflect.getOwnPropertyDescritptor()方法的使用:
通过Reflect.getOwnPropertyDescritptor获取属性描述:
Reflect.getOwnPropertyDescriptor({x: "hello"}, "x");
//也可以这样获取:
Object.getOwnPropertyDescriptor({x:"1"},"x");
//这两个的区别是一个会包装对象, 一个不会:
Reflect.getOwnPropertyDescriptor("hello",0); //抛出异常
Object.getOwnPropertyDescriptor("hello",0); //输出: {value: "h", writable: false, enumerable: true, configurable: false}Reflect.getPrototypeOf()方法的使用:
Reflect.getPrototypeOf和Object.getPrototypeOf是一样的, 他们都是返回一个对象的原型
Reflect.getPrototypeOf({}); // 输出:Object.prototype
Reflect.getPrototypeOf(Object.prototype); // 输出:null
Reflect.getPrototypeOf(Object.create(null)); // 输出: nullReflect.has的使用
Reflect.has这个方法有点像操作符:in , 比如这样: xx in obj;
<script>
Reflect.has({x:0}, "x") //输出: true;
Reflect.has({y:0}, "y") //输出:true
; var obj = {x:0}; console.log( "x" in obj); var proxy = new Proxy(obj, { has : function(target, args) { console.log("执行has方法"); return Reflect.has(target,...args); } }); console.log( "x" in proxy); //输出:true; console.log(Reflect.has(proxy, "x")) //输出:true; </script>这个demo的obj相当于变成了一个方法了, 没他什么用 , 只是利用了他的has方法:
obj = new Proxy({}, {
has(t, k) { return k.startsWith("door"); }
});
Reflect.has(obj, "doorbell"); // true
Reflect.has(obj, "dormitory"); // false
Reflect.isExtensible()的使用
// 现在这个元素是可以扩展的;
var empty = {};
Reflect.isExtensible(empty); // === true
// 使用preventExtensions方法, 让这个对象无法扩展新属性;
Reflect.preventExtensions(empty);
Reflect.isExtensible(empty); // === false
// 这个对象无法扩展新属性, 可写的属性依然可以改动
var sealed = Object.seal({});
Reflect.isExtensible(sealed); // === false
// 这个对象完全被冻结了
var frozen = Object.freeze({});
Reflect.isExtensible(frozen); // === falseReflect.isExtensible和Object.isExtensible的区别是, 如果参数不对,一个会抛错, 另一个只是返回true或者false:
Reflect.isExtensible(1);
// 抛错了: 1 is not an object
Object.isExtensible(1);
// 返回false;Reflect.ownKeys()方法的使用:
Reflect.ownKeys, Object可没有ownKeys方法, Reflect.ownKeysz他的作用是返回对象的keys;
console.log(Reflect.ownKeys({"a":0,"b":1,"c":2,"d":3})); //输出 :["a", "b", "c", "d"]
console.log(Reflect.ownKeys([])); // ["length"]
var sym = Symbol.for("comet");
var sym2 = Symbol.for("meteor");
var obj = {[sym]: 0, "str": 0, "773": 0, "0": 0,
[sym2]: 0, "-1": 0, "8": 0, "second str": 0};
Reflect.ownKeys(obj); //输出:/ [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]Reflect.ownKeys的排序是根据: 先显示数字, 数字根据大小排序,然后是 字符串根据插入的顺序排序, 最后是symbol类型的key也根据插入插入顺序排序;
出现这中排序是因为,你给一个对象属性赋值时候, 对象的key的排序规则就是先数字, 在字符串, 最后是symbol类型的数据;
Reflect.preventExtensions()的使用方法:
Object也有preventExtensions方法, 和Reflect.preventExtensions()有一点区别, 如果Reflect.preventExtensions参数不是对象会抛错;
var empty = {};
Reflect.isExtensible(empty); // === true
// 执行preventExtensions后的对象可以修改;
Reflect.preventExtensions(empty);
Reflect.isExtensible(empty); // === false
Reflect.preventExtensions(1);
// TypeError: 1 is not an object
Object.preventExtensions(1);
//不会抛错, 会返回:1Reflect.set()
Reflect.set方法和get是差不多的;
var obj = {};
Reflect.set(obj, "prop", "value"); // 输出:true
console.log( obj.prop ); // 输出:"value"
var arr = ["duck", "duck", "duck"];
Reflect.set(arr, 2, "goose"); // true
console.log( arr[2] ); // "goose"
Reflect.set(arr, "length", 1); // true
console.log( arr );// ["duck"];Reflect.set(obj)相当于 Reflect.set(obj, undefined, undefined);
var obj = {};
Reflect.set(obj); // 输出:true
//以上的代码相当于 Reflect.set(obj, undefined, undefined);
Reflect.getOwnPropertyDescriptor(obj, "undefined");
// { value: undefined, writable: true, enumerable: true, configurable: true }Reflect.set也可以有第四个参数, 这个参数会作为stter的this;
var obj = {
value : 10,
set key( value ) {
console.log("setter");
this.value = value;
},
get key() {
return this.value;
}
};
Reflect.set(obj,"key","heheda", obj);
console.log(obj);Reflect.setPrototypeOf()
Reflect.setPrototypeOf()方法和Object.setPrototypeOf差不多一样样的, 会给对象设置原型, 就是更改对象的__proto__属性了…;
Reflect.setPrototypeOf({}, Object.prototype); // 输出true
// 给该对象数组[[Prototype]] 为null.
Reflect.setPrototypeOf({}, null); // true
// 此时的obj.__proto__为undefine
//把对象冻结以后重新设置[[prototype]]
Reflect.setPrototypeOf(Object.freeze({}), null); // false
// 如果原型链循环依赖的话就会返回false.
var target = {};
var proto = Object.create(target);
Reflect.setPrototypeOf(target, proto); // false
Javascript 相关文章推荐
asp.net HttpHandler实现图片防盗链
Nov 09 Javascript
javascript 计算两个整数的百分比值
Dec 26 Javascript
jquery 模拟类搜索框自动完成搜索提示功能(改进)
May 24 Javascript
Jquery修改image的src属性,图片不加载问题的解决方法
May 17 Javascript
JS控制弹出悬浮窗口(一览画面)的实例代码
May 30 Javascript
jquery submit()不能提交表单的解决方法
Apr 24 jQuery
vue+vuex+axios实现登录、注册页权限拦截
Mar 09 Javascript
JS 实现分页打印功能
May 16 Javascript
vue-swiper的使用教程
Aug 30 Javascript
vue router 跳转后回到顶部的实例
Aug 31 Javascript
JavaScript使用Math.random()生成简单的验证码
Jan 21 Javascript
javascript实现自由编辑图片代码详解
Jun 21 Javascript
JavaScript结合Bootstrap仿微信后台多图文界面管理
Jul 22 #Javascript
详细探究ES6之Proxy代理
Jul 22 #Javascript
jquery常用的12个小功能
Jul 22 #Javascript
BootStrap 附加导航组件
Jul 22 #Javascript
jquery checkbox无法用attr()二次勾选问题的解决方法
Jul 22 #Javascript
Select下拉框模糊查询功能实现代码
Jul 22 #Javascript
jQuery Select下拉框操作小结(推荐)
Jul 22 #Javascript
You might like
详解thinkphp实现excel数据的导入导出(附完整案例)
2016/12/29 PHP
使用laravel根据用户类型来显示或隐藏字段
2019/10/17 PHP
一个简单的jQuery插件制作 学习过程及实例
2010/04/25 Javascript
jquery聚焦文本框与扩展文本框聚焦方法
2012/10/12 Javascript
在新窗口打开超链接的方法小结
2013/04/14 Javascript
js中的push和join方法使用介绍
2013/10/08 Javascript
javascript定时器完整实例
2015/02/10 Javascript
Javascript removeChild()删除节点及删除子节点的方法
2015/12/27 Javascript
详解JavaScript模块化开发
2016/12/04 Javascript
Bootstrap基本组件学习笔记之导航(10)
2016/12/07 Javascript
原生js编写焦点图效果
2016/12/08 Javascript
Bootstrap CSS组件之导航条(navbar)
2016/12/17 Javascript
jQuery插件zTree实现清空选中第一个节点所有子节点的方法
2017/03/08 Javascript
JavaScript html5 canvas实现图片上画超链接
2017/10/20 Javascript
vue-cli系列之vue-cli-service整体架构浅析
2019/01/14 Javascript
vue Element左侧无限级菜单实现
2020/06/10 Javascript
python采用requests库模拟登录和抓取数据的简单示例
2014/07/05 Python
在windows系统中实现python3安装lxml
2016/03/23 Python
Python新手入门最容易犯的错误总结
2017/04/24 Python
Python中字典(dict)合并的四种方法总结
2017/08/10 Python
Django内容增加富文本功能的实例
2017/10/17 Python
利用Python进行异常值分析实例代码
2017/12/07 Python
使用k8s部署Django项目的方法步骤
2019/01/14 Python
ubuntu16.04升级Python3.5到Python3.7的方法步骤
2020/08/20 Python
sublime3之内网安装python插件Anaconda的流程
2020/11/10 Python
Pandas对每个分组应用apply函数的实现
2020/12/13 Python
基于HTML5陀螺仪实现ofo首页眼睛移动效果的示例
2017/07/31 HTML / CSS
应用电子技术专业个人求职信
2013/09/21 职场文书
广告设计专业自荐信范文
2013/11/14 职场文书
面试后感谢信
2014/02/01 职场文书
工作证明范本(2篇)
2014/09/14 职场文书
创先争优演讲稿
2014/09/15 职场文书
机械制造专业大学生自我鉴定
2014/09/19 职场文书
幼儿园托班教育随笔
2015/08/14 职场文书
初中语文教学研修日志
2015/11/13 职场文书
辞职报告(范文三篇)
2019/08/27 职场文书