1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
      JavaScript的Proxy可以做哪些有意思的事兒

      Proxy是什么

      創(chuàng)新互聯(lián)專注于企業(yè)成都全網(wǎng)營銷、網(wǎng)站重做改版、白云網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5頁面制作商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價格優(yōu)惠性價比高,為白云等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

      首先,我們要清楚,Proxy是什么意思,這個單詞翻譯過來,就是 代理。

      可以理解為,有一個很火的明星,開通了一個微博賬號,這個賬號非常活躍,回復(fù)粉絲、到處點(diǎn)贊之類的,但可能并不是真的由本人在維護(hù)的。

      而是在背后有一個其他人 or 團(tuán)隊(duì)來運(yùn)營,我們就可以稱他們?yōu)榇砣耍驗(yàn)樗麄儼l(fā)表的微博就代表了明星本人的意思。

      P.S. 強(qiáng)行舉例子,因?yàn)楸救瞬蛔沸牵皇遣聹y可能會有這樣的運(yùn)營團(tuán)隊(duì)

      這個代入到JavaScript當(dāng)中來,就可以理解為對對象或者函數(shù)的代理操作。

      JavaScript中的Proxy

      Proxy是ES6中提供的新的API,可以用來定義對象各種基本操作的自定義行為 (在文檔中被稱為traps,我覺得可以理解為一個針對對象各種行為的鉤子),拿它可以做很多有意思的事情,在我們需要對一些對象的行為進(jìn)行控制時將變得非常有效。

      Proxy的語法

      創(chuàng)建一個Proxy的實(shí)例需要傳入兩個參數(shù)

      1.target 要被代理的對象,可以是一個object或者function

      2.handlers對該代理對象的各種操作行為處理

      let target = {}
      let handlers = {} // do nothing
      let proxy = new Proxy(target, handlers)
      proxy.a = 123
      console.log(target.a) // 123

      在第二個參數(shù)為空對象的情況下,基本可以理解為是對第一個參數(shù)做的一次淺拷貝
      (Proxy必須是淺拷貝,如果是深拷貝則會失去了代理的意義)

      Traps(各種行為的代理)

      就像上邊的示例代碼一樣,如果沒有定義對應(yīng)的trap,則不會起任何作用,相當(dāng)于直接操作了target。

      當(dāng)我們寫了某個trap以后,在做對應(yīng)的動作時,就會觸發(fā)我們的回調(diào)函數(shù),由我們來控制被代理對象的行為。

      最常用的兩個trap應(yīng)該就是get和set了。

      早年JavaScript有著在定義對象時針對某個屬性進(jìn)行設(shè)置getter、setter:

      let obj = {
      _age: 18,
      get age () {
      return `I'm ${this._age} years old`
      },
      set age (val) {
      this._age = Number(val)
      }
      }
      console.log(obj.age) // I'm 18 years old
      obj.age = 19
      console.log(obj.age) // I'm 19 years old

      就像這段代碼描述的一樣,我們設(shè)置了一個屬性_age,然后又設(shè)置了一個get age和set age。

      然后我們可以直接調(diào)用obj.age來獲取一個返回值,也可以對其進(jìn)行賦值。

      這么做有幾個缺點(diǎn):

      1.針對每一個要代理的屬性都要編寫對應(yīng)的getter、setter。

      2.必須還要存在一個存儲真實(shí)值的key(如果我們直接在getter里邊調(diào)用this.age則會出現(xiàn)堆棧溢出的情況,因?yàn)闊o論何時調(diào)用this.age進(jìn)行取值都會觸發(fā)getter)。

      Proxy很好的解決了這兩個問題:

      let target = { age: 18, name: 'Niko Bellic' }
      let handlers = {
      get (target, property) {
      return `${property}: ${target[property]}`
      },
      set (target, property, value) {
      target[property] = value
      }
      }
      let proxy = new Proxy(target, handlers)
      proxy.age = 19
      console.log(target.age, proxy.age) // 19, age : 19
      console.log(target.name, proxy.name) // Niko Bellic, name: Niko Bellic

      我們通過創(chuàng)建get、set兩個trap來統(tǒng)一管理所有的操作,可以看到,在修改proxy的同時,target的內(nèi)容也被修改,而且我們對proxy的行為進(jìn)行了一些特殊的處理。

      而且我們無需額外的用一個key來存儲真實(shí)的值,因?yàn)槲覀冊趖rap內(nèi)部操作的是target對象,而不是proxy對象。

      拿Proxy來做些什么

      因?yàn)樵谑褂昧薖roxy后,對象的行為基本上都是可控的,所以我們能拿來做一些之前實(shí)現(xiàn)起來比較復(fù)雜的事情。

      在下邊列出了幾個簡單的適用場景。

      解決對象屬性為undefined的問題

      在一些層級比較深的對象屬性獲取中,如何處理undefined一直是一個痛苦的過程,如果我們用Proxy可以很好的兼容這種情況。

      (() => {
      let target = {}
      let handlers = {
      get: (target, property) => {
      target[property] = (property in target) ? target[property] : {}
      if (typeof target[property] === 'object') {
      return new Proxy(target[property], handlers)
      }
      return target[property]
      }
      }
      let proxy = new Proxy(target, handlers)
      console.log('z' in proxy.x.y) // false (其實(shí)這一步已經(jīng)針對`target`創(chuàng)建了一個x.y的屬性)
      proxy.x.y.z = 'hello'
      console.log('z' in proxy.x.y) // true
      console.log(target.x.y.z) // hello
      })()

      我們代理了get,并在里邊進(jìn)行邏輯處理,如果我們要進(jìn)行g(shù)et的值來自一個不存在的key,則我們會在target中創(chuàng)建對應(yīng)個這個key,然后返回一個針對這個key的代理對象。

      這樣就能夠保證我們的取值操作一定不會拋出can not get xxx from undefined
      但是這會有一個小缺點(diǎn),就是如果你確實(shí)要判斷這個key是否存在只能夠通過in操作符來判斷,而不能夠直接通過get來判斷。

      普通函數(shù)與構(gòu)造函數(shù)的兼容處理

      如果我們提供了一個Class對象給其他人,或者說一個ES5版本的構(gòu)造函數(shù)。
      如果沒有使用new關(guān)鍵字來調(diào)用的話,Class對象會直接拋出異常,而ES5中的構(gòu)造函數(shù)this指向則會變?yōu)檎{(diào)用函數(shù)時的作用域。
      我們可以使用apply這個trap來兼容這種情況:

      class Test {
      constructor (a, b) {
      console.log('constructor', a, b)
      }
      }
      // Test(1, 2) // throw an error
      let proxyClass = new Proxy(Test, {
      apply (target, thisArg, argumentsList) {
      // 如果想要禁止使用非new的方式來調(diào)用函數(shù),直接拋出異常即可
      // throw new Error(`Function ${target.name} cannot be invoked without 'new'`)
      return new (target.bind(thisArg, ...argumentsList))()
      }
      })
      proxyClass(1, 2) // constructor 1 2

      我們使用了apply來代理一些行為,在函數(shù)調(diào)用時會被觸發(fā),因?yàn)槲覀兠鞔_的知道,代理的是一個Class或構(gòu)造函數(shù),所以我們直接在apply中使用new關(guān)鍵字來調(diào)用被代理的函數(shù)。

      以及如果我們想要對函數(shù)進(jìn)行限制,禁止使用new關(guān)鍵字來調(diào)用,可以用另一個trap:construct

      function add (a, b) {
      return a + b
      }
      let proxy = new Proxy(add, {
      construct (target, argumentsList, newTarget) {
      throw new Error(`Function ${target.name} cannot be invoked with 'new'`)
      }
      })
      proxy(1, 2) // 3
      new proxy(1, 2) // throw an error

      用Proxy來包裝fetch

      在前端發(fā)送請求,我們現(xiàn)在經(jīng)常用到的應(yīng)該就是fetch了,一個原生提供的API。
      我們可以用Proxy來包裝它,使其變得更易用。

      let handlers = {
      get (target, property) {
      if (!target.init) {
      // 初始化對象
      ['GET', 'POST'].forEach(method => {
      target[method] = (url, params = {}) => {
      return fetch(url, {
      headers: {
      'content-type': 'application/json'
      },
      mode: 'cors',
      credentials: 'same-origin',
      method,
      ...params
      }).then(response => response.json())
      }
      })
      }
      return target[property]
      }
      }
      let API = new Proxy({}, handlers)
      await API.GET('XXX')
      await API.POST('XXX', {
      body: JSON.stringify({name: 1})
      })

      對GET、POST進(jìn)行了一層封裝,可以直接通過.GET這種方式來調(diào)用,并設(shè)置一些通用的參數(shù)。

      實(shí)現(xiàn)一個簡易的斷言工具

      寫過測試的各位童鞋,應(yīng)該都會知道斷言這個東西
      console.assert就是一個斷言工具,接受兩個參數(shù),如果第一個為false,則會將第二個參數(shù)作為Error message拋出。
      我們可以使用Proxy來做一個直接賦值就能實(shí)現(xiàn)斷言的工具。

      let assert = new Proxy({}, {
      set (target, message, value) {
      if (!value) console.error(message)
      }
      })
      assert['Isn\'t true'] = false // Error: Isn't true
      assert['Less than 18'] = 18 >= 19 // Error: Less than 18


      統(tǒng)計(jì)函數(shù)調(diào)用次數(shù)
      在做服務(wù)端時,我們可以用Proxy代理一些函數(shù),來統(tǒng)計(jì)一段時間內(nèi)調(diào)用的次數(shù)。
      在后期做性能分析時可能會能夠用上:

      function orginFunction () {}
      let proxyFunction = new Proxy(orginFunction, {
      apply (target, thisArg. argumentsList) {
      log(XXX)
      return target.apply(thisArg, argumentsList)
      }
      })

      全部的traps

      這里列出了handlers所有可以定義的行為 (traps):

      具體的可以查看MDN-Proxy

      里邊同樣有一些例子

      trapsdescription
      get獲取某個key值
      set設(shè)置某個key值
      has使用in操作符判斷某個key是否存在
      apply函數(shù)調(diào)用,僅在代理對象為function時有效
      ownKeys獲取目標(biāo)對象所有的key
      construct函數(shù)通過實(shí)例化調(diào)用,僅在代理對象為function時有效
      isExtensible判斷對象是否可擴(kuò)展,Object.isExtensible的代理
      deleteProperty刪除一個property
      defineProperty定義一個新的property
      getPrototypeOf獲取原型對象
      setPrototypeOf設(shè)置原型對象
      preventExtensions設(shè)置對象為不可擴(kuò)展
      getOwnPropertyDescriptor獲取一個自有屬性 (不會去原型鏈查找) 的屬性描述

      以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。


      本文題目:JavaScript的Proxy可以做哪些有意思的事兒
      文章來源:http://www.ef60e0e.cn/article/gejspj.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        原平市| 屯昌县| 会昌县| 德保县| 龙陵县| 广西| 射阳县| 军事| 阳山县| 泰来县| 邳州市| 开阳县| 曲周县| 喀什市| 澎湖县| 大英县| 高邮市| 庐江县| 乌鲁木齐县| 容城县| 九寨沟县| 西畴县| 临邑县| 康马县| 吴旗县| 金华市| 襄城县| 青阳县| 泰来县| 修武县| 安泽县| 神木县| 玉屏| 松潘县| 莱州市| 普定县| 贵港市| 天台县| 凤凰县| 沈丘县| 翁源县|