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

          4 個實(shí)用示例幫助你掌握 JavaScript 中Proxy功能

          共 7615字,需瀏覽 16分鐘

           ·

          2022-07-26 18:16

          英文 | https://javascript.plainenglish.io/why-proxies-in-javascript-are-fantastic-db100ddc10a0

          翻譯 | 楊小愛


          什么是Proxy?它究竟是做什么的?在解釋這一點(diǎn)之前,讓我們看一個真實(shí)世界的開發(fā)例子。

          我們每個人在日常生活中都有很多事情要做,比如看郵件、收快遞等等。有時我們可能會感到有點(diǎn)焦慮:我們的郵件列表上有很多垃圾郵件,需要花費(fèi)大量時間來篩選它們;收到的貨物可能含有恐怖分子安放的炸彈,威脅到我們的安全。

          那么,這時你可能需要一個忠誠的管家,您希望管家?guī)椭鷪?zhí)行以下操作:在您開始閱讀之前讓其檢查您的收件箱并刪除所有垃圾郵件;當(dāng)您收到包裹時,請它用專業(yè)設(shè)備檢查包裹,確保里面沒有炸彈。

          在上面的例子中,管家就是我們的代理,當(dāng)我們試圖做某事時,管家為我們做了一些額外的事情。

          現(xiàn)在讓我們回到 JavaScript,我們知道 JavaScript 是一種面向?qū)ο蟮木幊陶Z言,沒有對象我們就無法編寫代碼。但是 JavaScript 對象總是裸奔的,你可以用它們做任何事情。很多時候,這會降低我們的代碼安全性。

          所以在 ECMAScript2015 中引入了 Proxy 功能。有了Proxy,我們可以為物件找到忠實(shí)的管家,幫助我們增強(qiáng)物件原有的功能。

          在最基本的層面上,使用 Proxy 的語法看起來像這樣:

          // This is a normal objectlet obj = {a: 1, b:2}// Configure obj with a housekeeper using Proxylet objProxy = new Proxy(obj, handler)

          這只是代碼的示例,因?yàn)槲覀冞€沒有寫handler,所以這段代碼暫時還不能正常運(yùn)行。

          對于一個人來說,我們可能有閱讀郵件、取件等操作,管家可以為我們做這些。對于一個對象,我們可以讀取屬性、設(shè)置屬性等等,這些也可以通過代理對象來增強(qiáng)。

          在處理程序中,我們可以列出我們想要代理的操作。例如,如果我們想在獲取對象屬性的同時在控制臺中打印出一條語句,我們可以這樣寫:

          let obj = {a: 1, b:2}// Use Proxy syntax to find a housekeeper for the objectlet objProxy = new Proxy(obj, {  get: function(item, property, itemProxy){    console.log(`You are getting the value of '${property}' property`)    return item[property]  }})

          在上面的示例中,我們的處理程序是:

          {  get: function(item, property, itemProxy){    console.log(`You are getting the value of '${property}' property`)    return item[propery] }

          當(dāng)我們嘗試讀取對象的屬性時,get 函數(shù)就會執(zhí)行。

          get 函數(shù)可以接受三個參數(shù):

          • item :它是對象本身。

          • proerty :您要讀取的屬性的名稱。

          • itemProxy :它是我們剛剛創(chuàng)建的管家對象。

          你可能已經(jīng)在其他地方閱讀過有關(guān) Proxy 的教程,并且您會注意到我對參數(shù)的命名與它們不同。我這樣做是為了更接近我之前的示例,以幫助你理解。我希望它對你有用。

          那么get函數(shù)的返回值就是讀取這個屬性的結(jié)果。因?yàn)槲覀冞€不想改變?nèi)魏螙|西,所以我們只返回原始對象的屬性值。

          如果需要,我們也可以更改結(jié)果。例如,我們可以這樣做:

          let obj = {a: 1, b:2}let objProxy = new Proxy(obj, {  get: function(item, property, itemProxy){    console.log(`You are getting the value of '${property}' property`)    return item[property] * 2  }})

          以下是讀取它的屬性的結(jié)果:

          我們將跟進(jìn)實(shí)際示例來說明此技巧的實(shí)際用途。

          除了攔截對屬性的讀取,我們還可以攔截對屬性的修改。像這樣:

          let obj = {a: 1, b:2}let objProxy = new Proxy(obj, {  set: function(item, property, value, itemProxy){    console.log(`You are setting '${value}' to '${property}' property`)    item[property] = value  }})

          當(dāng)我們嘗試設(shè)置對象屬性的值時,會觸發(fā) set 函數(shù)。

          因?yàn)槲覀冊谠O(shè)置屬性值時需要傳遞一個額外的值,所以上面的 set 函數(shù)比 get 函數(shù)多了一個參數(shù)。

          除了攔截對屬性的讀取和修改外,Proxy 總共可以攔截對對象的 13 種操作。

          他們是:

          1. get(item, propKey, itemProxy):攔截對象屬性的讀取操作,如obj.a和ojb['b']

          2. set(item, propKey, value, itemProxy):攔截對象屬性的設(shè)置操作,如 obj.a = 1 。

          3. has(item, propKey):攔截objProxy中propKey的操作,返回一個布爾值。

          4. deleteProperty(item, propKey):攔截delete proxy[propKey]的操作,返回一個布爾值。

          5. ownKeys(item):攔截Object.getOwnPropertyNames(proxy),Object.getOwnPropertySymbols(proxy),Object.keys(proxy),for...in等操作,返回一個數(shù)組。該方法返回目標(biāo)對象自身所有屬性的屬性名,而 Object.keys() 的返回結(jié)果只包含目標(biāo)對象自身的可枚舉屬性。

          6. getOwnPropertyDescriptor(item, propKey):攔截Object.getOwnPropertyDescriptor(proxy, propKey)的操作,返回屬性的描述符。

          7. defineProperty(item, propKey, propDesc):攔截這些操作:Object.defineProperty(proxy, propKey, propDesc),Object.defineProperties(proxy, propDescs),返回一個布爾值。

          8. preventExtensions(item):攔截Object.preventExtensions(proxy)的操作,返回一個布爾值。

          9. getPrototypeOf(item):攔截Object.getPrototypeOf(proxy)的操作,返回一個對象。

          10. isExtensible(item):攔截Object.isExtensible(proxy)的操作,返回一個布爾值。

          11. setPrototypeOf(item, proto):攔截Object.setPrototypeOf(proxy, proto)的操作,返回一個布爾值。

          12. 如果目標(biāo)對象是一個函數(shù),還有兩個額外的操作要intercept.s

          13. apply(item, object, args):攔截函數(shù)調(diào)用操作,如proxy(...args),proxy.call(object, ...args),proxy.apply(...)。

          14. constructor(item, args):攔截Proxy實(shí)例調(diào)用的操作作為構(gòu)造函數(shù),如new proxy(...args)。

          有些攔截不常用,我就不細(xì)說了?,F(xiàn)在讓我們進(jìn)入現(xiàn)實(shí)世界的例子,看看 Proxy 可以為我們做什么。

          1、實(shí)現(xiàn)數(shù)組的負(fù)索引

          我們知道其他一些編程語言,例如 Python,支持對數(shù)組的負(fù)索引訪問。

          負(fù)索引以數(shù)組的最后一個位置為起點(diǎn)并向前計(jì)數(shù)。如:

          • arr[-1] 是數(shù)組的最后一個元素。

          • arr[-3] 是數(shù)組中倒數(shù)第三個元素。

          許多人認(rèn)為這是一個非常有用的功能,但不幸的是,JavaScript 目前不支持負(fù)索引語法。

          但是 JavaScript 中強(qiáng)大的 Proxy 給了我們元編程的能力。

          我們可以將數(shù)組包裝為 Proxy 對象。當(dāng)用戶試圖訪問一個負(fù)數(shù)索引時,我們可以通過 Proxy 的 get 方法攔截這個操作。然后根據(jù)之前定義的規(guī)則將負(fù)索引轉(zhuǎn)換為正索引,訪問就完成了。

          讓我們從一個基本操作開始:攔截對數(shù)組屬性的讀取。

          function negativeArray(array) {  return new Proxy(array, {    get: function(item, propKey){      console.log(propKey)      return item[propKey]    }  })}

          上面的函數(shù)可以包裝一個數(shù)組,讓我們看看它是如何使用的。

          如您所見,我們對數(shù)組屬性的讀取確實(shí)被攔截了。

          請注意:JavaScript 中的對象只能有一個字符串或符號類型的鍵,當(dāng)我們寫 arr[1] 時,它實(shí)際上是在訪問 arr['1'] ,鍵是字符串“1”,而不是數(shù)字 1。

          所以現(xiàn)在我們需要做的是:當(dāng)用戶試圖訪問一個屬性是數(shù)組的索引時,發(fā)現(xiàn)它是一個負(fù)索引,然后進(jìn)行相應(yīng)的攔截和處理;如果屬性不是索引,或者索引是正數(shù),我們什么也不做。

          結(jié)合以上需求,我們可以編寫如下模板代碼。

          function negativeArray(array) {  return new Proxy(array, {    get: function(target, propKey){      if(/** the propKey is a negative index */){        // translate the negative index to positive      }      return target[propKey]  })}

          那么我們?nèi)绾巫R別負(fù)指數(shù)呢?很容易出錯,所以我將更詳細(xì)地介紹。

          首先,Proxy的get方法會攔截對數(shù)組所有屬性的訪問,包括對數(shù)組索引的訪問和對數(shù)組其他屬性的訪問。僅當(dāng)屬性名稱可以轉(zhuǎn)換為整數(shù)時,才會執(zhí)行訪問數(shù)組中元素的操作。我們實(shí)際上需要攔截這個操作來訪問數(shù)組中的元素。

          我們可以通過檢查是否可以將其轉(zhuǎn)換為整數(shù)來確定數(shù)組的屬性是否為索引。

          Number(propKey) != NaN && Number.isInteger(Number(propKey))

          所以,完整的代碼可以這樣寫:

          function negativeArray(array) {  return new Proxy(array, {    get: function(target, propKey){      if (Number(propKey) != NaN && Number.isInteger(Number(propKey)) && Number(propKey) < 0) {        propKey = String(target.length + Number(propKey));      }      return target[propKey]    }  })}

          這是一個例子:

          2、數(shù)據(jù)驗(yàn)證

          眾所周知,javascript 是一種弱類型語言。通常,創(chuàng)建對象時,它會裸運(yùn)行。任何人都可以修改它。

          但大多數(shù)時候,對象的屬性值需要滿足某些條件。例如,一個記錄用戶信息的對象,其age字段中應(yīng)該有一個大于0的整數(shù),通常小于150。

          let person1 = {  name: 'Jon',  age: 23}

          但是,默認(rèn)情況下,JavaScript 不提供安全機(jī)制,我們可以隨意更改此值。

          person1.age = 9999person1.age = 'hello world'

          為了讓我們的代碼更安全,我們可以用 Proxy 包裝我們的對象。我們可以截取對象的set操作,驗(yàn)證age字段的新值是否符合規(guī)則。

          let ageValidate = {  set (item, property, value) {    if (property === 'age') {      if (!Number.isInteger(value) || value < 0 || value > 150) {        throw new TypeError('age should be an integer between 0 and 150');      }    }    item[property] = value  }}

          現(xiàn)在,我們嘗試修改這個屬性的值,可以看到我們設(shè)置的保護(hù)機(jī)制在起作用。

          3、關(guān)聯(lián)屬性

          很多時候,一個對象的屬性是相互關(guān)聯(lián)的。例如,對于存儲用戶信息的對象,其郵政編碼和位置是兩個高度相關(guān)的屬性,當(dāng)用戶的郵政編碼確定后,他的位置也隨之確定。

          為了適應(yīng)來自不同國家的讀者,我在這里使用了一個虛擬示例。假設(shè)位置和郵編有如下關(guān)系:

          JavaScript Street  --  232200Python Street -- 234422Golang Street -- 231142

          這是用代碼表達(dá)它們的關(guān)系的結(jié)果。

          const location2postcode = {  'JavaScript Street': 232200,  'Python Street': 234422,  'Golang Street': 231142}const postcode2location = {  '232200': 'JavaScript Street',  '234422': 'Python Street',  '231142': 'Golang Street'}

          然后看一個例子:

          let person = {  name: 'Jon'}person.postcode = 232200

          當(dāng)我們設(shè)置 person.postcode=232200 時,我們希望能夠自動觸發(fā) person.location='JavaScript Street'。

          這是解決方案:

          let postcodeValidate = {  set(item, property, value) {    if(property === 'location') {      item.postcode = location2postcode[value]
          } if(property === 'postcode'){ item.location = postcode2location[value] } }}

          因此,我們將postcode和location在一起。

          4、私有屬性

          我們知道 JavaScript 從來不支持私有屬性。這使得我們在編寫代碼時無法合理地管理訪問權(quán)限。

          為了解決這個問題,JavaScript 社區(qū)的約定是以字符 _ 開頭的字段被視為私有屬性。

          var obj = {  a: 1,  _value: 22}

          上面的 _value 屬性被認(rèn)為是私有的。然而,重要的是要注意,這只是一個約定,在語言層面沒有這樣的規(guī)則。

          現(xiàn)在我們有了Proxy,我們可以模擬私有屬性特性。

          與普通屬性相比,私有屬性具有以下特點(diǎn):

          • 無法讀取此屬性的值

          • 當(dāng)用戶嘗試訪問對象的鍵時,該屬性不明顯

          然后,我們可以查看前面提到的Proxy的13個攔截操作,看到有3個操作需要攔截。

          function setPrivateField(obj, prefix = "_"){  return new Proxy(obj, {    // Intercept the operation of `propKey in objProxy`    has: (obj, prop) => {},    // Intercept the operations such as `Object.keys(proxy)`    ownKeys: obj => {},    //Intercepts the reading operation of object properties    get: (obj, prop, rec) => {})    });}

          然后,我們在模板中添加適當(dāng)?shù)呐袛嗾Z句:如果發(fā)現(xiàn)用戶試圖訪問以_開頭的字段,則拒絕訪問。

          function setPrivateField(obj, prefix = "_"){  return new Proxy(obj, {    has: (obj, prop) => {      if(typeof prop === "string" && prop.startsWith(prefix)){        return false      }      return prop in obj    },    ownKeys: obj => {      return Reflect.ownKeys(obj).filter(        prop => typeof prop !== "string" || !prop.startsWith(prefix)      )    },    get: (obj, prop) => {      if(typeof prop === "string" && prop.startsWith(prefix)){        return undefined      }      return obj[prop]    }  });}

          這是一個例子:

          總結(jié)
          以上就是我與你分享的關(guān)于Proxy的實(shí)用知識,如果你也覺得它對你有用的話,請記得點(diǎn)贊我,關(guān)注我,并將它分享給你身邊做開發(fā)的朋友,也許能夠幫助到他。
          最后,感謝你的閱讀,祝編程愉快!

          學(xué)習(xí)更多技能

          請點(diǎn)擊下方公眾號

          瀏覽 44
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  欧美爱爱免费看 | 国内三级视频 | 暴操美女网站 | 枕瑶钗十三回兴云弄雨又春风 | 无码三级在线观看 |