es6 Reflect對象詳解


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

  • 將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對象,很多操作會更易讀

// 老寫法
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對象的方法一一對應的。

 


 

Reflect.get(target, name, receiver)

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

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。

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。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM