ES6 之 Reflect 的方法總結

1. 概述

  • 將 Object 對象的一些明顯屬於語言內部的方法(好比 Object.defineProperty ),放到 Reflect 對象上。 app

  • 修改某些 Object 方法的返回結果,讓其變得更合理。好比, Object.defineProperty(obj, name, desc) 在沒法定義屬性時,會拋出一個錯誤,而 Reflect.defineProperty(obj, name, desc) 則會返回 false 。 函數

  • 讓 Object 操做都變成函數行爲。某些 Object 操做是命令式,好比 name in obj 和 delete obj[name] ,而 Reflect.has(obj, name) 和 Reflect.deleteProperty(obj, name) 讓它們變成了函數行爲。 this

  • Reflect 對象的方法與 Proxy 對象的方法一一對應,只要是 Proxy 對象的方法,就能在 Reflect 對象上找到對應的方法。spa

// 老寫法
Function.prototype.apply.call(Math.floor, undefined, [8.75]) // 8

// 新寫法
Reflect.apply(Math.floor, undefined, [20.5]) // 20

2. 靜態方法

Reflect對象一共喲13個靜態方法:prototype

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)

1.Reflect.get(target, name, receiver)

Reflect.get 方法查找並返回 target 對象的 name 屬性,若是沒有該屬性,則返回 undefined 。代理

var obj = {
    name: 'houfee',
    age: 24,
    get func() {
        return this.name + this.age
    }
}
console.log(Reflect.get(obj, 'name')) // houfee
console.log(Reflect.get(obj, 'age')) // 24
console.log(Reflect.get(obj, 'func')) // houfee24

若是 name 屬性部署了讀取函數(getter),則讀取函數的 this 綁定 receiver 。code

改變this指向server

var obj = {
    name: 'houfee',
    age: 24,
    get func() {
        return this.name + this.age
    }
}
var obj2 = {
    name: 'houyue',
    age: 14,
}
console.log(Reflect.get(obj, 'name')) // houfee
console.log(Reflect.get(obj, 'age')) // 24
console.log(Reflect.get(obj, 'func', obj2)) // houyue14

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

 

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

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

var obj = {
    name: 'houfee',
    set func(value) {
        return this.name = value
    }
}
var obj2 = {
    name: 'houyue'
}
console.log(Reflect.set(obj, 'name', 'houyue')) // true
console.log(Reflect.set(obj, 'func', 'houyue')) // true
console.log(obj) // {name: "houyue", age: 24}

若是 name 屬性設置了賦值函數,則賦值函數的 this 綁定 receiver 。

var obj = {
    name: 'houfee',
    set func(value) {
        return this.name = value
    }
}
var obj2 = {
    name: 'zhangsan'
}
console.log(Reflect.set(obj, 'func', 'houyue', obj2)) // true
console.log(obj) // {name: "houfee"}
console.log(obj2) // {name: "houyue"}

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

ProxyReflect 配合使用:

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

let p = {
    a: 'a'
};
let handler = {
    set(target, key, value, receiver) {
        console.log('set');
        Reflect.set(target, key, value)
    },
    defineProperty(target, key, attribute) {
        console.log('defineProperty');
        Reflect.defineProperty(target, key, attribute);
    }
};
let obj = new Proxy(p, handler);
obj.a = 'A'; // set

若是第一個參數不是對象, Reflect.set 會報錯。

 

3.Reflect.has(obj, name)

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

var myObject = {
    foo: 1,
};
// 舊寫法
console.log('foo' in myObject); // true
// 新寫法
console.log(Reflect.has(myObject, 'foo')); // true

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

 

4. Reflect.deleteProperty(obj, name)

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

const myObj = {
    foo: 'bar'
};
// 舊寫法
delete myObj.foo;
// 新寫法
Reflect.deleteProperty(myObj, 'foo');

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

 

5. Reflect.construct(target, args)

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

function Func(name) {
    this.name = name
}
// new 的寫法
const instance = new Func('張三')
// Reflect.construct 的寫法
const instance = Reflect.construct(Func, ['張三'])

 

6. Reflect.getPrototypeOf(obj)

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

function Func(name) {
    this.name = name
}
// new 的寫法
const instance = new Func('張三')
Object.getPrototypeOf(instance) === Func.prototype
// Reflect.construct 的寫法
const instance = Reflect.construct(Func, ['張三'])
Reflect.getPrototypeOf(instance) === Func.prototype

Reflect.getPrototypeOf 和 Object.getPrototypeOf 的一個區別是:

若是參數不是對象, Object.getPrototypeOf 會將這個參數轉爲對象,而後再運行,而 Reflect.getPrototypeOf 會報錯。

 

7. Reflect.setPrototypeOf(obj, newProto)

Reflect.setPrototypeOf 方法用於設置對象的 proto 屬性,返回第一個參數對象,對應bject.setPrototypeOf(obj, newProto) 。

function Func(name) {
    this.name = name
}
function Age(age) {
    this.age = age
}
// new 的寫法 將 Age 掛載到 instance 實例的原型上
var instance = new Func('張三')
Object.setPrototypeOf(instance, Age.prototype)
// Reflect.construct 的寫法
var instance = Reflect.construct(Func, ['張三'])
Reflect.setPrototypeOf(instance, Age.prototype)

若是第一個參數不是對象, Object.setPrototypeOf 會返回第一個參數自己,而 Reflect.setPrototypeOf 會報錯。

