摘要:使用陷阱驗證屬性用于接收屬性代理的目標的對象要寫入的屬性鍵被寫入的屬性的值操作發生的對象通常是代理屬性必須是數字拋錯用陷阱驗證對象結構屬性不存在拋出錯誤使用陷阱隱藏已有屬性可以用操作符來檢測給定對象中是否包含有某個屬性,如果自有屬性或原型屬
使用set陷阱驗證屬性
let target = { name: "target" } let proxy = new Proxy(target, { /** * * * @param {any} trapTarget 用于接收屬性(代理的目標)的對象 * @param {any} key 要寫入的屬性鍵 * @param {any} value 被寫入的屬性的值 * @param {any} receiver 操作發生的對象(通常是代理) */ set(trapTarget, key, value, receiver) { if (!trapTarget.hasOwnProperty(key)) { if (isNaN(value)) { throw new TypeError("屬性必須是數字") } } 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:// 拋錯用get陷阱驗證對象結構
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)//拋出錯誤使用has陷阱隱藏已有屬性
可以用in操作符來檢測給定對象中是否包含有某個屬性,如果自有屬性或原型屬性匹配這個名稱或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,如果在嚴格模式下還會拋出錯誤
可以通過deleteProperty陷阱來改變這個行為
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 如果沒有使用代理則會返回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) { //必須返回對象或null,只要返回的是值類型必將導致運行時錯誤 return null; }, setPrototypeOf(trapTarget, proto) { // 如果操作失敗則返回false 如果setPrototypeOf返回了任何不是false的值,那么Object.setPrototypeOf便設置成功 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, {})//拋出錯誤
再看一下下面的代碼
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, {})//成功
再來說說Object.getPrototypeOf和Reflect.getPrototypeOf的異同點吧
1如果傳入的參數不是對象,則Reflect.getPrototypeOf方法會拋出錯誤,而Object.getPrototypeOf方法則會在操作執行前先將參數強制轉換為一個對象(對于Object.getPrototypeOf也是同樣)
let result = Object.getPrototypeOf(1) console.log(result === Number.prototype)//true Reflect.getPrototypeOf(1)//拋錯對象可擴展性陷阱
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
比方說你想讓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 //書上說這里會返回true,可是我自己運行的時候就已經拋出錯誤了 console.log(Object.isExtensible(proxy))//true
Object.isExtensible和Reflect.isExtensible方法非常相似,只有當傳入非對象值時,Object.isExtensible返回false而Reflect.isExtensible則拋出錯誤
let result1 = Object.isExtensible(2) console.log(result1)//false let result2 = Reflect.isExtensible(2)
Object.preventExtensions和Reflect.preventExtensions非常類似,無論傳入Object.preventExtensions方法的參數是否為一個對象,它總是返回該參數,而如果Reflect.preventExtensions方法的參數不是一個對象則會拋出錯誤,如果參數是一個對象,操作成功時Reflect.preventExtensions會返回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)///拋出錯誤屬性描述符陷阱
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") //拋錯 Object.defineProperty(proxy, nameSymbol, { value: "proxy" })
如果讓陷阱返回true并且不調用Reflect.defineProperty方法,則可以讓Object.defineProperty方法靜默失效,這既消除了錯誤又不會真正定義屬性
無論將什么參數作為第三個參數傳遞給Object.defineProperty方法都只有屬性enumerable、configurable、value、writable、get和set將出現在傳遞給defineProperty陷阱的描述符對象中
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或是一個對象,如果返回對象,則對象自己的屬性只能是enumerable、configurable、value、writable、get和set,在返回的對象中使用不被允許的屬性則會拋出一個錯誤
let proxy = new Proxy({}, { getOwnPropertyDescriptor(trapTarget, key) { //在返回的對象中使用不被允許的屬性則會拋出一個錯誤 return { name: "proxy" } } }) let descriptor = Object.getOwnPropertyDescriptor(proxy, "name")
Object.defineProperty和Reflect.defineProperty只有返回值不同
Object.defineProperty返回第一個參數
Reflect.defineProperty的返回值與操作有關,成功則返回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如果傳入原始值作為第一個參數,內部會將這個值強制轉換成一個對象,若調用Reflect.getOwnPropertyDescriptor傳入原始值作為第一個參數,則會拋出錯誤
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循環,這些不能使用代理為更改,ownKeys陷阱也會影響for in循環,當確定循環內部使用的鍵時會調用陷阱
函數代理中的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) } }); //一個目標是函數的代理看起來也像是一個函數 console.log(typeof proxy)//function console.log(proxy())//42 let instance=new proxy(); //用new創建一個instance對象,它同時是代理和目標的實例,因為instanceof通過原型鏈來確定此信息,而原型鏈查找不受代理影響,這也就是代理和目標好像有相同原型的原因 console.log(instance instanceof proxy)//true console.log(instance instanceof target)//true
可以在apply陷阱中檢查參數,在construct陷阱中來確認函數不會被new調用
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("所有參數必須是數字。") } }); return Reflect.apply(trapTarget, thisArg, argumentList) }, construct: function (trapTarget, argumentList) { throw new TypeError("該函數不可通過new來調用") } }) console.log(sumProxy(1, 2, 3, 4, 5))//15 console.log(sumProxy(1, 2, "3", 4, 5))//拋出錯誤 let result = new sumProxy()//拋出錯誤
以下例子是確保用new來調用函數并驗證其參數為數字
function Numbers(...values) { this.values = values } let NumberProxy = new Proxy(Numbers, { apply: function (trapTarget, thisArg, argumentList) { throw new TypeError("該函數必須通過new來調用") }, construct: function (trapTarget, argumentList) { argumentList.forEach(arg => { if (typeof arg !== "number") { throw new TypeError("所有參數必須是數字") } }) 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)//報錯
看一個不用new調用構造函數的例子:
function Numbers(...values) { if (typeof new.target === "undefined") { throw new TypeError("該函數必須通過new來調用") } 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]
覆寫抽象基類構造函數
class AbstractNumbers { constructor(...values) { if (new.target === AbstractNumbers) { throw new TypeError("此函數必須被繼承") } 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)//報錯 此函數必須被繼承
手動用代理給new.target賦值來繞過構造函數限制
class AbstractNumbers { constructor(...values) { if (new.target === AbstractNumbers) { throw new TypeError("此函數必須被繼承") } 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]
可調用的類構造函數
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)//報錯解決數組問題
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}將代理用作原型
如果代理是原型,僅當默認操作繼續執行到原型上時才調用代理陷阱,這會限制代理作為原型的能力
在原型上使用get陷阱
let target={} let thing=Object.create(new Proxy(target,{ /** * * * @param {any} trapTarget 原型對象 * @param {any} key * @param {any} receiver 實例對象 */ get(trapTarget,key,receiver){ throw new ReferenceError(`${key} doesn"t exist`) } })) thing.name="thing" console.log(thing.name)//thing let unknown=thing.unknown//拋出錯誤
在原型上使用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")) //觸發set代理陷阱 thing.name="thing" console.log(thing.name) console.log(thing.hasOwnProperty("name")) //不觸發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) } })) //觸發has代理陷阱 console.log("name" in thing)//false thing.name = "thing" //不觸發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代理陷阱中拋出錯誤 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//拋出錯誤
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/112595.html
摘要:是陷阱函數對應的反射方法,同時也是操作的默認行為。對象外形指的是對象已有的屬性與方法的集合,由于該屬性驗證只須在讀取屬性時被觸發,因此只要使用陷阱函數。無論該屬性是對象自身的屬性還是其原型的屬性。 主要知識點:代理和反射的定義、常用的陷阱函數、可被撤銷的代理、將代理對象作為原型使用、將代理作為類的原型showImg(https://segmentfault.com/img/bVbfWr...
摘要:方法與代理處理程序的方法相同。使用給目標函數傳入指定的參數。當然,不用反射也可以讀取的值。的例子我們可以理解成是攔截了方法,然后傳入參數,將返回值賦值給,這樣我們就能在需要讀取這個返回值的時候調用。這種代理模式和的代理有異曲同工之妙。 反射 Reflect 當你見到一個新的API,不明白的時候,就在瀏覽器打印出來看看它的樣子。 showImg(https://segmentfault....
摘要:方法與代理處理程序的方法相同。使用給目標函數傳入指定的參數。當然,不用反射也可以讀取的值。的例子我們可以理解成是攔截了方法,然后傳入參數,將返回值賦值給,這樣我們就能在需要讀取這個返回值的時候調用。這種代理模式和的代理有異曲同工之妙。 反射 Reflect 當你見到一個新的API,不明白的時候,就在瀏覽器打印出來看看它的樣子。 showImg(https://segmentfault....
摘要:使用陷阱驗證屬性用于接收屬性代理的目標的對象要寫入的屬性鍵被寫入的屬性的值操作發生的對象通常是代理屬性必須是數字拋錯用陷阱驗證對象結構屬性不存在拋出錯誤使用陷阱隱藏已有屬性可以用操作符來檢測給定對象中是否包含有某個屬性,如果自有屬性或原型屬 使用set陷阱驗證屬性 let target = { name: target } let proxy = new Proxy(targe...
閱讀 2945·2021-11-22 15:25
閱讀 2240·2021-11-18 10:07
閱讀 1044·2019-08-29 15:29
閱讀 472·2019-08-29 13:25
閱讀 1502·2019-08-29 12:58
閱讀 3200·2019-08-29 12:55
閱讀 2910·2019-08-29 12:28
閱讀 500·2019-08-29 12:16