亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

拿Proxy可以做哪些有意思的事兒

tabalt / 2738人閱讀

摘要:拿來做些什么因?yàn)樵谑褂昧撕?,?duì)象的行為基本上都是可控的,所以我們能拿來做一些之前實(shí)現(xiàn)起來比較復(fù)雜的事情。如果沒有使用關(guān)鍵字來調(diào)用的話,對(duì)象會(huì)直接拋出異常,而中的構(gòu)造函數(shù)指向則會(huì)變?yōu)檎{(diào)用函數(shù)時(shí)的作用域。

Proxy是什么

首先,我們要清楚,Proxy是什么意思,這個(gè)單詞翻譯過來,就是 代理
可以理解為,有一個(gè)很火的明星,開通了一個(gè)微博賬號(hào),這個(gè)賬號(hào)非常活躍,回復(fù)粉絲、到處點(diǎn)贊之類的,但可能并不是真的由本人在維護(hù)的。
而是在背后有一個(gè)其他人 or 團(tuán)隊(duì)來運(yùn)營(yíng),我們就可以稱他們?yōu)榇砣?,因?yàn)樗麄儼l(fā)表的微博就代表了明星本人的意思。
P.S. 強(qiáng)行舉例子,因?yàn)楸救瞬蛔沸?,只是猜測(cè)可能會(huì)有這樣的運(yùn)營(yíng)團(tuán)隊(duì)

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

JavaScript中的Proxy

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

Proxy的語(yǔ)法

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

target 要被代理的對(duì)象,可以是一個(gè)object或者function

handlers對(duì)該代理對(duì)象的各種操作行為處理

let target = {}
let handlers = {} // do nothing
let proxy = new Proxy(target, handlers)

proxy.a = 123

console.log(target.a) // 123

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

Traps(各種行為的代理)

就像上邊的示例代碼一樣,如果沒有定義對(duì)應(yīng)的trap,則不會(huì)起任何作用,相當(dāng)于直接操作了target。
當(dāng)我們寫了某個(gè)trap以后,在做對(duì)應(yīng)的動(dòng)作時(shí),就會(huì)觸發(fā)我們的回調(diào)函數(shù),由我們來控制被代理對(duì)象的行為。

最常用的兩個(gè)trap應(yīng)該就是getset了。
早年JavaScript有著在定義對(duì)象時(shí)針對(duì)某個(gè)屬性進(jìn)行設(shè)置gettersetter

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

就像這段代碼描述的一樣,我們?cè)O(shè)置了一個(gè)屬性_age,然后又設(shè)置了一個(gè)get ageset age。
然后我們可以直接調(diào)用obj.age來獲取一個(gè)返回值,也可以對(duì)其進(jìn)行賦值。
這么做有幾個(gè)缺點(diǎn):

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

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

Proxy很好的解決了這兩個(gè)問題:

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兩個(gè)trap來統(tǒng)一管理所有的操作,可以看到,在修改proxy的同時(shí),target的內(nèi)容也被修改,而且我們對(duì)proxy的行為進(jìn)行了一些特殊的處理。
而且我們無需額外的用一個(gè)key來存儲(chǔ)真實(shí)的值,因?yàn)槲覀冊(cè)?b>trap內(nèi)部操作的是target對(duì)象,而不是proxy對(duì)象。

拿Proxy來做些什么

因?yàn)樵谑褂昧?b>Proxy后,對(duì)象的行為基本上都是可控的,所以我們能拿來做一些之前實(shí)現(xiàn)起來比較復(fù)雜的事情。
在下邊列出了幾個(gè)簡(jiǎn)單的適用場(chǎng)景。

解決對(duì)象屬性為undefined的問題

在一些層級(jí)比較深的對(duì)象屬性獲取中,如何處理undefined一直是一個(gè)痛苦的過程,如果我們用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)針對(duì)`target`創(chuàng)建了一個(gè)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)行get的值來自一個(gè)不存在的key,則我們會(huì)在target中創(chuàng)建對(duì)應(yīng)個(gè)這個(gè)key,然后返回一個(gè)針對(duì)這個(gè)key的代理對(duì)象。
這樣就能夠保證我們的取值操作一定不會(huì)拋出can not get xxx from undefined
但是這會(huì)有一個(gè)小缺點(diǎn),就是如果你確實(shí)要判斷這個(gè)key是否存在只能夠通過in操作符來判斷,而不能夠直接通過get來判斷。

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

如果我們提供了一個(gè)Class對(duì)象給其他人,或者說一個(gè)ES5版本的構(gòu)造函數(shù)。
如果沒有使用new關(guān)鍵字來調(diào)用的話,Class對(duì)象會(huì)直接拋出異常,而ES5中的構(gòu)造函數(shù)this指向則會(huì)變?yōu)檎{(diào)用函數(shù)時(shí)的作用域。
我們可以使用apply這個(gè)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)用時(shí)會(huì)被觸發(fā),因?yàn)槲覀兠鞔_的知道,代理的是一個(gè)Class或構(gòu)造函數(shù),所以我們直接在apply中使用new關(guān)鍵字來調(diào)用被代理的函數(shù)。

