ES6 Reflect 介紹, Reflect 相關方法介紹

Reflect

1.0 概述

Reflect 對象與 Proxy 對象一樣, 也是ES6爲了操作對象而提供的新 API, Reflect 對象的設計目的有這樣幾個.

  1. Object 對象的一些明顯屬於語言內部的語法(比如 Object.defineProperty) 放到 Reflect 對象上. 也就是說, 可以從 Reflect 對象上拿到語言內部的方法.
  2. 修改某些 Object 方法的返回結果, 讓其變得更合理. 比如Object.defineProperty(obj, name, desc) 在無法定義屬性時, 會拋出一個錯誤, 而 Reflect.defineProterty(obj,name, desc)則返回false`
// 老寫法
try {
  Object.defineProperty(target, property, attributes);
  // success
} catch (e) {
  // failure
}

// 新寫法
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}
  1. Object 操作都變成函數行爲, 某些 Object 操作時命令式, 比如 name in objdelete obj[name], 而 reflect.has(obj,name)Reflect.deleteProperty(obj,name) 讓它們變成了函數行爲.

    const obj = {
        name: 'Gene',
    }
    // 老寫法
    console.log('name' in obj);
    // 新寫法
    Reflect.has(obj, 'name')
    
  2. Reflect 對象的方法與 Proxy 對象的方法一一對應, 只要是 Proxy 對象的方法, 就能在 Reflect 對象上找到對應的方法. 這就讓 Proxy 對象可以方便的調用對應的 Reflect方法, 完成默認行爲, 作爲修改行爲的基礎, 也就是說, 不管 Proxy 怎麼修改默認行爲, 你總是可以在 Reflect 上獲取默認行爲.

2.0 靜態方法

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)

2.1 Reflect.get(target,name,receiver)

Reflect.get 方法查找並返回 target 對象的 name 屬性, 如果沒有該 name 屬性, 則返回 undefined

const obj = {
    name: 'Gene',
    age: 18,
    get baz() {
        return this.name + '-' + this.age
    }
}
console.log(Reflect.get(obj, 'name')); // Gene
console.log(Reflect.get(obj, 'age')); // 18
console.log(Reflect.get(obj, 'baz')); //Gene-18

如果name 屬性部署了讀取函數(getter), 則讀取函數的 this 和會綁定receiver

const obj = {
    foo: 1,
    bar: 2,
    get baz() {
        return this.foo + this.bar
    }
}
const myReceiver = {
    foo: 10,
    bar: 10,
}
console.log(Reflect.get(obj, 'baz', myReceiver)); // 20

如果第一個參數不是對象, Reflect.get 方法會報錯.

Reflect.get(1, 'foo') // 報錯
Reflect.get(false, 'foo') // 報錯

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

Reflect.set 方法設置 target 對象的name 屬性等於value

var myObject = {
  foo: 1,
  set bar(value) {
    return this.foo = value;
  },
}

myObject.foo // 1

Reflect.set(myObject, 'foo', 2);
myObject.foo // 2

Reflect.set(myObject, 'bar', 3)
myObject.foo // 3

如果 name 設置了賦值函數, 則賦值函數的 this 綁定receiver

var myObject = {
  foo: 4,
  set bar(value) {
    return this.foo = value;
  },
};

var myReceiverObject = {
  foo: 0,
};

Reflect.set(myObject, 'bar', 1, myReceiverObject);
myObject.foo // 4
myReceiverObject.foo // 1

注意, 如果 Proxy對象和 Reflect對象聯合使用,前者攔截賦值操作,後者完成賦值的默認行爲,而且傳入了receiver,那麼Reflect.set會觸發Proxy.defineProperty攔截。

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.setreceiver參數總是指向當前的 Proxy實例(即上例的obj),而Reflect.set一旦傳入receiver,就會將屬性賦值到receiver上面(即obj),導致觸發defineProperty攔截。如果Reflect.set沒有傳入receiver,那麼就不會觸發defineProperty攔截。

2.3 Reflect.has(obj,name)

Reflect.has方法對應name in obj裏面的in運算符。

const obj = {
    name: 'Gene',
}
console.log(Reflect.has(obj, 'name')); // true
console.log(Reflect.has(obj, 'age')); // false

如果Reflect.has()方法的第一個參數不是對象,會報錯。

2.4 Reflect.deleteProperty(obj,name)

Reflect.deleteProperty方法等同於delete obj[name],用於刪除對象的屬性。

const myObj = { foo: 'bar' };

// 舊寫法
delete myObj.foo;

// 新寫法
Reflect.deleteProperty(myObj, 'foo');

該方法返回一個布爾值。如果刪除成功,或者被刪除的屬性不存在,返回true;刪除失敗,被刪除的屬性依然存在,返回false

2.5 Reflect.construct(target,args)

Reflect.construct方法等同於new target(...args),這提供了一種不使用new,來調用構造函數的方法。

function Greeting(name) {
  this.name = name;
}

// new 的寫法
const instance = new Greeting('張三');

// Reflect.construct 的寫法
const instance = Reflect.construct(Greeting, ['張三']);

2.6 Reflect.getPrototypeOf(obj)

Reflect.getPrototypeOf方法用於讀取對象的__proto__屬性,對應Object.getPrototypeOf(obj)

const myObj = new FancyThing();

// 舊寫法
Object.getPrototypeOf(myObj) === FancyThing.prototype;

// 新寫法
Reflect.getPrototypeOf(myObj) === FancyThing.prototype;

Reflect.getPrototypeOfObject.getPrototypeOf的一個區別是,如果參數不是對象,Object.getPrototypeOf會將這個參數轉爲對象,然後再運行,而Reflect.getPrototypeOf會報錯。

2.7 Reflect.setPrototypeOf(obj,newProto)

Reflect.setPrototypeOf方法用於設置目標對象的原型(prototype),對應Object.setPrototypeOf(obj, newProto)方法。它返回一個布爾值,表示是否設置成功。

const myObj = {};

// 舊寫法
Object.setPrototypeOf(myObj, Array.prototype);

// 新寫法
Reflect.setPrototypeOf(myObj, Array.prototype);

myObj.length // 0

如果無法設置目標對象的原型(比如,目標對象禁止擴展),Reflect.setPrototypeOf方法返回false

Reflect.setPrototypeOf({}, null)
// true
Reflect.setPrototypeOf(Object.freeze({}), null)
// false

總結:

上一章: ES6 Proxy構造函數攔截器, Proxy實例的常用方法

下一章:

交流學習添加微信(備註技術交流學習): Gene199302
在這裏插入圖片描述

該博客爲學習阮一峯 ES6入門課所做的筆記記錄, 僅用來留作筆記記錄和學習理解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章