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 相关文章推荐
基于Jquery插件开发之图片放大镜效果(仿淘宝)
Nov 19 Javascript
前台js改变Session的值(用ajax实现)
Dec 28 Javascript
js调用后台servlet方法实例
Jun 09 Javascript
javascript 实现字符串反转的三种方法
Nov 23 Javascript
使用coffeescript编写node.js项目的方法汇总
Aug 05 Javascript
JS跨域解决方案之使用CORS实现跨域
Apr 14 Javascript
Vue.js Ajax动态参数与列表显示实现方法
Oct 20 Javascript
JS瀑布流实现方法实例分析
Dec 19 Javascript
微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析
Nov 27 Javascript
Vue-cli3项目配置Vue.config.js实战记录
Jul 29 Javascript
微信小程序实现页面浮动导航
Jan 08 Javascript
vue组件中传值EventBus的使用及注意事项说明
Nov 16 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
destoon实现调用图文新闻的方法
2014/08/21 PHP
thinkPHP交易详情查询功能详解
2016/12/02 PHP
thinkPHP利用ajax异步上传图片并显示、删除的示例
2018/09/26 PHP
jQuery ui 1.7更新小结
2009/08/15 Javascript
jQuery实现的产品自动360度旋转展示特效源码分享
2015/08/21 Javascript
利用JS提交表单的几种方法和验证(必看篇)
2016/09/17 Javascript
jQuery双向列表选择器select版
2016/11/01 Javascript
jQuery电话号码验证实例
2017/01/05 Javascript
jQuery实现的简单排序功能示例【冒泡排序】
2017/01/13 Javascript
js判断手机号是否正确并返回的实现代码
2017/01/17 Javascript
html+javascript+bootstrap实现层级多选框全层全选和多选功能
2017/03/09 Javascript
js中apply和Math.max()函数的问题及区别介绍
2018/03/27 Javascript
VUE基于NUXT的SSR 服务端渲染
2018/11/30 Javascript
node.js实现微信开发之获取用户授权
2019/03/18 Javascript
微信小程序防止多次点击跳转(函数节流)
2019/09/19 Javascript
JavaScript制作3D旋转相册
2020/08/02 Javascript
[02:49]2014DOTA2电竞也是体育项目! 势要把荣誉带回中国!
2014/07/20 DOTA
[33:15]2018DOTA2亚洲邀请赛3月30日 小组赛B组 VP VS Mineski
2018/03/31 DOTA
python正则表达式判断字符串是否是全部小写示例
2013/12/25 Python
Python入门教程之if语句的用法
2015/05/14 Python
python学习教程之使用py2exe打包
2017/09/24 Python
详解supervisor使用教程
2017/11/21 Python
详解python中的 is 操作符
2017/12/26 Python
Python实现的rsa加密算法详解
2018/01/24 Python
python 反向输出字符串的方法
2018/07/16 Python
python函数装饰器之带参数的函数和带参数的装饰器用法示例
2019/11/06 Python
django 外键创建注意事项说明
2020/05/20 Python
python 多线程中join()的作用
2020/10/29 Python
Python爬虫实现selenium处理iframe作用域问题
2021/01/27 Python
html5使用Canvas绘图的使用方法
2017/11/21 HTML / CSS
三星加拿大官方网上商店:Samsung CA
2020/12/18 全球购物
前台文员岗位职责
2013/12/28 职场文书
会计电算化学生个人的自我评价
2014/02/08 职场文书
伦敦奥运会的口号
2014/06/21 职场文书
小学班主任评语
2014/12/29 职场文书
《游戏公平》教学反思
2016/02/20 职场文书