以及如果我們想要對(duì)函數(shù)進(jìn)行限制,禁止使用new關(guān)鍵字來調(diào)用,可以用另一個(gè)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ā)送請(qǐng)求,我們現(xiàn)在經(jīng)常用到的應(yīng)該就是fetch了,一個(gè)原生提供的API。
我們可以用Proxy來包裝它,使其變得更易用。

let handlers = {
  get (target, property) {
    if (!target.init) {
      // 初始化對(duì)象
      ["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})
})

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

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

寫過測(cè)試的各位童鞋,應(yīng)該都會(huì)知道斷言這個(gè)東西
console.assert就是一個(gè)斷言工具,接受兩個(gè)參數(shù),如果第一個(gè)為false,則會(huì)將第二個(gè)參數(shù)作為Error message拋出。
我們可以使用Proxy來做一個(gè)直接賦值就能實(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ù)端時(shí),我們可以用Proxy代理一些函數(shù),來統(tǒng)計(jì)一段時(shí)間內(nèi)調(diào)用的次數(shù)。
在后期做性能分析時(shí)可能會(huì)能夠用上:

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

    return target.apply(thisArg, argumentsList)
  }
})
全部的traps

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

具體的可以查看MDN-Proxy  
里邊同樣有一些例子
traps description
get 獲取某個(gè)key
set 設(shè)置某個(gè)key
has 使用in操作符判斷某個(gè)key是否存在
apply 函數(shù)調(diào)用,僅在代理對(duì)象為function時(shí)有效
ownKeys 獲取目標(biāo)對(duì)象所有的key
construct 函數(shù)通過實(shí)例化調(diào)用,僅在代理對(duì)象為function時(shí)有效
isExtensible 判斷對(duì)象是否可擴(kuò)展,Object.isExtensible的代理
deleteProperty 刪除一個(gè)property
defineProperty 定義一個(gè)新的property
getPrototypeOf 獲取原型對(duì)象
setPrototypeOf 設(shè)置原型對(duì)象
preventExtensions 設(shè)置對(duì)象為不可擴(kuò)展
getOwnPropertyDescriptor 獲取一個(gè)自有屬性 (不會(huì)去原型鏈查找) 的屬性描述
參考資料

Magic Methods in JavaScript? Meet Proxy!

How to use JavaScript Proxies for Fun and Profit

MDN-Proxy

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/95239.html

相關(guān)文章

  • APICloud CEO 劉鑫客喜馬拉雅:揭秘AI如何通過移動(dòng)技術(shù)落地

    摘要:不僅如此,倒霉的不只是文科生,根據(jù)劍橋大學(xué)的數(shù)據(jù),目前熱門的工程師居然有的幾率被淘汰,程序員也有接近的幾率被淘汰。 現(xiàn)如今人工智能已經(jīng)在很多方面得到了應(yīng)用落地,通過手機(jī)移動(dòng)端的一些功能和應(yīng)用程序,我們已經(jīng)能夠很直觀地感受到它對(duì)我們生活的影響。比如蘋果IphoneX的人臉識(shí)別功能,包括一些主流app有語(yǔ)音識(shí)別和語(yǔ)音對(duì)話的功能。 隨著AI時(shí)代的移動(dòng)技術(shù)革新大會(huì)開幕鄰近,APICloud創(chuàng)始...

    Shisui 評(píng)論0 收藏0
  • WPS思路重寫了一套私有云系統(tǒng)

    摘要:年初,金山啟動(dòng)私有云項(xiàng)目,該項(xiàng)目旨在為向金山提出了私有云網(wǎng)盤存儲(chǔ)需求的政府大型企業(yè)以及中型企業(yè)提供服務(wù),項(xiàng)目組由金山云楊鋼牽頭組建。中文站對(duì)楊鋼進(jìn)行了專訪,了解其私有云服務(wù)的技術(shù)組成和業(yè)務(wù)狀態(tài)。 2013年初,金山啟動(dòng)私有云項(xiàng)目,該項(xiàng)目旨在為向金山提出了私有云網(wǎng)盤/存儲(chǔ)需求的政府、大型企業(yè)以及中型企業(yè)提供服務(wù),項(xiàng)目組由金山云CTO楊鋼牽頭組建。InfoQ中文站對(duì)楊鋼進(jìn)行了專訪,了解其私有云服...

    Achilles 評(píng)論0 收藏0
  • 肖鵬:微博數(shù)據(jù)庫(kù)那些事兒(圖靈訪談)

    摘要:經(jīng)歷了微博數(shù)據(jù)庫(kù)各個(gè)階段的架構(gòu)改造,包括服務(wù)保障及體系建設(shè)微博多機(jī)房部署微博平臺(tái)化改造等項(xiàng)目。第二階段爆發(fā)階段微博上線之后,隨著用戶活躍度的增加,數(shù)據(jù)庫(kù)的壓力也與日俱增。 非商業(yè)轉(zhuǎn)載請(qǐng)注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/article/211461 肖鵬,微博研發(fā)中心技術(shù)經(jīng)理,主要負(fù)責(zé)微博數(shù)據(jù)庫(kù)(MySQL/Reids/HBase...

    wangzy2019 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<