監(jiān)聽對象
Object.defineProperty
的存儲屬性描述符來對
屬性的操作進(jìn)行監(jiān)聽凳干。
缺點:
-
Object.defineProperty
在設(shè)計之初并沒有考慮監(jiān)聽對象的實現(xiàn)摄狱,只是為了定義對象的屬性 - 對于一些對對象的操作,無法監(jiān)聽铝阐,如新增屬性椭符、刪除屬性跟伏,只能監(jiān)聽獲取和設(shè)置屬性
Proxy
在ES6中,新增了一個Proxy類秧了,這個類從名字就可以看出來跨扮,是用于幫助我們創(chuàng)建一個代理的:
- 此時,如果我們希望監(jiān)聽一個對象的相關(guān)操作,那么我們可以先創(chuàng)建一個代理對象(Proxy對象)衡创;
- 之后對該對象的所有操作帝嗡,都通過代理對象來完成,代理對象可以監(jiān)聽我們想要對原對象進(jìn)行哪些操作璃氢;
將上面的案例用Proxy來實現(xiàn):
new Proxy
對象哟玷,并且傳入需要偵聽的對象以及一個處理對象,可以稱之為handler一也;
const p = new Proxy(target, handler)
之后的操作都是對Proxy的操作巢寡,而不是原有的對象,因為我們需要在handler里面進(jìn)行偵聽椰苟;
const obj = {
name: "zs",
age: 18,
};
const objProxy = new Proxy(obj, {});
有了代理對象讼渊,就可以監(jiān)聽到具體的操作,只需要在handler中添加對應(yīng)的捕捉器(trap)
set和get分別對應(yīng)的是函數(shù)類型尊剔;
pset函數(shù)有四個參數(shù):
- target:目標(biāo)對象(偵聽的對象)爪幻;
- property:將被設(shè)置的屬性key;
- value:新屬性值须误;
- receiver:調(diào)用的代理對象挨稿;
get函數(shù)有三個參數(shù): - target:目標(biāo)對象(偵聽的對象);
- property:被獲取的屬性key京痢;
- receiver:調(diào)用的代理對象奶甘;
使用案列:
const obj = {
name: "zs",
age: 18,
};
const objProxy = new Proxy(obj, {
set: function (target, propety, newValue, receiver) {
target[propety] = newValue;
},
has: function (target, key) {
console.log("has捕捉器", key);
return key in target;
},
get: function (target, key) {
console.log("get捕捉器", key);
return target[key];
},
deleteProperty: function (target, key) {
console.log("delete捕捉器");
delete target[key];
},
});
console.log(objProxy.name); //get捕捉器 name
//zs
所有捕捉器
Proxy共有13個捕捉器
- handler.getPrototypeOf()
Object.getPrototypeOf 方法的捕捉器。 - handler.setPrototypeOf()
Object.setPrototypeOf 方法的捕捉器祭椰。 - handler.isExtensible()
Object.isExtensible 方法的捕捉器臭家。 - handler.preventExtensions()
Object.preventExtensions 方法的捕捉器。 - handler.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor 方法的捕捉器方淤。 - handler.defineProperty()
Object.defineProperty 方法的捕捉器钉赁。 - handler.ownKeys()
Object.getOwnPropertyNames 方法和
Object.getOwnPropertySymbols 方法的捕捉器。 - handler.has()
in 操作符的捕捉器携茂。 - handler.get()
屬性讀取操作的捕捉器你踩。 - handler.set()
屬性設(shè)置操作的捕捉器。 - handler.deleteProperty()
delete 操作符的捕捉器讳苦。 - handler.apply()
函數(shù)調(diào)用操作的捕捉器带膜。 - handler.construct()
new 操作符的捕捉器
construct和apply
捕捉器中的construct和apply,是應(yīng)用于函數(shù)對象的:
onst obj = {
name: "zs",
age: 18,
};
function foo() {
console.log("foo調(diào)用", this, arguments);
return "foo";
}
const fooProxy = new Proxy(foo, {
apply: function (target, thisArg, argArray) {
console.log("函數(shù)的apply監(jiān)聽");
return target.apply(thisArg, argArray);
},
construct(target, argArray, newTarget) {
console.log(target, argArray, newTarget);
return new target(...argArray);
},
});
// 函數(shù)的apply監(jiān)聽
// foo調(diào)用 { name: 'zs', age: 18 } [Arguments] { '0': 1, '1': 2, '2': 3 }
fooProxy.apply(obj,[1,2,3])
//[Function: foo] [ 1, 2 ] [Function: foo]
//foo調(diào)用 foo {} [Arguments] { '0': 1, '1': 2 }
new fooProxy(1,2)
Reflect的使用
Reflect也是ES6新增的一個API鸳谜,它是一個對象膝藕,字面的意思是反射
Reflect作用:提供了很多操作JavaScript對象的方法,有點像Object中操作對象的方法咐扭;
-
Reflect.getPrototypeOf(target)
類似于Object.getPrototypeOf()
-
Reflect.defineProperty(target, propertyKey, attributes)
類似于Object.defineProperty()
芭挽;
我們有Object可以做這些操作滑废,那么為什么還需要有Reflect
這樣的新增對象呢?
- 在早期的ECMA規(guī)范中沒有考慮到這種對 對象本身 的操作如何設(shè)計會更加規(guī)范览绿,所以將這些API放到了Object上面策严;
- Object作為一個構(gòu)造函數(shù),這些操作實際上放到它身上并不合適饿敲;
- 另外還包含一些類似于 in妻导、delete操作符,讓JS看起來是會有一些奇怪的怀各;
- 在ES6中新增了Reflect倔韭,讓我們這些操作都集中到了Reflect對象上;(專人專辦)
Reflect的常見方法
與Proxy是一一對應(yīng)的瓢对,也是13個:
- Reflect.getPrototypeOf(target)
類似于 Object.getPrototypeOf()寿酌。 - Reflect.setPrototypeOf(target, prototype)
設(shè)置對象原型的函數(shù). 返回一個 Boolean, 如果更新成功硕蛹,則返
回true醇疼。 - Reflect.isExtensible(target)
類似于 Object.isExtensible() - Reflect.preventExtensions(target)
類似于 Object.preventExtensions()。返回一個Boolean法焰。 - Reflect.getOwnPropertyDescriptor(target, propertyKey)
類似于 Object.getOwnPropertyDescriptor()秧荆。如果對象中存在
該屬性,則返回對應(yīng)的屬性描述符, 否則返回 undefined. - Reflect.defineProperty(target, propertyKey, attributes)
和 Object.defineProperty() 類似埃仪。如果設(shè)置成功就會返回 true - Reflect.ownKeys(target)
返回一個包含所有自身屬性(不包含繼承屬性)的數(shù)組乙濒。(類似于
Object.keys(), 但不會受enumerable影響). - Reflect.has(target, propertyKey)
判斷一個對象是否存在某個屬性,和 in 運算符 的功能完全相同卵蛉。 - Reflect.get(target, propertyKey[, receiver])
獲取對象身上某個屬性的值颁股,類似于 target[name]。 - Reflect.set(target, propertyKey, value[, receiver])
將值分配給屬性的函數(shù)傻丝。返回一個Boolean甘有,如果更新成功,則返回true桑滩。 - Reflect.deleteProperty(target, propertyKey)
作為函數(shù)的delete操作符梧疲,相當(dāng)于執(zhí)行 delete target[name]。 - Reflect.apply(target, thisArgument, argumentsList)
對一個函數(shù)進(jìn)行調(diào)用操作运准,同時可以傳入一個數(shù)組作為調(diào)用參數(shù)。和Function.prototype.apply() 功能類似缭受。 - Reflect.construct(target, argumentsList[, newTarget])
對構(gòu)造函數(shù)進(jìn)行 new 操作胁澳,相當(dāng)于執(zhí)行 new target(...args)。
基于此米者,可以將Proxy中對原代替中的操作進(jìn)行優(yōu)化
new Proxy(obj, {
set: function (target, propety, newValue, receiver) {
Reflect.set(target,key,newValue)
},
has: function (target, key) {
console.log("has捕捉器", key);
return Reflect.has(target,key)
},
get: function (target, key) {
console.log("get捕捉器", key);
return Reflect.get(target,key)
},
deleteProperty: function (target, key) {
console.log("delete捕捉器");
delete Reflect.deleteProperty(target,key)
},
});
Receiver的作用
如果我們的源對象(obj)有setter韭畸、getter的訪問器屬性宇智,那么可以通過receiver來改變里面的this;
Reflect的construct
用一個類的構(gòu)造器創(chuàng)建對象胰丁,對象卻是另一個類的子類