(十一)Reflect

Reflect對象的設(shè)計目的有這樣幾個。

1) 將Object對象的一些明顯屬于語言內(nèi)部的方法(比如Object.defineProperty),放到Reflect對象上?,F(xiàn)階段,某些方法同時在Object和Reflect對象上部署,未來的新方法將只部署在Reflect對象上。也就是說,從Reflect對象上可以拿到語言內(nèi)部的方法。

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

// 老寫法
try {
  Object.defineProperty(target, property, attributes);
  // success
} catch (e) {
  // failure
}

// 新寫法
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}

(3) 讓Object操作都變成函數(shù)行為。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)讓它們變成了函數(shù)行為。

// 老寫法
'assign' in Object // true

// 新寫法
Reflect.has(Object, 'assign') // true

(4)Reflect對象的方法與Proxy對象的方法一一對應(yīng),只要是Proxy對象的方法,就能在Reflect對象上找到對應(yīng)的方法。這就讓Proxy對象可以方便地調(diào)用對應(yīng)的Reflect方法,完成默認行為,作為修改行為的基礎(chǔ)。也就是說,不管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;
  }
});

Reflect對象一共有 13 個靜態(tài)方法。

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

什么是get 和set?

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屬性部署了讀取函數(shù)(getter),則讀取函數(shù)的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

receiver注意這個參數(shù),注意this在有無receiver參數(shù)的異同,演示

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

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

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

Reflect.set方法設(shè)置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屬性設(shè)置了賦值函數(shù),則賦值函數(shù)的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 對象聯(lián)合使用,前者攔截賦值操作,后者完成賦值的默認行為,而且傳入了receiver,那么Reflect.set會觸發(fā)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

receiver注意這個參數(shù),注意this在有無receiver參數(shù)的異同,演示

實例:使用 Proxy 實現(xiàn)觀察者模式

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

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;
}

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

function print() {
  console.log(`${person.name}, ${person.age}`)
}
function print2() {
  console.log('就是任性')
}

observe(print);
observe(print2);
person.name = '李四';

上面代碼中,數(shù)據(jù)對象person是觀察目標,函數(shù)print是觀察者。一旦數(shù)據(jù)對象發(fā)生變化,print就會自動執(zhí)行。

使用 Proxy 寫一個觀察者模式的最簡單實現(xiàn),即實現(xiàn)observable和observe這兩個函數(shù)。思路是observable函數(shù)返回一個原始對象的 Proxy 代理,攔截賦值操作,觸發(fā)充當觀察者的各個函數(shù)。

上面代碼中,先定義了一個Set集合,所有觀察者函數(shù)都放進這個集合。然后,observable函數(shù)返回原始對象的代理,攔截賦值操作。攔截函數(shù)set之中,會自動執(zhí)行所有觀察者。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • defineProperty() 學(xué)習(xí)書籍《ECMAScript 6 入門 》 Proxy Proxy 用于修改某...
    Bui_vlee閱讀 706評論 0 1
  • ECMAScript發(fā)展歷史 (1)ECMA-262 第1版:去除了對針對瀏覽器的特性,支持Unicode標準(多...
    congnie116閱讀 1,972評論 0 2
  • 本人自學(xué)es6已經(jīng)有一段時間了,只覺得有些時候很是枯燥無味, 時而又覺得在以后的職業(yè)生涯中會很有用,因為es6的很...
    可樂_37d3閱讀 1,659評論 0 0
  • 都忘了怎么把毛不易的《消愁》放進收藏夾的了,晚上放到這首就設(shè)置了單曲循環(huán)模式,一邊聽一邊和T聊天,和他說希望...
    愛卡卡愛生活閱讀 226評論 0 0
  • 20年如一日,頂著個蘑菇頭,活脫脫一個假小子。 幾個慵懶的早晨,賴著被窩不肯撒手,耷拉著腦袋坐在梳妝臺前,任媽子把...
    exclusive_lt閱讀 722評論 0 1

友情鏈接更多精彩內(nèi)容