一、Proxy
1、概述
Proxy取其英文意思即“代理”。
所謂代理,是你要取得某樣?xùn)|西或?qū)ζ溥M(jìn)行某些操作的中間媒介,而不是直接作用在這個對象上。這就類似我們網(wǎng)購東西,需要在網(wǎng)店平臺上購買,而不是直接向廠家購買。
Proxy 對象就是這樣的媒介,要操作這個對象的話,需要經(jīng)過這個媒介的同意。
使用方式: let p = new Proxy(target, habdler);
target:用 Proxy 包裝的目標(biāo)對象(可以是數(shù)組對象,函數(shù),或者另一個代理);
handler:一個對象,攔截過濾代理操作的函數(shù)。
let obj = {
name: "猿跑跑",
age:28
}
let p = new Proxy(obj, {
get: function (target, key) {
if (key in target) {
return target[key]
} else {
console.log("對象沒有此屬性");
}
},
set: function (target, key, value) {
if (key == "age" & value < 1) {
console.log("參數(shù)有誤");
} else {
target[key] = value;
}
}
});
p.age = -1;
p.age = 22;
console.log(p.age);
console.log(p.name);
// 參數(shù)有誤
// 22
// 猿跑跑
2、實(shí)例方法
除了上面代碼中set 和 get 兩個實(shí)例方法外,Proxy 對象實(shí)例方法如下表顯示:
方法 | 描述 |
---|---|
handler.apply() | 攔截 Proxy 實(shí)例作為函數(shù)調(diào)用的操作 |
handler.construct() | 攔截 Proxy 實(shí)例作為函數(shù)調(diào)用的操作 |
handler.defineProperty() | 攔截 Object.defineProperty() 的操作 |
handler.deleteProperty() | 攔截 Proxy 實(shí)例刪除屬性操作 |
handler.get() | 攔截 讀取屬性的操作 |
handler.set() | 攔截 屬性賦值的操作 |
handler.getOwnPropertyDescriptor() | 攔截 Object.getOwnPropertyDescriptor() 的操作 |
handler.getPrototypeOf() | 攔截 獲取原型對象的操作 |
handler.has() | 攔截 屬性檢索操作 |
handler.isExtensible() | 攔截 Object.isExtensible()操作 |
handler.ownKeys() | 攔截 Object.getOwnPropertyDescriptor() 的操作 |
handler.preventExtension() | 攔截 Object().preventExtension() 操作 |
handler.setPrototypeOf() | 攔截Object.setPrototypeOf()操作 |
Proxy.revocable() | 創(chuàng)建一個可取消的 Proxy 實(shí)例 |
二、Reflect
1、概述
與Proxy相同,也是ES6新增。
它新增了一些方法,這些方法可以使一些操作更加規(guī)范化,更加便利。對象有如下特點(diǎn):
(1).只要Proxy對象具有的代理方法,Reflect對象全部具有,以靜態(tài)方法的形式存在。這些方法能夠執(zhí)行默認(rèn)行為,無論P(yáng)roxy怎么修改默認(rèn)行為,總是可以通過Reflect對應(yīng)的方法獲取默認(rèn)行為。
(2).新增的方法與現(xiàn)有一些方法功能重復(fù),新增的方法會取代現(xiàn)有的方法,比如Reflect.getPrototypeOf(),Object對象具有同樣的方法,功能也相同,但是將getPrototypeOf()方法移植到Reflect更加合理。還有一些新增方法用來取代現(xiàn)有的命令式操作,比如判斷屬性是否存在的in命令,用Reflect.has()方法替代。
2、實(shí)例方法
方法 | 描述 |
---|---|
handler.apply() | 通過指定的參數(shù)列表發(fā)起對目標(biāo)函數(shù)的調(diào)用 |
handler.construct() | 此方法行為有點(diǎn)像 new 操作符構(gòu)造函數(shù),相當(dāng)于運(yùn)行 new target(...args) |
handler.defineProperty() | 方法功能類似于Object.defineProperty()方法 |
handler.deleteProperty() | 功能類似于delete運(yùn)算符 |
handler.get() | 從對象獲取指定屬性值 |
handler.set() | 設(shè)置指定對象的屬性,比如為對象添加新屬性或者修改原有屬性的值 |
handler.getOwnPropertyDescriptor() | 功能類似于Object.getOwnPropertyDescriptor() |
handler.getPrototypeOf() | 獲取對象的原型對象 |
handler.has() | 獲取對象的原型對象 |
handler.isExtensible() | 判斷一個對象是否是可擴(kuò)展的 |
handler.ownKeys() | 返回一個數(shù)組,此數(shù)組中包含有參數(shù)對象自有屬性名稱 |
handler.preventExtension() | 將對象設(shè)置為不可擴(kuò)展 |
handler.setPrototypeOf() | 設(shè)置指定對象的原型對象 |
三、應(yīng)用實(shí)例
1、操作節(jié)點(diǎn)(切換兩個不同的元素的屬性或類名)
let view = new Proxy({
selected: null
},
{
set: function(obj, prop, newval) {
let oldval = obj[prop];
if (prop === 'selected') {
if (oldval) {
oldval.setAttribute('aria-selected', 'false');
}
if (newval) {
newval.setAttribute('aria-selected', 'true');
}
}
// The default behavior to store the value
obj[prop] = newval;
}
});
let i1 = view.selected = document.getElementById('item-1');
console.log(i1.getAttribute('aria-selected')); // 'true'
let i2 = view.selected = document.getElementById('item-2');
console.log(i1.getAttribute('aria-selected')); // 'false'
console.log(i2.getAttribute('aria-selected')); // 'true'
2、對象多重繼承
var obj1 = {
name: "obj-1",
foo() {
console.log( "obj1.foo:", this.name );
}
},
obj2 = {
name: "obj-2",
foo() {
console.log( "obj2.foo:", this.name );
},
bar() {
console.log( "obj2.bar:", this.name );
}
},
handlers = {
get(target,key,context) {
if (Reflect.has( target, key )) {
return Reflect.get(target, key, context);
}
else {
for (var P of target[Symbol.for( "[[Prototype]]" )]) {
if (Reflect.has( P, key )) {
return Reflect.get(P, key, context);
}
}
}
}
},
obj3 = new Proxy({
name: "obj-3",
baz() {
this.foo();
this.bar();
}
},handlers);
obj3[Symbol.for("[[Prototype]]")] = [obj1, obj2];
obj3.baz();
//obj1.foo:obj-3
//obj2.bar:obj-3
如果我們要實(shí)現(xiàn)對象間的單繼承,比如obj3繼承在obj1,可以使用Object.setPrototypeOf方法,但是沒法實(shí)現(xiàn)多繼承。所以上面的代碼中用了一個自定義的屬性Symbol.for("[[Prototype]]")來表示要繼承的多個父對象。
然后用Proxy來攔截所有obj3中的get請求,先檢查obj3中是否有相應(yīng)的屬性或者方法,使用的就是Reflect.has方法,如果有,就直接轉(zhuǎn)發(fā);如果沒有,就遍歷父對象列表,在父對象中逐個檢查是否有相應(yīng)的屬性或者方法,有就調(diào)用。如果都沒有,那get就相當(dāng)于返回undefined了。邏輯還是非常好理解的,代碼也比較清楚,應(yīng)該不需要太多的解釋。
章節(jié)目錄
1、ES6中啥是塊級作用域?運(yùn)用在哪些地方?
2、ES6中使用解構(gòu)賦值能帶給我們什么?
3、ES6字符串?dāng)U展增加了哪些?
4、ES6對正則做了哪些擴(kuò)展?
5、ES6數(shù)值多了哪些擴(kuò)展?
6、ES6函數(shù)擴(kuò)展(箭頭函數(shù))
7、ES6 數(shù)組給我們帶來哪些操作便利?
8、ES6 對象擴(kuò)展
9、Symbol 數(shù)據(jù)類型在 ES6 中起什么作用?
10、Map 和 Set 兩數(shù)據(jù)結(jié)構(gòu)在ES6的作用
11、ES6 中的Proxy 和 Reflect 到底是什么鬼?
12、從 Promise 開始踏入異步操作之旅
13、ES6 迭代器(Iterator)和 for...of循環(huán)使用方法
14、ES6 異步進(jìn)階第二步:Generator 函數(shù)
15、JavaScript 異步操作進(jìn)階第三步:async 函數(shù)
16、ES6 構(gòu)造函數(shù)語法糖:class 類