Proxy
在教程中對(duì)于Proxy的定義是這樣的,Proxy用于修改某些操作的默認(rèn)行為,即對(duì)編程語(yǔ)言層面進(jìn)行修改,屬于“元編程”,Proxy意思為“代理”,即在訪問(wèn)對(duì)象之前建立一道“攔截”,任何訪問(wèn)該對(duì)象的操作之前都會(huì)通過(guò)這道“攔截”,即執(zhí)行Proxy里面定義的方法。
(1)ES6原生規(guī)定的Proxy的基本用法為
let pro = new Proxy(target,handler);
其中 new Proxy相當(dāng)于創(chuàng)建了一個(gè)Proxy實(shí)例,target為所要攔截的目標(biāo)對(duì)象,handler也是一個(gè)對(duì)象,里面定義的是對(duì)攔截對(duì)象所要進(jìn)行的攔截方法。
Proxy實(shí)例1
let target = {
name:"小明",
age: 15
}
let handler = {
get:(target,name,)=>{
return "success"
}
}
let pro = new Proxy(target,handler);
console.log(pro.name);
//此時(shí)打印出來(lái)的結(jié)果為 success;
解釋:創(chuàng)建的target對(duì)象為所要攔截的對(duì)象,handler對(duì)象為攔截對(duì)象后執(zhí)行的操作,這里get方法為讀取操作,即用戶想要讀取pro中的屬性時(shí)執(zhí)行的攔截操作。最后創(chuàng)建一個(gè)Proxy實(shí)例,因?yàn)槲以O(shè)定的讀取攔截操作為返回一個(gè)“success”字符串,所以當(dāng)我想讀取pro中的屬性時(shí),結(jié)果打印出來(lái)的總是“success”字符串。
Proxy實(shí)例2
let target = {
name:"小明",
age:15
};
let handler = {};
let pro = new Proxy(target,handler);
console.log(pro.name);
//結(jié)果為 小明
pro.name = "小紅";
console.log(pro.name);
//結(jié)果為 小紅
解釋:攔截操作對(duì)象handler為空,未對(duì)攔截對(duì)象設(shè)定攔截方法,該情況下pro直接指向原對(duì)象target,訪問(wèn)pro等同于訪問(wèn)target,所以結(jié)果為target中的結(jié)果。
Proxy實(shí)例3
Proxy也可以作為其他對(duì)象的原型對(duì)象使用
let target = {
name:"小明",
age:15
}
let handler = {
get:(target,name){
return "success";
}
}
let pro = new Proxy(target,handler);
let obj = Object.create(pro);
console.log(obj.name);
//打印結(jié)果為 success
解釋:上述實(shí)例將pro作為obj的原型對(duì)象使用,雖然obj本身沒(méi)有name這個(gè)屬性,但是根據(jù)原型鏈,會(huì)在pro上讀取到name屬性,之后會(huì)執(zhí)行相對(duì)應(yīng)的攔截操作。
(2)Proxy常用的攔截方法
1、get(target,name,property)方法,用于攔截某個(gè)讀取屬性的操作,第一個(gè)參數(shù)為目標(biāo)對(duì)象,第二個(gè)參數(shù)為屬性名稱,第三個(gè)屬性為操作所針對(duì)的對(duì)象(可選參數(shù))。
let handler = {
get:function(target,name,property){
if(name in target){
cosnole.log("success");
}else{
console.log("error")
}
return Reflect.get(target,name,property);
}
}
let target = {
name:"小明",
age:15
}
let pro = new Proxy(target,handler);
pro.name;
//結(jié)果為 success
pro.grade;
//結(jié)果為error
2、set(target,name,value,property),用于攔截某個(gè)屬性的賦值操作,第一個(gè)參數(shù)為目標(biāo)對(duì)象,第二個(gè)參數(shù)為屬性名,第三個(gè)參數(shù)為屬性值,第四個(gè)參數(shù)為操作行為所針對(duì)的對(duì)象(可選參數(shù))。
let handler = {
set:function(target,name,value,property){
if(typeof value != "number"){
console.log("error");
throw new TypeError('The age is not an integer');
}else{
console.log("success");
return Reflect.set(target,name,value,property);
}
}
}
let target = {
name:"小明",
age:15
}
let pro = new Proxy(target,handler);
pro.age = 35;
//結(jié)果為 success,并且返回修改值
pro.age = "12";
//結(jié)果為error,拋出錯(cuò)誤
3、has(target,key),用來(lái)攔截對(duì)象是否具有某個(gè)屬性值的操作,第一個(gè)參數(shù)為目標(biāo)對(duì)象,第二個(gè)參數(shù)為屬性名
let handler = {
has:function(target,key){
if(key[0] != "_"){
return false
}
return true;
}
}
let target = {
_name:"小明",
age:15
}
let pro = new Proxy(target,handler);
console.log("_name" in pro);
//打印結(jié)果為 true
console.log("age" in pro);
//打印結(jié)果為false
Reflect對(duì)象
概述
1、Reflect對(duì)象與Proxy對(duì)象一樣,都是ES6對(duì)操作對(duì)象設(shè)計(jì)的API
2、對(duì)于我個(gè)人的理解而言,Reflect設(shè)計(jì)的目的是為了優(yōu)化Object的一些操作方法以及合理的返回Object操作返回的結(jié)果,對(duì)于一些命令式的Object行為,Reflect對(duì)象可以將其變?yōu)楹瘮?shù)式的行為
實(shí)例1
//舊寫(xiě)法
try{
Object.defineProperty(target,name,property);
}catch(e){
console.log("error");
}
//Reflect對(duì)象操作
if(Reflect(target,name,property)){
console.log("success");
}else{
console.log("error")
}
實(shí)例2
let obj = {
name:"小明",
age:15
}
//舊寫(xiě)法
console.log(name in obj);
//結(jié)果為 true
//Reflect對(duì)象操作
console.log(Reflect.has(obj,"name"));
//結(jié)果為 true
實(shí)例3
因?yàn)镽eflect對(duì)象的操作和Proxy對(duì)象的操作是一一對(duì)應(yīng)的,所以在Proxy的攔截操作中,可以直接利用Reflect對(duì)象直接獲取Proxy的默認(rèn)值
let target = {
name:"小明",
age:15
}
let handler = {
get:function(target,name,property){
if(name === "name"){
console.log("success");
}else{
console.log("success");
}
return Reflect.get(target,name,property);
}
}
let pro = new Proxy(target,handler);
console.log(pro.name)
//結(jié)果為
// success
//小明
Reflect的靜態(tài)方法
Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
Reflect對(duì)象的靜態(tài)方法和Proxy對(duì)象的靜態(tài)方法一一對(duì)應(yīng),所以在功能上也相同,不一一列舉了
如果要查閱對(duì)應(yīng)方法的用法實(shí)例,參照阮一峰老師的ES6語(yǔ)法http://es6.ruanyifeng.com/#docs/reflect
實(shí)例:使用Proxy實(shí)現(xiàn)觀察者模式
觀察者模式(Observer mode)指的是函數(shù)自動(dòng)觀察數(shù)據(jù)對(duì)象,一旦數(shù)據(jù)有變化,函數(shù)就會(huì)自動(dòng)執(zhí)行
//初始化觀察者隊(duì)列
const arr = new Set();
//將監(jiān)聽(tīng)函數(shù)加入隊(duì)列
const obe = fun => {
arr.add(fun);
}
//初始化Proxy對(duì)象,設(shè)置攔截操作
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver){
//內(nèi)部調(diào)用對(duì)應(yīng)的 Reflect 方法
const result = Reflect.set(target, key, value, receiver);
//額外執(zhí)行觀察者隊(duì)列
arr.forEach( item => item() );
return Reflect.set(target, key, value, receiver);
}
const target = {
name:"小明",
age:15
}
const per = observable(target);
function print(){
console.log( per.name);
}
obe(print);
per.name = "小紅"
//結(jié)果 小紅,15