最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳細探究ES6之Proxy代理

 更新時間:2016年07月22日 10:11:51   投稿:daisy  
ES6中提出了一個新的特性,就是proxy,用來攔截在一個對象上的指定操作,這個功能非常的有用。本文會介紹 Proxy的使用方式及列舉具體實例解釋 Proxy 的使用場景。

前言

在ES6中,Proxy構造器是一種可訪問的全局對象,使用它你可以在對象與各種操作對象的行為之間收集有關請求操作的各種信息,并返回任何你想做的。ES6中的箭頭函數(shù)、數(shù)組解構、rest 參數(shù)等特性一經(jīng)實現(xiàn)就廣為流傳,但類似 Proxy 這樣的特性卻很少見到有開發(fā)者在使用,一方面在于瀏覽器的兼容性,另一方面也在于要想發(fā)揮這些特性的優(yōu)勢需要開發(fā)者深入地理解其使用場景。就我個人而言是非常喜歡 ES6 的 Proxy,因為它讓我們以簡潔易懂的方式控制了外部對對象的訪問。在下文中,首先我會介紹 Proxy 的使用方式,然后列舉具體實例解釋 Proxy 的使用場景。

Proxy,見名知意,其功能非常類似于設計模式中的代理模式,該模式常用于三個方面:

1.和監(jiān)視外部對對象的訪問

2.函數(shù)或類的復雜度

3.操作前對操作進行校驗或對所需資源進行管理

