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

          JavaScript中的代理和反射

          共 10605字,需瀏覽 22分鐘

           ·

          2021-10-26 10:41

          來源 | https://segmentfault.com/a/1190000040829478


          什么是反射

          反射這個概念在很多編程語言中都存在,像Java,C#。
          在面向?qū)ο缶幊讨校话銜葘㈩惡头椒ǘx好,然后創(chuàng)建對象顯式調(diào)用方法,比如下面的例子:
          public class User{   private String name;   private Date birthday;       //....   public int calculateAgeByBirthday(){            // .....   }}// 調(diào)用    User u = new User("jack", new Date());u.calculateAgeByBirthday();

          上面這種調(diào)用方式我們比較熟悉,不過當你想編寫一些抽象框架時(框架又需要與業(yè)務(wù)定義的類進行互操作),由于你不知道業(yè)務(wù)類的成員和方法,這時反射動態(tài)獲取成員變量或調(diào)用方法。

          下面例子,我們利用反射將json轉(zhuǎn)換為Java對象。

          public static class User {    private String name;    public String getName() {    return name;    }   public void setName(String name) {     this.name = name;   }}
          // 使用反射調(diào)用對象setter方法。public static <T> T fill(Class<T> userClass, Map<String, Object> json) throws Exception { Field[] fields = userClass.getDeclaredFields(); T user = userClass.newInstance(); for (Field field : fields) { // 首字母大寫 String name = field.getName(); char[] arr = name.toCharArray(); arr[0] = Character.toUpperCase(arr[0]); System.out.println(new String(arr)); Method method = userClass.getDeclaredMethod("set" + new String(arr), field.getType()); Object returnValue = method.invoke(user, json.get(name)); } return user;}

          JavaScript中Reflect

          JavaScript在ES6提供了反射內(nèi)置對象Reflect,但JavaScript里面的反射和Java反射有所不同。先看下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)

          Reflect.get(target, name, receiver)

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

          const obj = {  name: 'jack',  age: 12,  get userInfo() {    return this.name + ' age is ' + this.age;  }}
          Reflect.get(obj, 'name') // jackReflect.get(obj, 'age') // 12Reflect.get(obj, 'userInfo') // jack age is 12
          // 如果傳遞了receiver參數(shù),在調(diào)用userInfo()函數(shù)時,this是指向receiver對象。const receiverObj = { name: '小明', age: 22};
          Reflect.get(obj, 'userInfo', receiverObj) // 小明 age is 22

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

          const obj = {  name: 'jack',  age: 12,  set updateAge(value) {    return this.age = value;  },}Reflect.set(obj, 'age', 22);obj.age // 22
          // 如果傳遞了receiver參數(shù),在調(diào)用updateAge()函數(shù)時,this是指向receiver對象。const receiverObj = { age: 0};
          Reflect.set(obj, 'updateAge', 10, receiverObj) // obj.age // 22receiverObj.age // 10

          Reflect.has(obj, name)

          Reflect.has方法相當于name in obj里面的in運算符。

          const obj = {  name: 'jack',}obj in name // trueReflect.has(obj, 'name') // true

          Reflect.deleteProperty(obj, name)

          Reflect.deleteProperty方法相當于delete obj[name],用于刪除對象的屬性。如果刪除成功,或者被刪除的屬性不存在,返回true;刪除失敗,被刪除的屬性依然存在,返回false。

          const obj = {  name: 'jack',}delete obj.name Reflect.deleteProperty(obj, 'name')

          Reflect.construct(target, args)

          Reflect.construct方法等同于new target(...args)。

          function User(name){  this.name = name;}const user = new User('jack');Reflect.construct(User, ['jack']);

          Reflect.getPrototypeOf(obj)

          Reflect.getPrototypeOf方法用于讀取對象的__proto__屬性。

          Reflect.setPrototypeOf(obj, newProto)

          Reflect.setPrototypeOf方法用于設(shè)置目標對象的原型(prototype)。返回一個布爾值,表示是否設(shè)置成功。

          const obj = {  name: 'jack',}Reflect.setPrototypeOf(obj, Array.prototype);obj.length // 0

          Reflect.apply(func, thisArg, args)

          Reflect.apply方法相當于Function.prototype.apply.call(func, thisArg, args),用于綁定this對象后執(zhí)行給定函數(shù)。

          const nums = [1,2,3,4,5];const min = Math.max.apply(Math, nums);// 通過 Reflect.apply 調(diào)用const min = Reflect.apply(Math.min, Math, nums);

          Reflect.defineProperty(target, propertyKey, attributes)

          Reflect.defineProperty方法相當于Object.defineProperty,用來為對象定義屬性。

          const obj = {};Object.defineProperty(obj, 'property', {  value: 0,  writable: false});
          Reflect.defineProperty(obj, 'property', { value: 0, writable: false});

          Reflect.getOwnPropertyDescriptor(target, propertyKey)

          獲取指定屬性的描述對象。

          Reflect.isExtensible (target)

          返回一個布爾值,表示當前對象是否可擴展。

          Reflect.preventExtensions(target)

          用于讓一個對象變?yōu)椴豢蓴U展。它返回一個布爾值,表示是否操作成功。

          Reflect.ownKeys (target)

          Reflect.ownKeys方法用于返回對象的所有屬性。

          const obj = {  name: 'jack',  age: 12,  get userInfo() {    return this.name + ' age is ' + this.age;  }}Object.getOwnPropertyNames(obj)Reflect.ownKeys(obj) // ['name', 'age', 'userInfo']

          JavaScript中Proxy

          代理在編程中很有用,它可以在目標對象之前增加一層“攔截”實現(xiàn)一些通用邏輯。

          Proxy 構(gòu)造函數(shù) Proxy(target, handler) 參數(shù):

          • target:代理的目標對象,它可以是任何類型的對象,包括內(nèi)置的數(shù)組,函數(shù),代理對象。

          • handler:它是一個對象,它的屬性提供了某些操作發(fā)生時的處理函數(shù)。

          const user = {name: 'hello'}const proxy = new Proxy(user, {  get: function(target, property) { // 讀取屬性時觸發(fā)    return 'hi';  }});proxy.name // 'hi'

          Proxy中支持的攔截操作

          • handler.get(target, property, receiver)

          • handler.set(target, property, value, receiver)

          • handler.has(target, property)

          • handler.defineProperty(target, property, descriptor)

          • handler.deleteProperty(target, property)

          • handler.getOwnPropertyDescriptor(target, prop)

          • handler.getPrototypeOf(target)

          • handler.setPrototypeOf(target, prototype)

          • handler.isExtensible(target)

          • handler.ownKeys(target)

          • handler.preventExtensions(target)

          • handler.apply(target, thisArg, argumentsList)

          • handler.construct(target, argumentsList, newTarget)

          get()

          用于攔截某個屬性的讀取操作,可以接受三個參數(shù),依次為目標對象、屬性名和 proxy 實例本身,其中最后一個參數(shù)可選。

          const user = {  name: 'jack'}// 只有屬性存在才返回值,否則拋出異常。const proxy = new Proxy(user, {  get: function(target, property) {    if (!(property in target)) {       throw new ReferenceError(`${property} does not exist.`);    }    return target[property];  }});proxy.name // jackproxy.age // ReferenceError: age does not exist.

          我們可以定義一些公共代理對象,然后讓子對象繼承。

          // 只有屬性存在才返回值,否則拋出異常。const proxy = new Proxy({}, {  get: function(target, property) {    if (!(property in target)) {       throw new ReferenceError(`${property} does not exist.`);    }    return target[property];  }});let obj = Object.create(proxy);obj.name = 'hello'obj.name // helloobj.age // ReferenceError: age does not exist.

          set()

          用來攔截某個屬性的賦值操作,可以接受四個參數(shù),依次為目標對象、屬性名、屬性值和 Proxy 實例本身,其中最后一個參數(shù)可選。

          // 字符類型的屬性長度校驗let sizeValidator = {  set: function(target, property, value, receiver) {    if (typeof value == 'string' && value.length > 5) {       throw new RangeError('Cannot exceed 5 character.');    }    target[property] = value;    return true;  }};
          const validator = new Proxy({}, sizeValidator);let obj = Object.create(validator);obj.name = '123456' // RangeError: Cannot exceed 5 character.obj.age = 12 // 12

          has()

          用來攔截HasProperty操作,即判斷對象是否具有某個屬性時,這個方法會生效。如in運算符。

          它接受兩個參數(shù),分別是目標對象、需查詢的屬性名。

          const handler = {  has (target, key) {    if (key[0] === '_') {      return false;    }    return key in target;  }};var target = { _prop: 'foo', prop: 'foo' };var proxy = new Proxy(target, handler);'_prop' in proxy // false

          defineProperty()

          defineProperty()方法攔截了Object.defineProperty()操作。

          deleteProperty()

          用于攔截delete操作,如果這個方法拋出錯誤或者返回false,當前屬性就無法被delete命令刪除。

          getOwnPropertyDescriptor()

          getOwnPropertyDescriptor()方法攔截Object.getOwnPropertyDescriptor(),返回一個屬性描述對象或者undefined。

          getPrototypeOf()

          主要用來攔截獲取對象原型,攔截的操作如下:

          • Object.getPrototypeOf()

          • Reflect.getPrototypeOf()

          • __proto__

          • Object.prototype.isPrototypeOf()

          • instanceof

          const obj = {};const proto = {};const handler = {    getPrototypeOf(target) {        console.log(target === obj);   // true        console.log(this === handler); // true        return proto;    }};
          const p = new Proxy(obj, handler);console.log(Object.getPrototypeOf(p) === proto); // true

          setPrototypeOf()

          主要用來攔截Object.setPrototypeOf()方法。

          const handlerReturnsFalse = {    setPrototypeOf(target, newProto) {        return false;    }};
          const newProto = {}, target = {};
          const p1 = new Proxy(target, handlerReturnsFalse);Object.setPrototypeOf(p1, newProto); // throws a TypeErrorReflect.setPrototypeOf(p1, newProto); // returns false

          isExtensible()

          方法攔截Object.isExtensible()操作。

          const p = new Proxy({}, {  isExtensible: function(target) {    console.log('called');    return true;//也可以return 1;等表示為true的值  }});
          console.log(Object.isExtensible(p)); // "called" // true

          ownKeys()

          用來攔截對象自身屬性的讀取操作。具體來說,攔截以下操作。

          • Object.getOwnPropertyNames()

          • Object.getOwnPropertySymbols()

          • Object.keys()

          • for...in循環(huán)。

          const p = new Proxy({}, {  ownKeys: function(target) {    console.log('called');    return ['a', 'b', 'c'];  }});
          console.log(Object.getOwnPropertyNames(p)); // "called"

          preventExtensions()

          用來攔截Object.preventExtensions()。該方法必須返回一個布爾值,否則會被自動轉(zhuǎn)為布爾值。

          這個方法有一個限制,只有目標對象不可擴展時(即Object.isExtensible(proxy)為false),proxy.preventExtensions才能返回true,否則會報錯。

          const p = new Proxy({}, {  preventExtensions: function(target) {    console.log('called');    Object.preventExtensions(target);    return true;  }});
          console.log(Object.preventExtensions(p)); // "called" // false

          apply()

          apply方法攔截以下操作。

          • proxy(...args)

          • Function.prototype.apply() 和 Function.prototype.call()

          • Reflect.apply()

          它接受三個參數(shù),分別是目標對象、目標對象的上下文對象(this)和目標對象的參數(shù)數(shù)組。

          const handler = {  apply (target, ctx, args) {    return Reflect.apply(...arguments);  }};

          例子

          const target = function () { };const handler = {  apply: function (target, thisArg, argumentsList) {    console.log('called: ' + argumentsList.join(', '));    return argumentsList[0] + argumentsList[1] + argumentsList[2];  }};
          const p = new Proxy(target, handler);p(1,2,3) // "called: 1, 2, 3" 6

          construct()

          用于攔截new命令,下面是攔截對象的寫法:

          const handler = {  construct (target, args, newTarget) {    return new target(...args);  }};

          它方法接受三個參數(shù)。

          • target:目標對象。

          • args:構(gòu)造函數(shù)的參數(shù)數(shù)組。

          • newTarget:創(chuàng)造實例對象時,new命令作用的構(gòu)造函數(shù)。

          注意:方法返回的必須是一個對象,目標對象必須是函數(shù),否則就會報錯。

          const p = new Proxy(function() {}, {  construct: function(target, argumentsList) {    return 0;  }});
          new p() // 返回值不是對象,報錯
          const p = new Proxy({}, { construct: function(target, argumentsList) { return {}; }});new p() //目標對象不是函數(shù),報錯

          觀察者模式

          觀察者是一種很常用的模式,它的定義是當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。

          我們使用Proxy 來實現(xiàn)一個例子,當觀察對象狀態(tài)變化時,讓觀察函數(shù)自動執(zhí)行。

          觀察者函數(shù),包裹觀察目標,添加觀察函數(shù)。

          • observable包裹觀察目標,返回一個Proxy對象。

          • observe 添加觀察函數(shù)到隊列。

          const queuedObservers = new Set();
          const observe = fn => queuedObservers.add(fn);const observable = obj => new Proxy(obj, {set});// 屬性改變時,自動執(zhí)行觀察函數(shù)。function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); queuedObservers.forEach(observer => observer()); return result;}

          例子

          const user = observable({  name: 'jack',  age: 20});
          function userInfo() { console.log(`${user.name}, ${user.age}`)}
          observe(userInfo);user.name = '小明'; // 小明, 20

          小結(jié)

          本文要點回顧,歡迎留言交流。

          • JavaScript中的內(nèi)置Reflect。

          • JavaScript中的內(nèi)置Proxy。

          • Proxy實現(xiàn)觀察者模式。

          感謝你的時間,謝謝閱讀。



          學習更多技能

          請點擊下方公眾號

          瀏覽 50
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  极品人妻疯狂3p超刺激 | 日韩在线色| 欧美最大操逼在线 | 大香蕉久久在线 | 国产黄色片网站 |