亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

 更新時(shí)間:2019年06月15日 11:35:17   作者:賈順名  
這篇文章主要介紹了JavaScript的Proxy可以做哪些有意思的事兒,Proxy是ES6中提供的新的API,可以用來(lái)定義對(duì)象各種基本操作的自定義行為 (在文檔中被稱(chēng)為traps,我覺(jué)得可以理解為一個(gè)針對(duì)對(duì)象各種行為的鉤子),,需要的朋友可以參考下

Proxy是什么

首先,我們要清楚,Proxy是什么意思,這個(gè)單詞翻譯過(guò)來(lái),就是 代理。

可以理解為,有一個(gè)很火的明星,開(kāi)通了一個(gè)微博賬號(hào),這個(gè)賬號(hào)非?;钴S,回復(fù)粉絲、到處點(diǎn)贊之類(lèi)的,但可能并不是真的由本人在維護(hù)的。

而是在背后有一個(gè)其他人 or 團(tuán)隊(duì)來(lái)運(yùn)營(yíng),我們就可以稱(chē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)中來(lái),就可以理解為對(duì)對(duì)象或者函數(shù)的代理操作。

JavaScript中的Proxy

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

Proxy的語(yǔ)法

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

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

2.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(各種行為的代理)

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

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

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

早年JavaScript有著在定義對(duì)象時(shí)針對(duì)某個(gè)屬性進(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

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

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

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

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

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

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

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

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

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

拿Proxy來(lái)做些什么

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

在下邊列出了幾個(gè)簡(jiǎn)單的適用場(chǎng)景。

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

在一些層級(jí)比較深的對(duì)象屬性獲取中,如何處理undefined一直是一個(gè)痛苦的過(guò)程,如果我們用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)行g(shù)et的值來(lái)自一個(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是否存在只能夠通過(guò)in操作符來(lái)判斷,而不能夠直接通過(guò)get來(lái)判斷。

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

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

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的方式來(lái)調(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來(lái)代理一些行為,在函數(shù)調(diào)用時(shí)會(huì)被觸發(fā),因?yàn)槲覀兠鞔_的知道,代理的是一個(gè)Class或構(gòu)造函數(shù),所以我們直接在apply中使用new關(guān)鍵字來(lái)調(diào)用被代理的函數(shù)。

以及如果我們想要對(duì)函數(shù)進(jìn)行限制,禁止使用new關(guān)鍵字來(lái)調(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來(lái)包裝fetch

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

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)行了一層封裝,可以直接通過(guò).GET這種方式來(lái)調(diào)用,并設(shè)置一些通用的參數(shù)。

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

寫(xiě)過(guò)測(cè)試的各位童鞋,應(yīng)該都會(huì)知道斷言這個(gè)東西
console.assert就是一個(gè)斷言工具,接受兩個(gè)參數(shù),如果第一個(gè)為false,則會(huì)將第二個(gè)參數(shù)作為Error message拋出。
我們可以使用Proxy來(lái)做一個(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ù),來(lái)統(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ù)通過(guò)實(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ì)去原型鏈查找) 的屬性描述

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論