摘要:使用陷阱驗(yàn)證屬性用于接收屬性代理的目標(biāo)的對(duì)象要寫入的屬性鍵被寫入的屬性的值操作發(fā)生的對(duì)象通常是代理屬性必須是數(shù)字拋錯(cuò)用陷阱驗(yàn)證對(duì)象結(jié)構(gòu)屬性不存在拋出錯(cuò)誤使用陷阱隱藏已有屬性可以用操作符來(lái)檢測(cè)給定對(duì)象中是否包含有某個(gè)屬性,如果自有屬性或原型屬
使用set陷阱驗(yàn)證屬性
let target = { name: "target" } let proxy = new Proxy(target, { /** * * * @param {any} trapTarget 用于接收屬性(代理的目標(biāo))的對(duì)象 * @param {any} key 要寫入的屬性鍵 * @param {any} value 被寫入的屬性的值 * @param {any} receiver 操作發(fā)生的對(duì)象(通常是代理) */ set(trapTarget, key, value, receiver) { if (!trapTarget.hasOwnProperty(key)) { if (isNaN(value)) { throw new TypeError("屬性必須是數(shù)字") } } return Reflect.set(trapTarget, key, value, receiver) } }) proxy.count = 1; console.log(proxy.count)//1 console.log(target.count)//1 proxy.name = "proxy" console.log(proxy.name)//proxy console.log(target.name)//proxy proxy.anthorName = "test"http:// 拋錯(cuò)用get陷阱驗(yàn)證對(duì)象結(jié)構(gòu)
let proxy = new Proxy({}, { get(trapTarget, key, receiver) { if (!(key in receiver)) { throw new TypeError("屬性" + key + "不存在") } return Reflect.get(trapTarget,key,receiver) } }) proxy.name="proxy" console.log(proxy.name)//proxy console.log(proxy.nme)//拋出錯(cuò)誤使用has陷阱隱藏已有屬性
可以用in操作符來(lái)檢測(cè)給定對(duì)象中是否包含有某個(gè)屬性,如果自有屬性或原型屬性匹配這個(gè)名稱或Symbol就返回true
let target = { name: "target", value: 42 } let proxy = new Proxy(target, { has(trapTarget, key) { if (key === "value") { return false } return Reflect.has(trapTarget, key) } }) console.log("value" in proxy)//false console.log("name" in proxy)//true console.log("toString" in proxy)//true用deleteProperty陷阱防止刪除屬性
不可配置屬性name用delete操作返回的是false,如果在嚴(yán)格模式下還會(huì)拋出錯(cuò)誤
可以通過(guò)deleteProperty陷阱來(lái)改變這個(gè)行為
let target = { name: "target", value: 42 } let proxy = new Proxy(target, { deleteProperty(trapTarget, key) { if (key === "value") { return false } return Reflect.deleteProperty(trapTarget, key) } }) console.log("value" in proxy)//true let result1 = delete proxy.value console.log(result1)//false console.log("value" in proxy)//true //嘗試刪除不可配置屬性name 如果沒(méi)有使用代理則會(huì)返回false并且刪除不成功 console.log("name" in proxy)//true let result2 = delete proxy.name; console.log(result2)//true console.log("name" in proxy)//false原型代理陷阱
let target = {} let proxy = new Proxy(target, { getPrototypeOf(trapTarget) { //必須返回對(duì)象或null,只要返回的是值類型必將導(dǎo)致運(yùn)行時(shí)錯(cuò)誤 return null; }, setPrototypeOf(trapTarget, proto) { // 如果操作失敗則返回false 如果setPrototypeOf返回了任何不是false的值,那么Object.setPrototypeOf便設(shè)置成功 return false } }) let targetProto = Object.getPrototypeOf(target); let proxyProto = Object.getPrototypeOf(proxy) console.log(targetProto === Object.prototype)//true console.log(proxyProto === Object.prototype)//false Object.setPrototypeOf(target, {})//成功 Object.setPrototypeOf(proxy, {})//拋出錯(cuò)誤
再看一下下面的代碼
let target = {} let proxy = new Proxy(target, { getPrototypeOf(trapTarget) { return Reflect.getPrototypeOf(trapTarget); }, setPrototypeOf(trapTarget, proto) { return Reflect.setPrototypeOf(trapTarget,proto) } }) let targetProto = Object.getPrototypeOf(target); let proxyProto = Object.getPrototypeOf(proxy) console.log(targetProto === Object.prototype)//true console.log(proxyProto === Object.prototype)//true Object.setPrototypeOf(target, {})//成功 Object.setPrototypeOf(proxy, {})//成功
再來(lái)說(shuō)說(shuō)Object.getPrototypeOf和Reflect.getPrototypeOf的異同點(diǎn)吧
1如果傳入的參數(shù)不是對(duì)象,則Reflect.getPrototypeOf方法會(huì)拋出錯(cuò)誤,而Object.getPrototypeOf方法則會(huì)在操作執(zhí)行前先將參數(shù)強(qiáng)制轉(zhuǎn)換為一個(gè)對(duì)象(對(duì)于Object.getPrototypeOf也是同樣)
let result = Object.getPrototypeOf(1) console.log(result === Number.prototype)//true Reflect.getPrototypeOf(1)//拋錯(cuò)對(duì)象可擴(kuò)展性陷阱
let target = {} let proxy = new Proxy(target, { isExtensible(trapTarget) { return Reflect.isExtensible(trapTarget) }, preventExtensions(trapTarget) { return Reflect.preventExtensions(trapTarget) } }) console.log(Object.isExtensible(target))//true console.log(Object.isExtensible(proxy))//true Object.preventExtensions(proxy) console.log(Object.isExtensible(target))//false console.log(Object.isExtensible(proxy))//false
比方說(shuō)你想讓Object.preventExtensions失敗,可返回false,看下面的例子
let target = {} let proxy = new Proxy(target, { isExtensible(trapTarget) { return Reflect.isExtensible(trapTarget) }, preventExtensions(trapTarget) { return false } }) console.log(Object.isExtensible(target))//true console.log(Object.isExtensible(proxy))//true Object.preventExtensions(proxy) console.log(Object.isExtensible(target))//true //書上說(shuō)這里會(huì)返回true,可是我自己運(yùn)行的時(shí)候就已經(jīng)拋出錯(cuò)誤了 console.log(Object.isExtensible(proxy))//true
Object.isExtensible和Reflect.isExtensible方法非常相似,只有當(dāng)傳入非對(duì)象值時(shí),Object.isExtensible返回false而Reflect.isExtensible則拋出錯(cuò)誤
let result1 = Object.isExtensible(2) console.log(result1)//false let result2 = Reflect.isExtensible(2)
Object.preventExtensions和Reflect.preventExtensions非常類似,無(wú)論傳入Object.preventExtensions方法的參數(shù)是否為一個(gè)對(duì)象,它總是返回該參數(shù),而如果Reflect.preventExtensions方法的參數(shù)不是一個(gè)對(duì)象則會(huì)拋出錯(cuò)誤,如果參數(shù)是一個(gè)對(duì)象,操作成功時(shí)Reflect.preventExtensions會(huì)返回true否則返回false
let result1 = Object.preventExtensions(2) console.log(result1)//2 let target = {} let result2 = Reflect.preventExtensions(target) console.log(result2)//true let result3 = Reflect.preventExtensions(3)///拋出錯(cuò)誤屬性描述符陷阱
let proxy = new Proxy({}, { defineProperty(trapTarget, key, descriptor) { if (typeof key === "symbol") { return false } return Reflect.defineProperty(trapTarget, key, descriptor) } }) Object.defineProperty(proxy, "name", { value: "proxy" }) console.log(proxy.name)//proxy let nameSymbol = Symbol("name") //拋錯(cuò) Object.defineProperty(proxy, nameSymbol, { value: "proxy" })
如果讓陷阱返回true并且不調(diào)用Reflect.defineProperty方法,則可以讓Object.defineProperty方法靜默失效,這既消除了錯(cuò)誤又不會(huì)真正定義屬性
無(wú)論將什么參數(shù)作為第三個(gè)參數(shù)傳遞給Object.defineProperty方法都只有屬性enumerable、configurable、value、writable、get和set將出現(xiàn)在傳遞給defineProperty陷阱的描述符對(duì)象中
let proxy = new Proxy({}, { defineProperty(trapTarget, key, descriptor) { console.log(descriptor) console.log(descriptor.value) console.log(descriptor.name) return Reflect.defineProperty(trapTarget, key, descriptor) } }) Object.defineProperty(proxy, "name", { value: "proxy", name: "custom" })
getOwnPropertyDescriptor它的返回值必須是null、undefined或是一個(gè)對(duì)象,如果返回對(duì)象,則對(duì)象自己的屬性只能是enumerable、configurable、value、writable、get和set,在返回的對(duì)象中使用不被允許的屬性則會(huì)拋出一個(gè)錯(cuò)誤
let proxy = new Proxy({}, { getOwnPropertyDescriptor(trapTarget, key) { //在返回的對(duì)象中使用不被允許的屬性則會(huì)拋出一個(gè)錯(cuò)誤 return { name: "proxy" } } }) let descriptor = Object.getOwnPropertyDescriptor(proxy, "name")
Object.defineProperty和Reflect.defineProperty只有返回值不同
Object.defineProperty返回第一個(gè)參數(shù)
Reflect.defineProperty的返回值與操作有關(guān),成功則返回true,失敗則返回false
let target = {} let result1 = Object.defineProperty(target, "name", { value: "target" }) console.log(target === result1)//true let result2 = Reflect.defineProperty(target, "name", { value: "refelct" }) console.log(result2)//false
Object.getOwnPropertyDescriptor如果傳入原始值作為第一個(gè)參數(shù),內(nèi)部會(huì)將這個(gè)值強(qiáng)制轉(zhuǎn)換成一個(gè)對(duì)象,若調(diào)用Reflect.getOwnPropertyDescriptor傳入原始值作為第一個(gè)參數(shù),則會(huì)拋出錯(cuò)誤
ownKeys陷阱let proxy = new Proxy({}, { ownKeys(trapTarget) { return Reflect.ownKeys(trapTarget).filter(key => { return typeof key !== "string" || key[0] !== "_" }) } }) let nameSymbol = Symbol("name") proxy.name = "proxy" proxy._name = "private" proxy[nameSymbol] = "symbol" let names = Object.getOwnPropertyNames(proxy), keys = Object.keys(proxy), symbols = Object.getOwnPropertySymbols(proxy) console.log(names)//["name"] console.log(keys)//["name"] console.log(symbols)//[Symbol(name)]
盡管ownKeys代理陷阱可以修改一小部分操作返回的鍵,但不影響更常用的操作,例如for of循環(huán),這些不能使用代理為更改,ownKeys陷阱也會(huì)影響for in循環(huán),當(dāng)確定循環(huán)內(nèi)部使用的鍵時(shí)會(huì)調(diào)用陷阱
函數(shù)代理中的apply和construct陷阱let target = function () { return 42; }, proxy = new Proxy(target, { apply: function (trapTarget, thisArg, argumentList) { return Reflect.apply(trapTarget, thisArg, argumentList) }, construct: function (trapTarget, argumentList) { return Reflect.construct(trapTarget, argumentList) } }); //一個(gè)目標(biāo)是函數(shù)的代理看起來(lái)也像是一個(gè)函數(shù) console.log(typeof proxy)//function console.log(proxy())//42 let instance=new proxy(); //用new創(chuàng)建一個(gè)instance對(duì)象,它同時(shí)是代理和目標(biāo)的實(shí)例,因?yàn)閕nstanceof通過(guò)原型鏈來(lái)確定此信息,而原型鏈查找不受代理影響,這也就是代理和目標(biāo)好像有相同原型的原因 console.log(instance instanceof proxy)//true console.log(instance instanceof target)//true
可以在apply陷阱中檢查參數(shù),在construct陷阱中來(lái)確認(rèn)函數(shù)不會(huì)被new調(diào)用
function sum(...values) { return values.reduce((pre, cur) => pre + cur, 0) } let sumProxy = new Proxy(sum, { apply: function (trapTarget, thisArg, argumentList) { argumentList.forEach(arg => { if (typeof arg !== "number") { throw new TypeError("所有參數(shù)必須是數(shù)字。") } }); return Reflect.apply(trapTarget, thisArg, argumentList) }, construct: function (trapTarget, argumentList) { throw new TypeError("該函數(shù)不可通過(guò)new來(lái)調(diào)用") } }) console.log(sumProxy(1, 2, 3, 4, 5))//15 console.log(sumProxy(1, 2, "3", 4, 5))//拋出錯(cuò)誤 let result = new sumProxy()//拋出錯(cuò)誤
以下例子是確保用new來(lái)調(diào)用函數(shù)并驗(yàn)證其參數(shù)為數(shù)字
function Numbers(...values) { this.values = values } let NumberProxy = new Proxy(Numbers, { apply: function (trapTarget, thisArg, argumentList) { throw new TypeError("該函數(shù)必須通過(guò)new來(lái)調(diào)用") }, construct: function (trapTarget, argumentList) { argumentList.forEach(arg => { if (typeof arg !== "number") { throw new TypeError("所有參數(shù)必須是數(shù)字") } }) return Reflect.construct(trapTarget, argumentList) } }) let instance = new NumberProxy(12, 3, 4, 8) console.log(instance.values)// [12, 3, 4, 8] NumberProxy(1, 2, 3, 4)//報(bào)錯(cuò)
看一個(gè)不用new調(diào)用構(gòu)造函數(shù)的例子:
function Numbers(...values) { if (typeof new.target === "undefined") { throw new TypeError("該函數(shù)必須通過(guò)new來(lái)調(diào)用") } this.values = values } let NumberProxy = new Proxy(Numbers, { apply: function (trapTarget, thisArg, argumentList) { return Reflect.construct(trapTarget, argumentList) } }) let instance = NumberProxy(1, 2, 3, 4) console.log(instance.values)//[1,2,3,4]
覆寫抽象基類構(gòu)造函數(shù)
class AbstractNumbers { constructor(...values) { if (new.target === AbstractNumbers) { throw new TypeError("此函數(shù)必須被繼承") } this.values = values } } class Numbers extends AbstractNumbers{} let instance = new Numbers(1,2,3,4,5) console.log(instance.values)//[1, 2, 3, 4, 5] new AbstractNumbers(1,2,3,4,5)//報(bào)錯(cuò) 此函數(shù)必須被繼承
手動(dòng)用代理給new.target賦值來(lái)繞過(guò)構(gòu)造函數(shù)限制
class AbstractNumbers { constructor(...values) { if (new.target === AbstractNumbers) { throw new TypeError("此函數(shù)必須被繼承") } this.values = values } } let AbstractNumbersProxy = new Proxy(AbstractNumbers, { construct: function (trapTarget, argumentList) { return Reflect.construct(trapTarget, argumentList, function () { }) } }) let instance = new AbstractNumbersProxy(1, 2, 3, 4) console.log(instance.values)//[1, 2, 3, 4]
可調(diào)用的類構(gòu)造函數(shù)
class Person { constructor(name) { this.name = name; } } let PersonProxy = new Proxy(Person, { apply: function (trapTarget, thisArg, argumentList) { return new trapTarget(...argumentList) } }) let me = PersonProxy("angela") console.log(me.name)//angela console.log(me instanceof Person)//true console.log(me instanceof PersonProxy)//true
可撤銷代理
let target = { name: "target" } let { proxy, revoke } = Proxy.revocable(target, {}) console.log(proxy.name)//traget revoke() console.log(proxy.name)//報(bào)錯(cuò)解決數(shù)組問(wèn)題
function toUint32(value) { return Math.floor(Math.abs(Number(value))) % Math.pow(2, 32) } function isArrayIndex(key) { let numericKey = toUint32(key) return String(numericKey) == key && numericKey < (Math.pow(2, 32) - 1) } function createMyArray(length = 0) { return new Proxy({ length }, { set(trapTarget, key, value) { let currentLength = Reflect.get(trapTarget, "length") if (isArrayIndex(key)) { let numericKey = Number(key) if (numericKey >= currentLength) { Reflect.set(trapTarget, "length", numericKey + 1) } } else if (key === "length") { if (value < currentLength) { for (let index = currentLength - 1; index >= value; index--) { Reflect.deleteProperty(trapTarget, index) } } } Reflect.set(trapTarget, key, value) } }) } let colors = createMyArray(3) colors[0] = "red" colors[1] = "green" colors[2] = "blue" console.log(colors.length)//3 colors[3] = "black" console.log(colors[3])//black console.log(colors.length)//4 colors.length = 1 console.log(colors)//{0: "red", length: 1}將代理用作原型
如果代理是原型,僅當(dāng)默認(rèn)操作繼續(xù)執(zhí)行到原型上時(shí)才調(diào)用代理陷阱,這會(huì)限制代理作為原型的能力
在原型上使用get陷阱
let target={} let thing=Object.create(new Proxy(target,{ /** * * * @param {any} trapTarget 原型對(duì)象 * @param {any} key * @param {any} receiver 實(shí)例對(duì)象 */ get(trapTarget,key,receiver){ throw new ReferenceError(`${key} doesn"t exist`) } })) thing.name="thing" console.log(thing.name)//thing let unknown=thing.unknown//拋出錯(cuò)誤
在原型上使用set陷阱
let target={} let thing=Object.create(new Proxy(target,{ set(trapTarget,key,value,receiver){ return Reflect.set(trapTarget,key,value,receiver) } })) console.log(thing.hasOwnProperty("name")) //觸發(fā)set代理陷阱 thing.name="thing" console.log(thing.name) console.log(thing.hasOwnProperty("name")) //不觸發(fā)set代理陷阱 thing.name="boo" console.log(thing.name)//boo
在原型上使用has陷阱
let target = {} let thing = Object.create(new Proxy(target, { has(trapTarget, key) { return Reflect.has(trapTarget, key) } })) //觸發(fā)has代理陷阱 console.log("name" in thing)//false thing.name = "thing" //不觸發(fā)has代理陷阱 console.log("name" in thing)//true
將代理用作類的原型
function NoSuchProperty(){} NoSuchProperty.prototype=new Proxy({},{ get(trapTarget,key,receiver){ throw new ReferenceError(`${key} doesn"t exist`) } }) let thing=new NoSuchProperty() //在get代理陷阱中拋出錯(cuò)誤 let result=thing.name
function NoSuchProperty() { } NoSuchProperty.prototype = new Proxy({}, { get(trapTarget, key, receiver) { throw new ReferenceError(`${key} doesn"t exist`) } }) class Square extends NoSuchProperty { constructor(length, width) { super() this.length = length; this.width = width } } let shape = new Square(2, 6) let area1 = shape.length * shape.width console.log(area1)//12 let area2 = shape.length * shape.wdth//拋出錯(cuò)誤
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88494.html
摘要:是陷阱函數(shù)對(duì)應(yīng)的反射方法,同時(shí)也是操作的默認(rèn)行為。對(duì)象外形指的是對(duì)象已有的屬性與方法的集合,由于該屬性驗(yàn)證只須在讀取屬性時(shí)被觸發(fā),因此只要使用陷阱函數(shù)。無(wú)論該屬性是對(duì)象自身的屬性還是其原型的屬性。 主要知識(shí)點(diǎn):代理和反射的定義、常用的陷阱函數(shù)、可被撤銷的代理、將代理對(duì)象作為原型使用、將代理作為類的原型showImg(https://segmentfault.com/img/bVbfWr...
摘要:方法與代理處理程序的方法相同。使用給目標(biāo)函數(shù)傳入指定的參數(shù)。當(dāng)然,不用反射也可以讀取的值。的例子我們可以理解成是攔截了方法,然后傳入?yún)?shù),將返回值賦值給,這樣我們就能在需要讀取這個(gè)返回值的時(shí)候調(diào)用。這種代理模式和的代理有異曲同工之妙。 反射 Reflect 當(dāng)你見(jiàn)到一個(gè)新的API,不明白的時(shí)候,就在瀏覽器打印出來(lái)看看它的樣子。 showImg(https://segmentfault....
摘要:方法與代理處理程序的方法相同。使用給目標(biāo)函數(shù)傳入指定的參數(shù)。當(dāng)然,不用反射也可以讀取的值。的例子我們可以理解成是攔截了方法,然后傳入?yún)?shù),將返回值賦值給,這樣我們就能在需要讀取這個(gè)返回值的時(shí)候調(diào)用。這種代理模式和的代理有異曲同工之妙。 反射 Reflect 當(dāng)你見(jiàn)到一個(gè)新的API,不明白的時(shí)候,就在瀏覽器打印出來(lái)看看它的樣子。 showImg(https://segmentfault....
閱讀 3556·2021-11-22 11:59
閱讀 954·2021-09-27 13:36
閱讀 3616·2021-09-24 09:47
閱讀 2266·2021-09-01 11:39
閱讀 985·2021-08-31 09:37
閱讀 2316·2021-08-05 10:01
閱讀 1677·2019-08-30 15:55
閱讀 703·2019-08-30 15:54