在支持 Proxy 的瀏覽器環(huán)境中,Proxy 是一個全局對象,可以直接使用。Proxy(target, handler) 是一個構造函數(shù),target 是被代理的對象,handlder 是聲明了各類代理操作的對象,最終返回一個代理對象。外界每次通過代理對象訪問 target 對象的屬性時,就會經(jīng)過 handler 對象,從這個流程來看,代理對象很類似 middleware(中間件)。那么 Proxy 可以攔截什么操作呢?最常見的就是 get(讀?。?、set(修改)對象屬性等操作,完整的可攔截操作列表請點擊這里。此外,Proxy 對象還提供了一個 revoke 方法,可以隨時注銷所有的代理操作。在我們正式介紹 Proxy 之前,建議你對 Reflect 有一定的了解,它也是一個 ES6 新增的全局對象。

Basic

const target = { 
  name: 'Billy Bob',
  age: 15
};

const handler = { 
  get(target, key, proxy) {
    const today = new Date();
    console.log(`GET request made for ${key} at ${today}`);

    return Reflect.get(target, key, proxy);
  }
};

const proxy = new Proxy(target, handler);
proxy.name;
// => "GET request made for name at Thu Jul 21 2016 15:26:20 GMT+0800 (CST)"
// => "Billy Bob"

在上面的代碼中,我們首先定義了一個被代理的目標對象 target,然后聲明了包含所有代理操作的 handler 對象,接下來使用 Proxy(target, handler) 創(chuàng)建代理對象 proxy,此后所有使用 proxytarget 屬性的訪問都會經(jīng)過 handler 的處理。

1. 抽離校驗模塊

讓我們從一個簡單的類型校驗開始做起,這個示例演示了如何使用 Proxy 保障數(shù)據(jù)類型的準確性:

let numericDataStore = { 
  count: 0,
  amount: 1234,
  total: 14
};

numericDataStore = new Proxy(numericDataStore, { 
  set(target, key, value, proxy) {
    if (typeof value !== 'number') {
      throw Error("Properties in numericDataStore can only be numbers");
    }
    return Reflect.set(target, key, value, proxy);
  }
});

// 拋出錯誤,因為 "foo" 不是數(shù)值
numericDataStore.count = "foo";

// 賦值成功
numericDataStore.count = 333;

如果要直接為對象的所有屬性開發(fā)一個校驗器可能很快就會讓代碼結構變得臃腫,使用 Proxy 則可以將校驗器從核心邏輯分離出來自成一體:

function createValidator(target, validator) { 
  return new Proxy(target, {
    _validator: validator,
    set(target, key, value, proxy) {
      if (target.hasOwnProperty(key)) {
        let validator = this._validator[key];
        if (!!validator(value)) {
          return Reflect.set(target, key, value, proxy);
        } else {
          throw Error(`Cannot set ${key} to ${value}. Invalid.`);
        }
      } else {
        throw Error(`${key} is not a valid property`)
      }
    }
  });
}

const personValidators = { 
  name(val) {
    return typeof val === 'string';
  },
  age(val) {
    return typeof age === 'number' && age > 18;
  }
}
class Person { 
  constructor(name, age) {
    this.name = name;
    this.age = age;
    return createValidator(this, personValidators);
  }
}

const bill = new Person('Bill', 25);

// 以下操作都會報錯
bill.name = 0; 
bill.age = 'Bill'; 
bill.age = 15; 

通過校驗器和主邏輯的分離,你可以無限擴展 personValidators 校驗器的內(nèi)容,而不會對相關的類或函數(shù)造成直接破壞。更復雜一點,我們還可以使用 Proxy 模擬類型檢查,檢查函數(shù)是否接收了類型和數(shù)量都正確的參數(shù):

let obj = { 
  pickyMethodOne: function(obj, str, num) { /* ... */ },
  pickyMethodTwo: function(num, obj) { /*... */ }
};

const argTypes = { 
  pickyMethodOne: ["object", "string", "number"],
  pickyMethodTwo: ["number", "object"]
};

obj = new Proxy(obj, { 
  get: function(target, key, proxy) {
    var value = target[key];
    return function(...args) {
      var checkArgs = argChecker(key, args, argTypes[key]);
      return Reflect.apply(value, target, args);
    };
  }
});

function argChecker(name, args, checkers) { 
  for (var idx = 0; idx < args.length; idx++) {
    var arg = args[idx];
    var type = checkers[idx];
    if (!arg || typeof arg !== type) {
      console.warn(`You are incorrectly implementing the signature of ${name}. Check param ${idx + 1}`);
    }
  }
}

obj.pickyMethodOne(); 
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 1
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 2
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 3

obj.pickyMethodTwo("wopdopadoo", {}); 
// > You are incorrectly implementing the signature of pickyMethodTwo. Check param 1

// No warnings logged
obj.pickyMethodOne({}, "a little string", 123); 
obj.pickyMethodOne(123, {});

2. 私有屬性

在 JavaScript 或其他語言中,大家會約定俗成地在變量名之前添加下劃線 _ 來表明這是一個私有屬性(并不是真正的私有),但我們無法保證真的沒人會去訪問或修改它。在下面的代碼中,我們聲明了一個私有的 apiKey,便于 api 這個對象內(nèi)部的方法調(diào)用,但不希望從外部也能夠訪問 api._apiKey:

var api = { 
  _apiKey: '123abc456def',
  /* mock methods that use this._apiKey */
  getUsers: function(){}, 
  getUser: function(userId){}, 
  setUser: function(userId, config){}
};

// logs '123abc456def';
console.log("An apiKey we want to keep private", api._apiKey);

// get and mutate _apiKeys as desired
var apiKey = api._apiKey; 
api._apiKey = '987654321';

很顯然,約定俗成是沒有束縛力的。使用 ES6 Proxy 我們就可以實現(xiàn)真實的私有變量了,下面針對不同的讀取方式演示兩個不同的私有化方法。

第一種方法是使用 set / get 攔截讀寫請求并返回 undefined:

let api = { 
  _apiKey: '123abc456def',
  getUsers: function(){ }, 
  getUser: function(userId){ }, 
  setUser: function(userId, config){ }
};

const RESTRICTED = ['_apiKey'];
api = new Proxy(api, { 
  get(target, key, proxy) {
    if(RESTRICTED.indexOf(key) > -1) {
      throw Error(`${key} is restricted. Please see api documentation for further info.`);
    }
    return Reflect.get(target, key, proxy);
  },
  set(target, key, value, proxy) {
    if(RESTRICTED.indexOf(key) > -1) {
      throw Error(`${key} is restricted. Please see api documentation for further info.`);
    }
    return Reflect.get(target, key, value, proxy);
  }
});

// 以下操作都會拋出錯誤
console.log(api._apiKey);
api._apiKey = '987654321'; 

第二種方法是使用 has 攔截 in 操作:

var api = { 
  _apiKey: '123abc456def',
  getUsers: function(){ }, 
  getUser: function(userId){ }, 
  setUser: function(userId, config){ }
};

const RESTRICTED = ['_apiKey'];
api = new Proxy(api, { 
  has(target, key) {
    return (RESTRICTED.indexOf(key) > -1) ?
      false :
      Reflect.has(target, key);
  }
});

// these log false, and `for in` iterators will ignore _apiKey
console.log("_apiKey" in api);

for (var key in api) { 
  if (api.hasOwnProperty(key) && key === "_apiKey") {
    console.log("This will never be logged because the proxy obscures _apiKey...")
  }
}

3. 訪問日志

對于那些調(diào)用頻繁、運行緩慢或占用執(zhí)行環(huán)境資源較多的屬性或接口,開發(fā)者會希望記錄它們的使用情況或性能表現(xiàn),這個時候就可以使用 Proxy 充當中間件的角色,輕而易舉實現(xiàn)日志功能:

let api = { 
  _apiKey: '123abc456def',
  getUsers: function() { /* ... */ },
  getUser: function(userId) { /* ... */ },
  setUser: function(userId, config) { /* ... */ }
};

function logMethodAsync(timestamp, method) { 
  setTimeout(function() {
    console.log(`${timestamp} - Logging ${method} request asynchronously.`);
  }, 0)
}

api = new Proxy(api, { 
  get: function(target, key, proxy) {
    var value = target[key];
    return function(...arguments) {
      logMethodAsync(new Date(), key);
      return Reflect.apply(value, target, arguments);
    };
  }
});

api.getUsers();

4. 預警和攔截

假設你不想讓其他開發(fā)者刪除 noDelete 屬性,還想讓調(diào)用 oldMethod 的開發(fā)者了解到這個方法已經(jīng)被廢棄了,或者告訴開發(fā)者不要修改 doNotChange 屬性,那么就可以使用 Proxy 來實現(xiàn):

let dataStore = { 
  noDelete: 1235,
  oldMethod: function() {/*...*/ },
  doNotChange: "tried and true"
};

const NODELETE = ['noDelete']; 
const NOCHANGE = ['doNotChange'];
const DEPRECATED = ['oldMethod']; 

dataStore = new Proxy(dataStore, { 
  set(target, key, value, proxy) {
    if (NOCHANGE.includes(key)) {
      throw Error(`Error! ${key} is immutable.`);
    }
    return Reflect.set(target, key, value, proxy);
  },
  deleteProperty(target, key) {
    if (NODELETE.includes(key)) {
      throw Error(`Error! ${key} cannot be deleted.`);
    }
    return Reflect.deleteProperty(target, key);

  },
  get(target, key, proxy) {
    if (DEPRECATED.includes(key)) {
      console.warn(`Warning! ${key} is deprecated.`);
    }
    var val = target[key];

    return typeof val === 'function' ?
      function(...args) {
        Reflect.apply(target[key], target, args);
      } :
      val;
  }
});

// these will throw errors or log warnings, respectively
dataStore.doNotChange = "foo"; 
delete dataStore.noDelete; 
dataStore.oldMethod();

5. 過濾操作

某些操作會非常占用資源,比如傳輸大文件,這個時候如果文件已經(jīng)在分塊發(fā)送了,就不需要在對新的請求作出相應(非絕對),這個時候就可以使用 Proxy 對當請求進行特征檢測,并根據(jù)特征過濾出哪些是不需要響應的,哪些是需要響應的。下面的代碼簡單演示了過濾特征的方式,并不是完整代碼,相信大家會理解其中的妙處:

let obj = { 
  getGiantFile: function(fileId) {/*...*/ }
};

obj = new Proxy(obj, { 
  get(target, key, proxy) {
    return function(...args) {
      const id = args[0];
      let isEnroute = checkEnroute(id);
      let isDownloading = checkStatus(id);   
      let cached = getCached(id);

      if (isEnroute || isDownloading) {
        return false;
      }
      if (cached) {
        return cached;
      }
      return Reflect.apply(target[key], target, args);
    }
  }
});

6. 中斷代理

Proxy 支持隨時取消對 target 的代理,這一操作常用于完全封閉對數(shù)據(jù)或接口的訪問。在下面的示例中,我們使用了 Proxy.revocable 方法創(chuàng)建了可撤銷代理的代理對象:

let sensitiveData = { username: 'devbryce' };
const {sensitiveData, revokeAccess} = Proxy.revocable(sensitiveData, handler);
function handleSuspectedHack(){ 
  revokeAccess();
}

// logs 'devbryce'
console.log(sensitiveData.username);
handleSuspectedHack();
// TypeError: Revoked
console.log(sensitiveData.username);

Decorator

ES7 中實現(xiàn)的 Decorator,相當于設計模式中的裝飾器模式。如果簡單地區(qū)分 Proxy Decorator 的使用場景,可以概括為:Proxy 的核心作用是控制外界對被代理者內(nèi)部的訪問,Decorator 的核心作用是增強被裝飾者的功能。只要在它們核心的使用場景上做好區(qū)別,那么像是訪問日志這樣的功能,雖然本文使用了 Proxy 實現(xiàn),但也可以使用 Decorator 實現(xiàn),開發(fā)者可以根據(jù)項目的需求、團隊的規(guī)范、自己的偏好自由選擇。

總結

ES6 的 Proxy還是非常實用的,看似簡單的特性,卻有極大的用處。希望給大家學習ES6有所幫助。

相關文章

  • 前端使用xlsx庫導出帶有樣式的excel文件

    前端使用xlsx庫導出帶有樣式的excel文件

    這篇文章主要為大家介紹了前端使用xlsx庫導出帶有樣式的excel文件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • 微信小程序開發(fā)圖片拖拽實例詳解

    微信小程序開發(fā)圖片拖拽實例詳解

    這篇文章主要介紹了微信小程序開發(fā)圖片拖拽實例詳解的相關資料,需要的朋友可以參考下
    2017-05-05
  • 微信小程序 scroll-view組件實現(xiàn)列表頁實例代碼

    微信小程序 scroll-view組件實現(xiàn)列表頁實例代碼

    這篇文章主要介紹了微信小程序 scroll-view組件實現(xiàn)列表頁實例代碼的相關資料,scroll-view組件介紹scroll-view是微信小程序提供的可滾動視圖組件,其主要作用是可以用來做手機端經(jīng)常會看到的上拉加載 ,需要的朋友可以參考下
    2016-12-12
  • JavaScript阻止事件冒泡的方法

    JavaScript阻止事件冒泡的方法

    這篇文章主要介紹了基于JavaScript阻止事件冒泡,事件冒泡?開始時由最具體的元素接收,然后逐級向上傳播到到?DOM?最頂層節(jié)點。更多詳細內(nèi)容請需要的小伙伴參考下面文章的具體內(nèi)容希望對你有所幫助
    2021-12-12
  • JS開發(fā)Blob和FileReader構造函數(shù)使用示例詳解

    JS開發(fā)Blob和FileReader構造函數(shù)使用示例詳解

    這篇文章主要為大家介紹了JS開發(fā)Blob和FileReader構造函數(shù)使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • JS中自定義事件與觀察者模式詳解

    JS中自定義事件與觀察者模式詳解

    這篇文章主要為大家介紹了JS中自定義事件與觀察者模式的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • Google 地圖API資料整理及詳細介紹

    Google 地圖API資料整理及詳細介紹

    本文主要介紹Google 地圖API資料,這里整理了Google地圖API的基本知識,并詳解每個函數(shù)的意思,有需要的小伙伴可以參考下
    2016-08-08
  • JS仿照3D手辦模型展臺實現(xiàn)示例詳解

    JS仿照3D手辦模型展臺實現(xiàn)示例詳解

    這篇文章主要為大家介紹了JS 實現(xiàn)偽3D手辦模型展臺示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • 詳解微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示

    詳解微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示

    這篇文章主要介紹了微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示的相關資料,需要的朋友可以參考下
    2017-05-05
  • 前端使用koa實現(xiàn)大文件分片上傳

    前端使用koa實現(xiàn)大文件分片上傳

    這篇文章主要為大家介紹了前端使用koa實現(xiàn)大文件分片上傳示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08

最新評論

长春市| 疏勒县| 长沙市| 城步| 威远县| 翼城县| 凤城市| 石家庄市| 江北区| 扶绥县| 云浮市| 吉安县| 全椒县| 衡水市| 萨迦县| 德庆县| 巫溪县| 淮安市| 会理县| 博爱县| 新巴尔虎右旗| 普格县| 洛宁县| 延庆县| 礼泉县| 天台县| 锡林郭勒盟| 盐山县| 仁布县| 兰溪市| 阳泉市| 宾阳县| 台北市| 诸城市| 陇川县| 霍邱县| 田林县| 伽师县| 阿拉善右旗| 友谊县| 商水县|