es6 Reflect對象詳解

Reflect是ES6爲操做對象而提供的新API,而這個API設計的目的只要有:es6

  • 將Object對象的一些屬於語言內部的方法放到Reflect對象上,從Reflect上能拿到語言內部的方法。如:Object.defineProperty
  • 修改某些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)相等
  • Reflect對象的方法與Proxy對象的方法一一對應,只要proxy對象上有的方法reflect也能找到。
Proxy(target, {
  set: function(target, name, value, receiver) {
    var success = Reflect.set(target,name, value, receiver);
    if (success) {
      log('property ' + name + ' on ' + target + ' set to ' + value);
    }
    return success;
  }
});
var loggedObj = new Proxy(obj, {
  get(target, name) {
    console.log('get', target, name);
    return Reflect.get(target, name);
  },
  deleteProperty(target, name) {
    console.log('delete' + name);
    return Reflect.deleteProperty(target, name);
  },
  has(target, name) {
    console.log('has' + name);
    return Reflect.has(target, name);
  }
});

有了Reflect對象,不少操做會更易讀app

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

// 新寫法
Reflect.apply(Math.floor, undefined, [1.75]) // 1

 

 

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)

上面這些方法的做用大部分與Object對象的同名方法都是相同的,與Proxy對象的方法一一對應的。this

 


 

Reflect.get(target, name, receiver)

Reflect.get方法查找並返回target的name屬性,若是沒有,則返回undefined。spa

var myObject = {
  foo: 1,
  bar: 2,
  get baz() {
    return this.foo + this.bar;
  },
}

Reflect.get(myObject, 'foo') // 1
Reflect.get(myObject, 'bar') // 2
Reflect.get(myObject, 'baz') // 3

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

var myObject = {
  foo: 1,
  bar: 2,
  get baz() {
    return this.foo + this.bar;
  },
};

var myReceiverObject = {
  foo: 4,
  bar: 4,
};

Reflect.get(myObject, 'baz', myReceiverObject) // 8

若是第一個參數不是對象,則Reflect.get則會報錯。設計


 

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。code

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攔截。server

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不會觸發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會報錯。


 

Reflect.has(obj, name)

Reflect.has對應 name in obj 裏面的in操做

var myObject = {
  foo: 1,
};

// 舊寫法
'foo' in myObject // true

// 新寫法
Reflect.has(myObject, 'foo') // true

若是第一個參數不是對象,Reflect.has和in都會報錯。


 

Reflect.deleteProperty(obj, name)

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

const myObj = { foo: 'bar' };

// 舊寫法
delete myObj.foo;

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

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


 

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, ['張三']);

Reflect.getPrototypeOf(obj)

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

const myObj = new FancyThing();

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

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

它們的區別是,若是參數不是對象,Object.getPrototypeOf會將參數轉化爲對象,而Reflect.getPrototypeOf會報錯。


 

Reflect.setPrototypeOf(obj, newProto)

Reflect.setPrototypeOf方法是設置對象的__proto__屬性,返回第一個參數對象,對應Object.setPrototypeOf(obj, newProto

const myObj = new FancyThing();

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

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

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

若是第一個參數是undefined或null,則兩個都會報錯。


 

Reflect.apply(func, thisArgs, args)

Reflect.apply等同於Function.prototype.apply.call(func, thisArgs, 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, []);

Reflect.defineProperty(target, propertyKey, attributes)

Reflect.defineProperty等同於Object.defineProperty,用來爲對象定義屬性。將來後者會被逐漸廢除。

function MyDate() {
  /**/
}

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

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

若是第一個參數不是對象,就會拋出錯誤信息。

該方法配合Proxy.defineProperty使用:

const p = new Proxy({}, {
  defineProperty(target, prop, descriptor) {
    console.log(descriptor);
    return Reflect.defineProperty(target, prop, descriptor);
  }
});

p.foo = 'bar';
// {value: "bar", writable: true, enumerable: true, configurable: true}

p.foo // "bar"

上面代碼中,Proxy.defineProperty對屬性賦值設置了攔截,Reflect.defineProperty完成了賦值。


 

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');

若是第一個參數不是對象Object.getOwnPropertyDescriptor不報錯,返回undefined,而Reflect.getOwnPropertyDescriptor會報錯,表示參數非法。


 

Reflect.isExtensible (target) 

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

const myObject = {};

// 舊寫法
Object.isExtensible(myObject) // true

// 新寫法
Reflect.isExtensible(myObject) // true

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


Reflect.preventExtensions(target)

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

var myObject = {};

// 舊寫法
Object.preventExtensions(myObject) // Object {}

// 新寫法
Reflect.preventExtensions(myObject) // true

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


 

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)]

 

 


 

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

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

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

function print() {
  console.log(`${person.name}, ${person.age}`)
}

observe(print);
person.name = '李四';
// 輸出
// 李四, 20

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

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

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中,會自動執行全部觀察者。

原文連接:http://es6.ruanyifeng.com/#docs/reflect

相關文章
相關標籤/搜索