若是第一個參數是 undefined 或 null , Object.setPrototypeOf 和 Reflect.setPrototypeOf 都會報錯。

 

8. Reflect.apply(func, thisArg, args)

Reflect.apply 方法等同於 Function.prototype.apply.call(func, thisArg, args) ,用於綁定 this 對象後執行給定函數。 通常來講,若是要綁定一個函數的 this 對象,能夠這樣寫 fn.apply(obj, args) ,可是若是函數定義了本身的 apply 方法,就只能寫成 Function.prototype.apply.call(fn, obj, args) ,採用 Reflect 對象能夠簡化這種操做。

const ages = [11, 33, 12, 54, 18, 96];
// 舊寫法
const youngest = Math.min.apply(Math, ages);
const oldest = Math.max.apply(Math, ages);
const type = Object.prototype.toString.call(youngest);
// 新寫法
const youngest = Reflect.apply(Math.min, Math, ages);
const oldest = Reflect.apply(Math.max, Math, ages);
const type = Reflect.apply(Object.prototype.toString, youngest, []);

 

9. Reflect.defineProperty(target, propertyKey, attributes)

Reflect.defineProperty 方法基本等同於 Object.defineProperty ,用來爲對象定義屬性。將來,後者會被逐漸廢除,請從如今開始就使用 Reflect.defineProperty 代替它。

function MyDate() {
    /**/
}
// 舊寫法
Object.defineProperty(MyDate, 'now', {
    value: () => Date.now()
});
// 新寫法
Reflect.defineProperty(MyDate, 'now', {
    value: () => Date.now()
});

若是 Reflect.defineProperty 的第一個參數不是對象,就會拋出錯誤,好比 Reflect.defineProperty(1, 'foo') 。

 

10. Reflect.getOwnPropertyDescriptor(target, propertyKey)

Reflect.getOwnPropertyDescriptor 基本等同於 Object.getOwnPropertyDescriptor ,用於獲得指定屬性的描述對象,未來會替代掉後者。

var myObject = {};
Object.defineProperty(myObject, 'hidden', {
    value: true,
    enumerable: false,
});
// 舊寫法
var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden');
// 新寫法
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');

Reflect.getOwnPropertyDescriptor 和 Object.getOwnPropertyDescriptor 的一個區別是,若是第一個參數不是對象, Object.getOwnPropertyDescriptor(1, 'foo') 不報錯,返回 undefined ,

而 Reflect.getOwnPropertyDescriptor(1, 'foo') 會拋出錯誤,表示參數非法。

 

11.Reflect.isExtensible (target)

Reflect.isExtensible 方法對應 Object.isExtensible ,返回一個布爾值,表示當前對象是否可擴展。

const myObject = {};
// 舊寫法
Object.isExtensible(myObject) // true
// 新寫法
Reflect.isExtensible(myObject) // true

若是參數不是對象, Object.isExtensible 會返回 false ,由於非對象原本就是不可擴展的,而 Reflect.isExtensible 會報錯。

 

12. Reflect.preventExtensions(target)

Reflect.preventExtensions 對應 Object.preventExtensions 方法,用於讓一個對象變爲不可擴展。它返回一個布爾值,表示是否操做成功。

var myObject = {};
// 舊寫法
Object.preventExtensions(myObject) // Object {}
// 新寫法
Reflect.preventExtensions(myObject) // true

若是參數不是對象, Object.preventExtensions 在 ES5 環境報錯,在 ES6 環境返回傳入的參數,而 Reflect.preventExtensions 會報錯。

 

13. Reflect.ownKeys (target)

Reflect.ownKeys 方法用於返回對象的全部屬性,基本等同於 Object.getOwnPropertyNames 與 Object.getOwnPropertySymbols 之和。

var myObject = {
    foo: 1,
    bar: 2,
    [Symbol.for('baz')]: 3,
    [Symbol.for('bing')]: 4,
};
// 舊寫法
Object.getOwnPropertyNames(myObject)
// ['foo', 'bar']
Object.getOwnPropertySymbols(myObject)
//[Symbol(baz), Symbol(bing)]
Reflect.ownKeys(myObject)
// ['foo', 'bar', Symbol(baz), Symbol(bing)]

 

3. 實例:使用 Proxy 實現觀察者模式

觀察者模式(Observer mode)指的是函數自動觀察數據對象,一旦對象有變化,函數就會自動執行。

 
const person = observable({
    name: '張三',
    age: 20
});

function print() {
    console.log(`${person.name}, ${person.age}`)
}
observe(print);
person.name = '李四'; // 輸出 // 李四, 20

上面代碼中,數據對象 person 是觀察目標,函數 print 是觀察者。一旦數據對象發生變化, print 就會自動執行。

下面,使用 Proxy 寫一個觀察者模式的最簡單實現,即實現 observable 和 observe 這兩個函數。思路是 observable 函數返回一個原始對象的 Proxy 代理,攔截賦值操做,觸發充當觀察者的各個函數。

const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {
    set
});

function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
    queuedObservers.forEach(observer => observer());
    return result;
}

上面代碼中,先定義了一個 Set 集合,全部觀察者函數都放進這個集合。而後, observable 函數返回原始對象的代理,攔截賦值操做。攔截函數 set 之中,會自動執行全部觀察者。

相關文章
相關標籤/搜索