核心內容:**與反射
mdn相關文件
**
什麼是**?
js中如何實現**?
**有哪些應用場景?
**即替某人做某事的行為可以看做是**,被委託的物件起到乙個**物件的作用,而委託方則是目標物件。遙記得設計模式中也有一種模式即**模式,當時書上給的例子是,有人想給總經理打**但這個**只能通過秘書轉接,這個時候秘書就相當於總經理的**類。
通過proxy(target, handler)
建立**物件
target: 需要使用proxy包裝的目標物件(可以是任何型別的物件,包括原生陣列,函式,甚至另乙個**)
handler:
乙個物件,其屬性是當執行乙個操作時定義**的行為的函式
(可以理解為某種觸發器)
簡單使用及目標物件和**物件在呼叫上的關係
const target =
;const handler =
;const proxy =
newproxy
(target, handler)
;// id 屬性會訪問同乙個值
console.
log(target.id)
;// target
console.
log(proxy.id)
;// target
// 給目標屬性賦值會反映在兩個物件上
// 因為兩個物件訪問的是同乙個值
target.id =
'foo'
;console.
log(target.id)
;// foo
console.
log(proxy.id)
;// foo
// 給**屬性賦值會反映在兩個物件上
// 因為這個賦值會轉移到目標物件
proxy.id =
'bar'
;console.
log(target.id)
;// bar
console.
log(proxy.id)
;// bar
// hasownproperty()方法在兩個地方
// 都會應用到目標物件
console.
log(target.
hasownproperty
('id'))
;// true
console.
log(proxy.
hasownproperty
('id'))
;// true
// proxy.prototype 是 undefined
// 因此不能使用 instanceof 操作符
console.
log(target instanceof
proxy);
// typeerror: function has non-object prototype
'undefined'
ininstanceof
check
console.
log(proxy instanceof
proxy);
// typeerror: function has non-object prototype
'undefined'
ininstanceof
check
// 嚴格相等可以用來區分**和目標
console.
log(target === proxy)
;// false
有些時候可能需要中斷**物件和目標物件的聯絡,可以通過proxy.revocable(target, handler);
撤銷關聯,此撤銷存在不可逆
**本質上是一種物件增強,通過**這個中間層可以在對目標物件呼叫的中間新增攔截。在定義**時傳入的第二個引數handler即為包含捕獲器(trap)的乙個物件,每次在**物件上呼叫這些基本操作時,**可以在這些操作傳播到目標物件之前先呼叫捕獲器函式,從而攔截並修改相應的行為。
例如,可以定義乙個 get()捕獲器,在 ecmascript 操作以某種形式呼叫 get()時觸發。下面的例子定義了乙個 get()捕獲器:
const target =
;const handler =
// 所有捕獲器都可以訪問相應的引數,基於這些引數可以重建**獲方法的原始行為
// get(traptarget, property, receiver), 捕獲器可以傳遞相關引數};
const proxy =
newproxy
(target, handler)
;const proxy =
newproxy
(target, handler)
;console.
log(target.foo)
;// bar
console.
log(proxy.foo)
;// handler override
console.
log(target[
'foo'])
;// bar
console.
log(proxy[
'foo'])
;// handler override
console.
log(object.
create
(target)
['foo'])
;// bar
console.
log(object.
create
(proxy)
['foo'])
;// handler override
通過捕獲器trap的使用可以使得我們對目標物件的特定方法進行二次加工處理,比如,插入日誌層,呼叫目標跳轉,賦值時屬性驗證,限制目標類的某些屬性被獲取,**這個中間層的新增使得使用方和呼叫方能解耦的同時保持內部方法的單一職責。
捕獲器使用中的編碼簡化
所有捕獲器都可以基於自己的引數重建原始操作,但並非所有捕獲器行為都像 get()那麼簡單。因此,通過手動寫碼如法炮製的想法是不現實的。實際上,開發者並不需要手動重建原始行為,而是可以通過呼叫全域性 reflect 物件上(封裝了原始行為)的同名方法來輕鬆重建。
const target =
;const handler =
// 更簡潔寫法
// get: reflect.get };
const proxy =
newproxy
(target, handler)
;console.
log(proxy.foo)
;// bar
console.
log(target.foo)
;// bar
當目標的某個屬性是不變式時,即writable: false,在get捕獲器中返回與屬性值不同的值時,會丟擲 typeerror
**使用上的不足
比如**中的this問題
const wm =
newweakmap()
;class
user
setid
(userid)
getid()
}由於這個實現依賴 user 例項的物件標識,在這個例項被**的情況下就會出問題:
const user =
newuser
(123);
console.
log(user.id)
;// 123
const userinstanceproxy =
newproxy
(user,);
console.
log(userinstanceproxy.id)
;// undefined
//這是因為user例項一開始使用目標物件作為weakmap的鍵,**物件卻嘗試從自身取得這個例項
**與內建引用型別(比如 array)的例項通常可以很好地協同,但有些 ecmascript 內建型別可能會依賴**無法控制的機制,結果導致在**上呼叫某些方法會出錯。
乙個典型的例子就是 date 型別。根據 ecmascript 規範,date 型別方法的執行依賴 this 值上的內部槽位[[numberdate]]。**物件上不存在這個內部槽位,而且這個內部槽位的值也不能通過普通的 get()和 set()操作訪問到,於是**攔截後本應**給目標物件的方法會丟擲 typeerror:
const target =
newdate()
;const proxy =
newproxy
(target,);
console.
log(proxy instanceof
date);
// true
proxy.
getdate()
;// typeerror: 'this' is not a date object
第九章(筆記)
轉移指令是可以修改ip,或同時修改cs和ip的指令 offset 是用於提取標號偏移位址的操作符 jmp在第2章裡說到時用於修改ip或同時修改cs和ip的轉移指令,這章裡單獨的jmp指令是乙個無條件的轉移指令 jmp short 標號 是實現段內短轉移 jmp near ptr 標號 是實現段內近轉...
程式設計珠璣第九章
1 記憶體訪問 連續記憶體訪問與跨頁面訪問記憶體的區別 注意在訪問記憶體的時候,要注意記憶體的連續性,如果訪問的記憶體不是連續的,那麼程式的執行速度也會受到極大的影響 例如訪問乙個二維陣列時,先訪問行,再訪問列,能夠減少頁面排程次數,同時cache命中率也相對高些。2 遞迴呼叫巨集時,需要小心,巨集...
第九章 迴圈結構高階
二重迴圈就是乙個迴圈體內包含了另乙個完整的迴圈結構。while與while迴圈巢狀 while 迴圈條件1 while 迴圈條件2 while 迴圈條件1 for與for迴圈巢狀 for 迴圈條件1 迴圈操作1 for 迴圈條件2 迴圈操作2 while與for迴圈巢狀 while 迴圈條件1 迴圈...