一、介紹
定義: 用于定義基本操作的自定義行為
本質(zhì): 修改的是程序默認(rèn)形為,就形同于在編程語(yǔ)言層面上做修改,屬于元編程(meta programming)
元編程(Metaprogramming,又譯超編程,是指某類計(jì)算機(jī)程序的編寫,這類計(jì)算機(jī)程序編寫或者操縱其它程序(或者自身)作為它們的數(shù)據(jù),或者在運(yùn)行時(shí)完成部分本應(yīng)在編譯時(shí)完成的工作
一段代碼來(lái)理解
#!/bin/bash
# metaprogram
echo '#!/bin/bash' >program
for ((I=1; I<=1024; I++)) do
echo "echo $I" >>program
done
chmod +x program
這段程序每執(zhí)行一次能幫我們生成一個(gè)名為program的文件,文件內(nèi)容為1024行echo,如果我們手動(dòng)來(lái)寫1024行代碼,效率顯然低效
元編程優(yōu)點(diǎn):與手工編寫全部代碼相比,程序員可以獲得更高的工作效率,或者給與程序更大的靈活度去處理新的情形而無(wú)需重新編譯
Proxy 亦是如此,用于創(chuàng)建一個(gè)對(duì)象的代理,從而實(shí)現(xiàn)基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)
二、用法
Proxy為 構(gòu)造函數(shù),用來(lái)生成 Proxy實(shí)例
var proxy = new Proxy(target, handler)
參數(shù)
- target表示所要攔截的目標(biāo)對(duì)象(任何類型的對(duì)象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理))
- handler通常以函數(shù)作為屬性的對(duì)象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為
handler解析
關(guān)于handler攔截屬性,有如下:
- get(target,propKey,receiver):攔截對(duì)象屬性的讀取
- set(target,propKey,value,receiver):攔截對(duì)象屬性的設(shè)置
- has(target,propKey):攔截propKey in proxy的操作,返回一個(gè)布爾值
- deleteProperty(target,propKey):攔截delete proxy[propKey]的操作,返回一個(gè)布爾值
- ownKeys(target):攔截Object.keys(proxy)、for…in等循環(huán),返回一個(gè)數(shù)組
- getOwnPropertyDescriptor(target, propKey):攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述對(duì)象
- defineProperty(target, propKey, propDesc):攔截Object.defineProperty(proxy, propKey, propDesc),返回一個(gè)布爾值
- preventExtensions(target):攔截Object.preventExtensions(proxy),返回一個(gè)布爾值
- getPrototypeOf(target):攔截Object.getPrototypeOf(proxy),返回一個(gè)對(duì)象
- isExtensible(target):攔截Object.isExtensible(proxy),返回一個(gè)布爾值
- setPrototypeOf(target, proto):攔截Object.setPrototypeOf(proxy, proto),返回一個(gè)布爾值
- apply(target, object, args):攔截 Proxy 實(shí)例作為函數(shù)調(diào)用的操作
- construct(target, args):攔截 Proxy 實(shí)例作為構(gòu)造函數(shù)調(diào)用的操作
Reflect
若需要在Proxy內(nèi)部調(diào)用對(duì)象的默認(rèn)行為,建議使用Reflect,其是ES6中操作對(duì)象而提供的新 API
基本特點(diǎn):
- 只要Proxy對(duì)象具有的代理方法,Reflect對(duì)象全部具有,以靜態(tài)方法的形式存在
- 修改某些Object方法的返回結(jié)果,讓其變得更合理(定義不存在屬性行為的時(shí)候不報(bào)錯(cuò)而是返回false)
- 讓Object操作都變成函數(shù)行為
下面我們介紹proxy幾種用法:
get()
get接受三個(gè)參數(shù),依次為目標(biāo)對(duì)象、屬性名和 proxy 實(shí)例本身,最后一個(gè)參數(shù)可選
var person = {
name: "張三"
};
var proxy = new Proxy(person, {
get: function(target, propKey) {
return Reflect.get(target,propKey)
}
});
proxy.name // "張三"
get能夠?qū)?shù)組增刪改查進(jìn)行攔截,下面是試下你數(shù)組讀取負(fù)數(shù)的索引
function createArray(...elements) {
let handler = {
get(target, propKey, receiver) {
let index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);
}
return Reflect.get(target, propKey, receiver);
}
};
let target = [];
target.push(...elements);
return new Proxy(target, handler);
}
let arr = createArray('a', 'b', 'c');
arr[-1] // c
注意:如果一個(gè)屬性不可配置(configurable)且不可寫(writable),則 Proxy 不能修改該屬性,否則會(huì)報(bào)錯(cuò)
const target = Object.defineProperties({}, {
foo: {
value: 123,
writable: false,
configurable: false
},
});
const handler = {
get(target, propKey) {
return 'abc';
}
};
const proxy = new Proxy(target, handler);
proxy.foo
// TypeError: Invariant check failed
set()
set方法用來(lái)攔截某個(gè)屬性的賦值操作,可以接受四個(gè)參數(shù),依次為目標(biāo)對(duì)象、屬性名、屬性值和 Proxy 實(shí)例本身
假定Person對(duì)象有一個(gè)age屬性,該屬性應(yīng)該是一個(gè)不大于 200 的整數(shù),那么可以使用Proxy保證age的屬性值符合要求
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 對(duì)于滿足條件的 age 屬性以及其他屬性,直接保存
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 報(bào)錯(cuò)
person.age = 300 // 報(bào)錯(cuò)
如果目標(biāo)對(duì)象自身的某個(gè)屬性,不可寫且不可配置,那么set方法將不起作用
const obj = {};
Object.defineProperty(obj, 'foo', {
value: 'bar',
writable: false,
});
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = 'baz';
}
};
const proxy = new Proxy(obj, handler);
proxy.foo = 'baz';
proxy.foo // "bar"
注意,嚴(yán)格模式下,set代理如果沒(méi)有返回true,就會(huì)報(bào)錯(cuò)
'use strict';
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
// 無(wú)論有沒(méi)有下面這一行,都會(huì)報(bào)錯(cuò)
return false;
}
};
const proxy = new Proxy({}, handler);
proxy.foo = 'bar';
// TypeError: 'set' on proxy: trap returned falsish for property 'foo'
deleteProperty()
deleteProperty方法用于攔截delete操作,如果這個(gè)方法拋出錯(cuò)誤或者返回false,當(dāng)前屬性就無(wú)法被delete命令刪除
var handler = {
deleteProperty (target, key) {
invariant(key, 'delete');
Reflect.deleteProperty(target,key)
return true;
}
};
function invariant (key, action) {
if (key[0] === '_') {
throw new Error(`無(wú)法刪除私有屬性`);
}
}
var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: 無(wú)法刪除私有屬性
注意,目標(biāo)對(duì)象自身的不可配置(configurable)的屬性,不能被deleteProperty方法刪除,否則報(bào)錯(cuò)
取消代理
Proxy.revocable(target, handler);
三、使用場(chǎng)景
Proxy其功能非常類似于設(shè)計(jì)模式中的代理模式,常用功能如下:
- 攔截和監(jiān)視外部對(duì)對(duì)象的訪問(wèn)
- 降低函數(shù)或類的復(fù)雜度
- 在復(fù)雜操作前對(duì)操作進(jìn)行校驗(yàn)或?qū)λ栀Y源進(jìn)行管理
使用 Proxy 保障數(shù)據(jù)類型的準(zhǔn)確性
let numericDataStore = { count: 0, amount: 1234, total: 14 };
numericDataStore = new Proxy(numericDataStore, {
set(target, key, value, proxy) {
if (typeof value !== 'number') {
throw Error("屬性只能是number類型");
}
return Reflect.set(target, key, value, proxy);
}
});
numericDataStore.count = "foo"
// Error: 屬性只能是number類型
numericDataStore.count = 333
// 賦值成功
聲明了一個(gè)私有的 apiKey,便于 api 這個(gè)對(duì)象內(nèi)部的方法調(diào)用,但不希望從外部也能夠訪問(wèn) api._apiKey
let api = {
_apiKey: '123abc456def',
getUsers: function(){ },
getUser: function(userId){ },
setUser: function(userId, config){ }
};
const RESTRICTED = ['_apiKey'];
api = new Proxy(api, {
get(target, key, proxy) {
if(RESTRICTED.indexOf(key) > -1) {
throw Error(`${key} 不可訪問(wèn).`);
} return Reflect.get(target, key, proxy);
},
set(target, key, value, proxy) {
if(RESTRICTED.indexOf(key) > -1) {
throw Error(`${key} 不可修改`);
} return Reflect.get(target, key, value, proxy);
}
});
console.log(api._apiKey)
api._apiKey = '987654321'
// 上述都拋出錯(cuò)誤
還能通過(guò)使用Proxy實(shí)現(xiàn)觀察者模式
觀察者模式(Observer mode)指的是函數(shù)自動(dòng)觀察數(shù)據(jù)對(duì)象,一旦對(duì)象有變化,函數(shù)就會(huì)自動(dòng)執(zhí)行
observable函數(shù)返回一個(gè)原始對(duì)象的 Proxy 代理,攔截賦值操作,觸發(fā)充當(dāng)觀察者的各個(gè)函數(shù)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-608251.html
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;
}
觀察者函數(shù)都放進(jìn)Set集合,當(dāng)修改obj的值,在會(huì)set函數(shù)中攔截,自動(dòng)執(zhí)行Set所有的觀察者文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-608251.html
到了這里,關(guān)于ES6基礎(chǔ)知識(shí)八:你是怎么理解ES6中Proxy的?使用場(chǎng)景?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!