<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ECMAScript 6 入門教程—Reflect

          共 10834字,需瀏覽 22分鐘

           ·

          2020-09-29 21:42

          作者 | 阮一峰

          1、概述

          Reflect對象與Proxy對象一樣,也是 ES6 為了操作對象而提供的新 API。Reflect對象的設計目的有這樣幾個。
          (1) 將Object對象的一些明顯屬于語言內部的方法(比如Object.defineProperty),放到Reflect對象上?,F階段,某些方法同時在Object和Reflect對象上部署,未來的新方法將只部署在Reflect對象上。也就是說,從Reflect對象上可以拿到語言內部的方法。
          (2) 修改某些Object方法的返回結果,讓其變得更合理。比如,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操作都變成函數行為。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)讓它們變成了函數行為。
          // 老寫法
          'assign' in Object // true

          // 新寫法
          Reflect.has(Object, 'assign') // true
          (4)Reflect對象的方法與Proxy對象的方法一一對應,只要是Proxy對象的方法,就能在Reflect對象上找到對應的方法。這就讓Proxy對象可以方便地調用對應的Reflect方法,完成默認行為,作為修改行為的基礎。也就是說,不管Proxy怎么修改默認行為,你總可以在Reflect上獲取默認行為。
          Proxy(target, {
          set: function(target, name, value, receiver) {
          var success = Reflect.set(target, name, value, receiver);
          if (success) {
          console.log('property ' + name + ' on ' + target + ' set to ' + value);
          }
          return success;
          }
          });
          上面代碼中,Proxy方法攔截target對象的屬性賦值行為。它采用Reflect.set方法將值賦值給對象的屬性,確保完成原有的行為,然后再部署額外的功能。
          下面是另一個例子。
          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);
          }
          });
          上面代碼中,每一個Proxy對象的攔截操作(get、delete、has),內部都調用對應的Reflect方法,保證原生行為能夠正常執(zhí)行。添加的工作,就是將每一個操作輸出一行日志。
          有了Reflect對象以后,很多操作會更易讀。
          // 老寫法
          Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1

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

          2、靜態(tài)方法

          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)

          上面這些方法的作用,大部分與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.get(1, 'foo') // 報錯
          Reflect.get(false, 'foo') // 報錯

          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會觸發(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
          上面代碼中,Proxy.set攔截里面使用了Reflect.set,而且傳入了receiver,導致觸發(fā)Proxy.defineProperty攔截。這是因為Proxy.set的receiver參數總是指向當前的 Proxy實例(即上例的obj),而Reflect.set一旦傳入receiver,就會將屬性賦值到receiver上面(即obj),導致觸發(fā)defineProperty攔截。如果Reflect.set沒有傳入receiver,那么就不會觸發(fā)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.set(1, 'foo', {}) // 報錯
          Reflect.set(false, 'foo', {}) // 報錯

          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()方法的第一個參數不是對象,會報錯。

          Reflect.deleteProperty(obj, name)

          Reflect.deleteProperty方法等同于delete obj[name],用于刪除對象的屬性。
          const myObj = { foo: 'bar' };

          // 舊寫法
          delete myObj.foo;

          // 新寫法
          Reflect.deleteProperty(myObj, 'foo');
          該方法返回一個布爾值。如果刪除成功,或者被刪除的屬性不存在,返回true;刪除失敗,被刪除的屬性依然存在,返回false。
          如果Reflect.deleteProperty()方法的第一個參數不是對象,會報錯。

          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.construct()方法的第一個參數不是函數,會報錯。

          Reflect.getPrototypeOf(obj)

          Reflect.getPrototypeOf方法用于讀取對象的__proto__屬性,對應Object.getPrototypeOf(obj)。
          const myObj = new FancyThing();

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

          // 新寫法
          Reflect.getPrototypeOf(myObj) === FancyThing.prototype;
          Reflect.getPrototypeOf和Object.getPrototypeOf的一個區(qū)別是,如果參數不是對象,Object.getPrototypeOf會將這個參數轉為對象,然后再運行,而Reflect.getPrototypeOf會報錯。
          Object.getPrototypeOf(1) // Number {[[PrimitiveValue]]: 0}
          Reflect.getPrototypeOf(1) // 報錯

          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
          如果第一個參數不是對象,Object.setPrototypeOf會返回第一個參數本身,而Reflect.setPrototypeOf會報錯。
          Object.setPrototypeOf(1, {})
          // 1

          Reflect.setPrototypeOf(1, {})
          // TypeError: Reflect.setPrototypeOf called on non-object
          如果第一個參數是undefined或null,Object.setPrototypeOf和Reflect.setPrototypeOf都會報錯。
          Object.setPrototypeOf(null, {})
          // TypeError: Object.setPrototypeOf called on null or undefined

          Reflect.setPrototypeOf(null, {})
          // TypeError: Reflect.setPrototypeOf called on non-object

          Reflect.apply(func, thisArg, args)

          Reflect.apply方法等同于Function.prototype.apply.call(func, thisArg, args),用于綁定this對象后執(zhí)行給定函數。
          一般來說,如果要綁定一個函數的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,用來為對象定義屬性。未來,后者會被逐漸廢除,請從現在開始就使用Reflect.defineProperty代替它。
          function MyDate() {
          /*…*/
          }

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

          // 新寫法
          Reflect.defineProperty(MyDate, 'now', {
          value: () => Date.now()
          });
          如果Reflect.defineProperty的第一個參數不是對象,就會拋出錯誤,比如Reflect.defineProperty(1, 'foo')。
          這個方法可以與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');
          Reflect.getOwnPropertyDescriptor和Object.getOwnPropertyDescriptor的一個區(qū)別是,如果第一個參數不是對象,Object.getOwnPropertyDescriptor(1, 'foo')不報錯,返回undefined,而Reflect.getOwnPropertyDescriptor(1, 'foo')會拋出錯誤,表示參數非法。

          Reflect.isExtensible (target)

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

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

          // 新寫法
          Reflect.isExtensible(myObject) // true
          如果參數不是對象,Object.isExtensible會返回false,因為非對象本來就是不可擴展的,而Reflect.isExtensible會報錯。
          Object.isExtensible(1) // false
          Reflect.isExtensible(1) // 報錯

          Reflect.preventExtensions(target)

          Reflect.preventExtensions對應Object.preventExtensions方法,用于讓一個對象變?yōu)椴豢蓴U展。它返回一個布爾值,表示是否操作成功。
          var myObject = {};

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

          // 新寫法
          Reflect.preventExtensions(myObject) // true
          如果參數不是對象,Object.preventExtensions在 ES5 環(huán)境報錯,在 ES6 環(huán)境返回傳入的參數,而Reflect.preventExtensions會報錯。
          // ES5 環(huán)境
          Object.preventExtensions(1) // 報錯

          // ES6 環(huán)境
          Object.preventExtensions(1) // 1

          // 新寫法
          Reflect.preventExtensions(1) // 報錯

          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)]
          如果Reflect.ownKeys()方法的第一個參數不是對象,會報錯。

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

          觀察者模式(Observer mode)指的是函數自動觀察數據對象,一旦對象有變化,函數就會自動執(zhí)行。
          const person = observable({
          name: '張三',
          age: 20
          });

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

          observe(print);
          person.name = '李四';
          // 輸出
          // 李四, 20
          上面代碼中,數據對象person是觀察目標,函數print是觀察者。一旦數據對象發(fā)生變化,print就會自動執(zhí)行。
          下面,使用 Proxy 寫一個觀察者模式的最簡單實現,即實現observable和observe這兩個函數。思路是observable函數返回一個原始對象的 Proxy 代理,攔截賦值操作,觸發(fā)充當觀察者的各個函數。
          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之中,會自動執(zhí)行所有觀察者。
          本文完~

          瀏覽 44
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩精品毛片免费视频 | 就要操妓女网 | 九精品| 国产孕妇孕交大片孕 | 91AV极品视觉盛宴 |