JavaScript中的Reflect對(duì)象詳解(ES6新特性)
Reflect介紹:
Reflect這個(gè)對(duì)象在我的node(v4.4.3)中還沒(méi)有實(shí)現(xiàn), babel(6.7.7)也沒(méi)有實(shí)現(xiàn) ,新版本的chrome是支持的, ff比較早就支持Proxy和Reflect了,要讓node支持Reflect可以安裝harmony-reflect ;
Reflect不是構(gòu)造函數(shù), 要使用的時(shí)候直接通過(guò)Reflect.method()調(diào)用, Reflect有的方法和Proxy差不多, 而且多數(shù)Reflect方法原生的Object已經(jīng)重新實(shí)現(xiàn)了。
什么要使用Reflect
這里列舉幾個(gè)為什么要使用Reflect的原因, 譯文地址:Reflect , 大概翻譯了一遍:
1:更加有用的返回值: Reflect有一些方法和ES5中Object方法一樣樣的, 比如: Reflect.getOwnPropertyDescriptor和Reflect.defineProperty, 不過(guò), Object.defineProperty(obj, name, desc)執(zhí)行成功會(huì)返回obj, 以及其它原因?qū)е碌腻e(cuò)誤, Reflect.defineProperty只會(huì)返回false或者true來(lái)表示對(duì)象的屬性是否設(shè)置上了, 如下代碼可以重構(gòu):
try { Object.defineProperty(obj, name, desc); // property defined successfully } catch (e) { // possible failure (and might accidentally catch the wrong exception) }
重構(gòu)成這樣:
if (Reflect.defineProperty(obj, name, desc)) { // success } else { // failure }
其余的方法, 比如Relect.set , Reflect.deleteProperty, Reflect.preventExtensions, Reflect.setPrototypeOf, 都可以進(jìn)行重構(gòu);
2:函數(shù)操作, 如果要判斷一個(gè)obj有定義或者繼承了屬性name, 在ES5中這樣判斷:name in obj ;
或者刪除一個(gè)屬性 :delete obj[name], 雖然這些很好用, 很簡(jiǎn)短, 很明確, 但是要使用的時(shí)候也要封裝成一個(gè)類;
有了Reflect, 它幫你封裝好了, Reflect.has(obj, name), Reflect.deleteProperty(obj, name);
3:更加可靠的函數(shù)式執(zhí)行方式: 在ES中, 要執(zhí)行一個(gè)函數(shù)f,并給它傳一組參數(shù)args, 還要綁定this的話, 要這么寫:
f.apply(obj, args)
但是f的apply可能被重新定義成用戶自己的apply了,所以還是這樣寫比較靠譜:
Function.prototype.apply.call(f, obj, args)
上面這段代碼太長(zhǎng)了, 而且不好懂, 有了Reflect, 我們可以更短更簡(jiǎn)潔明了:
Reflect.apply(f, obj, args)
4:可變參數(shù)形式的構(gòu)造函數(shù): 想象一下, 你想通過(guò)不確定長(zhǎng)度的參數(shù)實(shí)例化一個(gè)構(gòu)造函數(shù), 在ES5中, 我們可以使用擴(kuò)展符號(hào), 可以這么寫:
var obj = new F(...args)
不過(guò)在ES5中, 不支持?jǐn)U展符啊, 所以, 我們只能用F.apply,或者F.call的方式傳不同的參數(shù), 可惜F是一個(gè)構(gòu)造函數(shù), 這個(gè)就坑爹了, 不過(guò)有了Reflect,
我們?cè)贓S5中能夠這么寫:
var obj = Reflect.construct(F, args)
5:控制訪問(wèn)器或者讀取器的this: 在ES5中, 想要讀取一個(gè)元素的屬性或者設(shè)置屬性要這樣:
var name = ... // get property name as a string obj[name] // generic property lookup obj[name] = value // generic property
updateReflect.get和Reflect.set方法允許我們做同樣的事情, 而且他增加了一個(gè)額外的參數(shù)reciver, 允許我們?cè)O(shè)置對(duì)象的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)訪問(wèn)器中不想使用自己的方法,而是想要重定向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:避免直接訪問(wèn) __proto__ :
ES5提供了 Object.getPrototypeOf(obj),去訪問(wèn)對(duì)象的原型, ES6提供也提供了
Reflect.getPrototypeOf(obj) 和 Reflect.setPrototypeOf(obj, newProto), 這個(gè)是新的方法去訪問(wèn)和設(shè)置對(duì)象的原型:
Reflect.apply的使用
Reflect.apply其實(shí)就是ES5中的 Function.prototype.apply() 替身, 執(zhí)行Reflect.apply需要三個(gè)參數(shù)
第一個(gè)參數(shù)為: 需要執(zhí)行的函數(shù);
第二個(gè)參數(shù)為: 需要執(zhí)行函數(shù)的上下文this;
第三個(gè)參數(shù)為: 是一個(gè)數(shù)組或者偽數(shù)組, 會(huì)作為執(zhí)行函數(shù)的參數(shù);
<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聯(lián)合使用: { 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 (); //會(huì)先輸出: "proxy constructor" ; 再輸出: runs out }Reflect.construct()的使用: Reflect.construct其實(shí)就是實(shí)例化構(gòu)造函數(shù),通過(guò)傳參形式的實(shí)現(xiàn), 執(zhí)行的方式不同, 效果其實(shí)一樣, construct的第一個(gè)參數(shù)為構(gòu)造函數(shù), 第二個(gè)參數(shù)由參數(shù)組成的數(shù)組或者偽數(shù)組, 基本的使用方法為: 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 構(gòu)所以Reflect.consturct和new 構(gòu)造函數(shù)是一樣, 至少到目前為止.. 我們可以給Reflect.construct傳第三個(gè)參數(shù) , 第三個(gè)參數(shù)為一個(gè)超類, 新元素會(huì)繼承這個(gè)超類; <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> 所以我們可以用這個(gè)實(shí)現(xiàn)一個(gè)特殊的的數(shù)組, 繼承數(shù)組, 但是也有自己的方法; var Fn = function() { Array.apply(this, arguments); this.shot = ()=> { console.log("heheda"); }; }; var arr = Reflect.construct(Fn, [])Reflect.defineProperty的使用; Reflect.defineProperty返回的是一個(gè)布爾值, 通過(guò)直接賦值的方式把屬性和屬性值添加給對(duì)象返回的是一整個(gè)對(duì)象, 如果添加失敗會(huì)拋錯(cuò); 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>如果我們執(zhí)行preventExtensions, 通過(guò)Object.defindProperty定義新屬性報(bào)錯(cuò)了, 但是通過(guò)Reflect.defineProperty沒(méi)有報(bào)錯(cuò), 返回了一個(gè)false的值: var obj = {}; Object.preventExtensions(obj); Object.defineProperty(obj, "x" , { value: 101, writable: false, enumerable: false, configurable: false });// 直接拋錯(cuò)了; console.log( Reflect.defineProperty(obj, "x", {value:101}) ) //返回false:如果通過(guò)直接賦值的方式, 無(wú)論是否正確賦值, 都返回設(shè)置的值, 除非我們手動(dòng)確認(rèn)對(duì)象的屬性值是否設(shè)置成功; <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的操作結(jié)果是一樣, 區(qū)別是使用形式不同:一個(gè)是操作符,一個(gè)是函數(shù)調(diào)用; Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false delete Object.freeze({foo: 1}).foo; //輸出:false;Reflect.get()方法的使用 這個(gè)方法的有兩個(gè)必須的參數(shù): 第一個(gè)為obj目標(biāo)對(duì)象, 第二個(gè)為屬性名對(duì)象, 第三個(gè)是可選的,是作為讀取器的上下文(this); var obj = {}; obj.foo = 1; console.log( obj.foo ); //輸出:1; console.log( Reflect.get(obj, "foo") ) //輸出:1;如果Reflect.get有第三個(gè)參數(shù)的話, 第三個(gè)參數(shù)會(huì)作為讀取器的上下文: 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()方法的使用: 通過(guò)Reflect.getOwnPropertyDescritptor獲取屬性描述: Reflect.getOwnPropertyDescriptor({x: "hello"}, "x"); //也可以這樣獲?。? Object.getOwnPropertyDescriptor({x:"1"},"x"); //這兩個(gè)的區(qū)別是一個(gè)會(huì)包裝對(duì)象, 一個(gè)不會(huì): Reflect.getOwnPropertyDescriptor("hello",0); //拋出異常 Object.getOwnPropertyDescriptor("hello",0); //輸出: {value: "h", writable: false, enumerable: true, configurable: false}Reflect.getPrototypeOf()方法的使用: Reflect.getPrototypeOf和Object.getPrototypeOf是一樣的, 他們都是返回一個(gè)對(duì)象的原型 Reflect.getPrototypeOf({}); // 輸出:Object.prototype Reflect.getPrototypeOf(Object.prototype); // 輸出:null Reflect.getPrototypeOf(Object.create(null)); // 輸出: nullReflect.has的使用 Reflect.has這個(gè)方法有點(diǎn)像操作符: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("執(zhí)行has方法"); return Reflect.has(target,...args); } }); console.log( "x" in proxy); //輸出:true; console.log(Reflect.has(proxy, "x")) //輸出:true; </script>這個(gè)demo的obj相當(dāng)于變成了一個(gè)方法了, 沒(méi)他什么用 , 只是利用了他的has方法: obj = new Proxy({}, { has(t, k) { return k.startsWith("door"); } }); Reflect.has(obj, "doorbell"); // true Reflect.has(obj, "dormitory"); // false Reflect.isExtensible()的使用 // 現(xiàn)在這個(gè)元素是可以擴(kuò)展的; var empty = {}; Reflect.isExtensible(empty); // === true // 使用preventExtensions方法, 讓這個(gè)對(duì)象無(wú)法擴(kuò)展新屬性; Reflect.preventExtensions(empty); Reflect.isExtensible(empty); // === false // 這個(gè)對(duì)象無(wú)法擴(kuò)展新屬性, 可寫的屬性依然可以改動(dòng) var sealed = Object.seal({}); Reflect.isExtensible(sealed); // === false // 這個(gè)對(duì)象完全被凍結(jié)了 var frozen = Object.freeze({}); Reflect.isExtensible(frozen); // === falseReflect.isExtensible和Object.isExtensible的區(qū)別是, 如果參數(shù)不對(duì),一個(gè)會(huì)拋錯(cuò), 另一個(gè)只是返回true或者false: Reflect.isExtensible(1); // 拋錯(cuò)了: 1 is not an object Object.isExtensible(1); // 返回false;Reflect.ownKeys()方法的使用: Reflect.ownKeys, Object可沒(méi)有ownKeys方法, Reflect.ownKeysz他的作用是返回對(duì)象的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的排序是根據(jù): 先顯示數(shù)字, 數(shù)字根據(jù)大小排序,然后是 字符串根據(jù)插入的順序排序, 最后是symbol類型的key也根據(jù)插入插入順序排序; 出現(xiàn)這中排序是因?yàn)?,你給一個(gè)對(duì)象屬性賦值時(shí)候, 對(duì)象的key的排序規(guī)則就是先數(shù)字, 在字符串, 最后是symbol類型的數(shù)據(jù); Reflect.preventExtensions()的使用方法: Object也有preventExtensions方法, 和Reflect.preventExtensions()有一點(diǎn)區(qū)別, 如果Reflect.preventExtensions參數(shù)不是對(duì)象會(huì)拋錯(cuò); var empty = {}; Reflect.isExtensible(empty); // === true // 執(zhí)行preventExtensions后的對(duì)象可以修改; Reflect.preventExtensions(empty); Reflect.isExtensible(empty); // === false Reflect.preventExtensions(1); // TypeError: 1 is not an object Object.preventExtensions(1); //不會(huì)拋錯(cuò), 會(huì)返回: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)相當(dāng)于 Reflect.set(obj, undefined, undefined); var obj = {}; Reflect.set(obj); // 輸出:true //以上的代碼相當(dāng)于 Reflect.set(obj, undefined, undefined); Reflect.getOwnPropertyDescriptor(obj, "undefined"); // { value: undefined, writable: true, enumerable: true, configurable: true }Reflect.set也可以有第四個(gè)參數(shù), 這個(gè)參數(shù)會(huì)作為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差不多一樣樣的, 會(huì)給對(duì)象設(shè)置原型, 就是更改對(duì)象的__proto__屬性了…; Reflect.setPrototypeOf({}, Object.prototype); // 輸出true // 給該對(duì)象數(shù)組[[Prototype]] 為null. Reflect.setPrototypeOf({}, null); // true // 此時(shí)的obj.__proto__為undefine //把對(duì)象凍結(jié)以后重新設(shè)置[[prototype]] Reflect.setPrototypeOf(Object.freeze({}), null); // false // 如果原型鏈循環(huán)依賴的話就會(huì)返回false. var target = {}; var proto = Object.create(target); Reflect.setPrototypeOf(target, proto); // false
- ES6中非常實(shí)用的新特性介紹
- JavaScript ES6的新特性使用新方法定義Class
- 深入淺出ES6新特性之函數(shù)默認(rèn)參數(shù)和箭頭函數(shù)
- 簡(jiǎn)單談?wù)凟S6的六個(gè)小特性
- ES6新特性之Symbol類型用法分析
- ES6(ECMAScript 6)新特性之模板字符串用法分析
- ES6新特性之變量和字符串用法示例
- ES6新特性之模塊Module用法詳解
- ES6新特性之字符串的擴(kuò)展實(shí)例分析
- ES6新特性二:Iterator(遍歷器)和for-of循環(huán)詳解
- ES6新特性六:promise對(duì)象實(shí)例詳解
- ES6新特性七:數(shù)組的擴(kuò)充詳解
- ES6新特性八:async函數(shù)用法實(shí)例詳解
- ES6新特性之類(Class)和繼承(Extends)相關(guān)概念與用法分析
- 讓微信小程序支持ES6中Promise特性的方法詳解
- ES6新特性:使用export和import實(shí)現(xiàn)模塊化詳解
- es6新特性之 class 基本用法解析
- ES6 13個(gè)新特性總結(jié)
相關(guān)文章
JavaScript中日常收集常見(jiàn)的10種錯(cuò)誤(推薦)
本文是小編給大家日常收集整理的js中常見(jiàn)的10種錯(cuò)誤,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01Bootstrap樹(shù)形菜單插件TreeView.js使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Bootstrap樹(shù)形菜單插件TreeView.js使用方法,一款非??岬幕赽ootstrap的jQuery多級(jí)列表樹(shù)插件,具有一定的實(shí)用性,感興趣的小伙伴們可以參考一下2016-11-11利用js實(shí)現(xiàn)前臺(tái)動(dòng)態(tài)添加文本框,后臺(tái)獲取文本框內(nèi)容(示例代碼)
這篇文章主要是對(duì)利用js實(shí)現(xiàn)前臺(tái)動(dòng)態(tài)添加文本框,后臺(tái)獲取文本框內(nèi)容的示例代碼進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11js插件方式打開(kāi)pdf文件(瀏覽器pdf插件分享)
需求:JSP網(wǎng)頁(yè)中要顯示pdf文件,下面給出二種方案,JS插件那種不錯(cuò)啊2013-12-12js判斷一點(diǎn)是否在一個(gè)三角形內(nèi)
判斷一個(gè)點(diǎn)是否在一個(gè)三角行內(nèi)的代碼2008-02-02微信小程序返回到頂部功能的簡(jiǎn)單實(shí)現(xiàn)
在做微信小程序開(kāi)發(fā)時(shí),遇到一個(gè)問(wèn)題,要如何實(shí)現(xiàn)返回頂部的功能,下面這篇文章主要給大家介紹了微信小程序返回到頂部功能的簡(jiǎn)單實(shí)現(xiàn),文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11JavaScript實(shí)現(xiàn)自己的DOM選擇器原理及代碼
實(shí)現(xiàn)自己的DOM選擇器時(shí)匹配行為也應(yīng)該和瀏覽原生匹配行為一致,接下來(lái)本文將詳細(xì)介紹下實(shí)現(xiàn)思路及方法,感興趣的你可以參考下或許對(duì)你鞏固知識(shí)有所幫助2013-03-03JavaScript函數(shù)的特性與應(yīng)用實(shí)踐深入詳解
這篇文章主要介紹了JavaScript函數(shù)的特性與應(yīng)用實(shí)踐,結(jié)合實(shí)例形式較為深入的分析了javascript函數(shù)相關(guān)概念、特性、用法及操作注意事項(xiàng),需要的朋友可以參考下2018-12-12