JavaScript中的Reflect對象詳解(ES6新特性)

字號:


    這篇文章主要介紹了JavaScript中的Reflect對象(ES6新特性)的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    Reflect介紹:
    Reflect這個對象在我的node(v4.4.3)中還沒有實(shí)現(xiàn), babel(6.7.7)也沒有實(shí)現(xiàn) ,新版本的chrome是支持的, ff比較早就支持Proxy和Reflect了,要讓node支持Reflect可以安裝harmony-reflect ;
    Reflect不是構(gòu)造函數(shù), 要使用的時候直接通過Reflect.method()調(diào)用, Reflect有的方法和Proxy差不多, 而且多數(shù)Reflect方法原生的Object已經(jīng)重新實(shí)現(xiàn)了。
    什么要使用Reflect
    這里列舉幾個為什么要使用Reflect的原因, 譯文地址:Reflect , 大概翻譯了一遍:
    1:更加有用的返回值: Reflect有一些方法和ES5中Object方法一樣樣的, 比如: Reflect.getOwnPropertyDescriptor和Reflect.defineProperty, 不過, Object.defineProperty(obj, name, desc)執(zhí)行成功會返回obj, 以及其它原因?qū)е碌腻e誤, Reflect.defineProperty只會返回false或者true來表示對象的屬性是否設(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ù)操作, 如果要判斷一個obj有定義或者繼承了屬性name, 在ES5中這樣判斷:name in obj ;
    或者刪除一個屬性 :delete obj[name], 雖然這些很好用, 很簡短, 很明確, 但是要使用的時候也要封裝成一個類;
    有了Reflect, 它幫你封裝好了, Reflect.has(obj, name), Reflect.deleteProperty(obj, name);
    3:更加可靠的函數(shù)式執(zhí)行方式: 在ES中, 要執(zhí)行一個函數(shù)f,并給它傳一組參數(shù)args, 還要綁定this的話, 要這么寫:
    f.apply(obj, args)
    但是f的apply可能被重新定義成用戶自己的apply了,所以還是這樣寫比較靠譜:
    Function.prototype.apply.call(f, obj, args)
    上面這段代碼太長了, 而且不好懂, 有了Reflect, 我們可以更短更簡潔明了:
    Reflect.apply(f, obj, args)
    4:可變參數(shù)形式的構(gòu)造函數(shù): 想象一下, 你想通過不確定長度的參數(shù)實(shí)例化一個構(gòu)造函數(shù), 在ES5中, 我們可以使用擴(kuò)展符號, 可以這么寫:
    var obj = new F(...args)
    不過在ES5中, 不支持?jǐn)U展符啊, 所以, 我們只能用F.apply,或者F.call的方式傳不同的參數(shù), 可惜F是一個構(gòu)造函數(shù), 這個就坑爹了, 不過有了Reflect, 
    我們在ES5中能夠這么寫:
    var obj = Reflect.construct(F, args)
    5:控制訪問器或者讀取器的this: 在ES5中, 想要讀取一個元素的屬性或者設(shè)置屬性要這樣:
    var name = ... // get property name as a string
    obj[name] // generic property lookup
    obj[name] = value // generic property
    updateReflect.get和Reflect.set方法允許我們做同樣的事情, 而且他增加了一個額外的參數(shù)reciver, 允許我們設(shè)置對象的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), 這個是新的方法去訪問和設(shè)置對象的原型:
    Reflect.apply的使用
    Reflect.apply其實(shí)就是ES5中的 Function.prototype.apply() 替身, 執(zhí)行Reflect.apply需要三個參數(shù)
    第一個參數(shù)為: 需要執(zhí)行的函數(shù);
    第二個參數(shù)為: 需要執(zhí)行函數(shù)的上下文this;
    第三個參數(shù)為: 是一個數(shù)組或者偽數(shù)組, 會作為執(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 (); //會先輸出: "proxy constructor" ; 再輸出: runs out
    }Reflect.construct()的使用:
    Reflect.construct其實(shí)就是實(shí)例化構(gòu)造函數(shù),通過傳參形式的實(shí)現(xiàn), 執(zhí)行的方式不同, 效果其實(shí)一樣, construct的第一個參數(shù)為構(gòu)造函數(shù), 第二個參數(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傳第三個參數(shù) , 第三個參數(shù)為一個超類, 新元素會繼承這個超類;
    <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>
    所以我們可以用這個實(shí)現(xiàn)一個特殊的的數(shù)組, 繼承數(shù)組, 但是也有自己的方法;
    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>如果我們執(zhí)行preventExtensions, 通過Object.defindProperty定義新屬性報(bào)錯了, 但是通過Reflect.defineProperty沒有報(bào)錯, 返回了一個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:如果通過直接賦值的方式, 無論是否正確賦值, 都返回設(shè)置的值, 除非我們手動確認(rèn)對象的屬性值是否設(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ū)別是使用形式不同:一個是操作符,一個是函數(shù)調(diào)用;
    Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false
    delete Object.freeze({foo: 1}).foo; //輸出:false;Reflect.get()方法的使用
    這個方法的有兩個必須的參數(shù): 第一個為obj目標(biāo)對象, 第二個為屬性名對象, 第三個是可選的,是作為讀取器的上下文(this);
    var obj = {};
    obj.foo = 1;
    console.log( obj.foo ); //輸出:1;
    console.log( Reflect.get(obj, "foo") ) //輸出:1;如果Reflect.get有第三個參數(shù)的話, 第三個參數(shù)會作為讀取器的上下文:
    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");
    //也可以這樣獲?。?BR>    Object.getOwnPropertyDescriptor({x:"1"},"x");
    //這兩個的區(qū)別是一個會包裝對象, 一個不會:
    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這個方法有點(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>這個demo的obj相當(dāng)于變成了一個方法了, 沒他什么用 , 只是利用了他的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)在這個元素是可以擴(kuò)展的;
    var empty = {};
    Reflect.isExtensible(empty); // === true
    // 使用preventExtensions方法, 讓這個對象無法擴(kuò)展新屬性;
    Reflect.preventExtensions(empty);
    Reflect.isExtensible(empty); // === false
    // 這個對象無法擴(kuò)展新屬性, 可寫的屬性依然可以改動
    var sealed = Object.seal({});
    Reflect.isExtensible(sealed); // === false
    // 這個對象完全被凍結(jié)了
    var frozen = Object.freeze({});
    Reflect.isExtensible(frozen); // === falseReflect.isExtensible和Object.isExtensible的區(qū)別是, 如果參數(shù)不對,一個會拋錯, 另一個只是返回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的排序是根據(jù): 先顯示數(shù)字, 數(shù)字根據(jù)大小排序,然后是 字符串根據(jù)插入的順序排序, 最后是symbol類型的key也根據(jù)插入插入順序排序;
    出現(xiàn)這中排序是因?yàn)椋憬o一個對象屬性賦值時候, 對象的key的排序規(guī)則就是先數(shù)字, 在字符串, 最后是symbol類型的數(shù)據(jù);
    Reflect.preventExtensions()的使用方法:
    Object也有preventExtensions方法, 和Reflect.preventExtensions()有一點(diǎn)區(qū)別, 如果Reflect.preventExtensions參數(shù)不是對象會拋錯;
    var empty = {};
    Reflect.isExtensible(empty); // === true
    // 執(zhí)行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)相當(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也可以有第四個參數(shù), 這個參數(shù)會作為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差不多一樣樣的, 會給對象設(shè)置原型, 就是更改對象的__proto__屬性了…;
    Reflect.setPrototypeOf({}, Object.prototype); // 輸出true
    // 給該對象數(shù)組[[Prototype]] 為null.
    Reflect.setPrototypeOf({}, null); // true
    // 此時的obj.__proto__為undefine
    //把對象凍結(jié)以后重新設(shè)置[[prototype]]
    Reflect.setPrototypeOf(Object.freeze({}), null); // false
    // 如果原型鏈循環(huán)依賴的話就會返回false.
    var target = {};
    var proto = Object.create(target);
    Reflect.setPrototypeOf(target, proto); // false