es6 proxy

# ES6之proxy

## 是什么

Proxy是一個(gè)構(gòu)造器覆醇。通過(guò)new Proxy(原對(duì)象,{代理列表})的方式去創(chuàng)建對(duì)象,創(chuàng)建的這個(gè)對(duì)象我們稱之為代理對(duì)象刁笙。

即:

> 代理對(duì)象 = new Proxy(原對(duì)象,{代理列表})

之所以要額外產(chǎn)生這么一個(gè)代理對(duì)象贸典,好處在于可以保持原對(duì)象不變,在代理對(duì)象中添加新的功能眨层,或者是改造某些功能。而這個(gè)原對(duì)象則可以在適當(dāng)?shù)臅r(shí)機(jī)回滾回去失受。可以與設(shè)計(jì)模式中的代理模式對(duì)比理解咏瑟。

## 使用格式

```

var obj;

var proxyObj = new Proxy(obj, {

? ? 對(duì)obj的操作1: 函數(shù)1拂到,

? ? 對(duì)obj的操作2: 函數(shù)2,

? ? ...

? ?

})

```

## 入門示例

### Proxy的基本示范

```

var obj = {name:'fan',age:34}

console.info(obj.name)

var proxyObj = new Proxy(obj,{

? ? get:function(target,key,receiver){console.info(target,key,receiver); return 'no'}

})

console.info(proxyObj.name)

console.info(proxyObj.abc)

```

解釋如下:

- proxxy對(duì)象是在obj對(duì)象的基礎(chǔ)之上創(chuàng)建的一個(gè)新對(duì)象码泞。

- proxyObj.name是要去獲取proxy對(duì)象的name屬性兄旬。`.操作符會(huì)自動(dòng)去調(diào)用get()方法`。這一點(diǎn)非常重要余寥,`在js中领铐,對(duì)象是屬性的無(wú)序集合。對(duì)象只有屬性宋舷,其他什么都沒(méi)有`. 而我們經(jīng)常說(shuō)的調(diào)用對(duì)象的某個(gè)方法:例如數(shù)組對(duì)象arr的sort方法:arr.sort()绪撵,這里的sort也是arr對(duì)象的屬性(更嚴(yán)謹(jǐn)一點(diǎn),sort是arr.__proto__這個(gè)對(duì)象的屬性)肥缔,與length屬性相比,sort屬性的屬性值是一個(gè)函數(shù)汹来,所以在它的后面加()來(lái)執(zhí)行這個(gè)函數(shù)续膳,而length屬性的值是一個(gè)數(shù)值,所以不需要加()就可以直接使用收班。再次強(qiáng)調(diào)一下:`對(duì)象的.操作坟岔,會(huì)自動(dòng)去調(diào)用get`。當(dāng)然摔桦,我們平時(shí)使用.操作時(shí)社付,是沒(méi)有感知到這一點(diǎn)的。

- 在new Proxy的第二個(gè)參數(shù)中邻耕,明確設(shè)置了get的方法:當(dāng)訪問(wèn)proxyObj的任意屬性時(shí)鸥咖,輸出target,key,receiver的值,并統(tǒng)一返回no兄世。所以proxyObj.name和proxyObj.abc都會(huì)得到no啼辣。

寫到這里你會(huì)覺得原對(duì)象與代理對(duì)象之間有什么關(guān)系呢?為什么叫`代理`呢御滩?

### 理解代理的作用

代理對(duì)象可以理解為明星的經(jīng)紀(jì)人鸥拧。

```

外界 <----> 原對(duì)象党远;

外界 <----> 代理對(duì)象 <------> 原對(duì)象;

```

還以上面的代碼為例富弦,改進(jìn)一下需求:如果有人問(wèn)obj的名字沟娱,就直接告訴對(duì)方; 如果有人問(wèn)obj的年齡腕柜,就返回小5歲的年齡济似。

```

var obj = {name:'fan',age:34}

console.info(obj.age)? ? ? ? ? // 34

var proxyObj = new Proxy(obj,{

? ? get:function(target,key,receiver){

? ? ? ? console.info(target === obj);? ? ? ? ? //true

? ? ? ? console.info(receiver === proxyObj);? ? //true

? ? ? ? if('age' === key){

? ? ? ? ? ? return target[key] - 5;

? ? ? ? }

? ? ? ? else{

? ? ? ? ? ? return target[key]

? ? ? ? }

? ? }

})

console.info(proxyObj.age)? // 34- 5 = 29

```

解釋如下:

- get函數(shù)中的三個(gè)參數(shù):target,key,receiver。 target就是原對(duì)象j,keys是當(dāng)前的屬性名媳握;receiver是代理對(duì)象碱屁。你可以在get方法中做任意的自定義的處理。

## 代理對(duì)象與原對(duì)象的關(guān)系

```

var arr = [2,1]

var proxyArr = new Proxy(arr,{} )

proxyArr.push(3);

console.info(arr) // [2,1,3]

console.info(arr === proxyArr) // false

arr.sort();

console.info(proxyArr[0]) // 1

```

以上代碼中蛾找,這個(gè)代理對(duì)象并沒(méi)有做任何的特殊操作娩脾。理解為明星的經(jīng)理人消極怠工:原封不動(dòng)地轉(zhuǎn)告外界的信息給明星本身。所以在proxyArr上做到操作會(huì)直接影響到arr上打毛。

同理柿赊,在arr上的操作,也會(huì)影響proxyArr幻枉。

但是要注意:proxyArr與arr是兩個(gè)不同的對(duì)象:arr !== proxyArr碰声。

你可能會(huì)想一想:為什么proxyArr能夠直接使用push這個(gè)方法呢?原因是:

```

proxyArr.__proto__ === arr.__proto__ === Array.prototype

```

前一個(gè)等式成立的原因是由new Proxy的基因決定的:原對(duì)象被代理了嘛熬甫。

## 代理列表

在new Proxy的第二個(gè)參數(shù)中胰挑,可以設(shè)置的代理屬性如下:

```

var proxyObj = new Proxy(obj, {

? ? get: function(tagert,key,receiver){},

? ? set: function(tagert,key,receiver){},

? ? has: function(tagert,key){},

? ? deleteProperty: function(tagert,key){},

? ? ownKeys: function(tagert){},

? ? getOwnPropertyDescriptor: function(tagert,key){},

? ? defineProperty: function(tagert,key,desc){},

? ? preventExtensions: function(tagert){},

? ? getPrototypeOf: function(tagert){},

? ? isExtensible: function(tagert){},

? ? setPrototypeof: function(tagert,proto){},

? ? apply: function(tagert,obj,args){},

? ? construct: function(tagert,args){},

? ?

})

```

## get() 代理的應(yīng)用

### 訪問(wèn)不存在的屬性名時(shí)給出更加優(yōu)雅的提示

在我們的系統(tǒng)中,通常會(huì)把所有的用到的標(biāo)志字符串寫在成常量的格式.一般的做法是單獨(dú)寫在一個(gè)文件中椿肩,然后在其他需要用到的地方引入這個(gè)文件瞻颂。用es6的模塊化的寫法:

const.js

```

/*const.js 系統(tǒng)中所有的常量設(shè)置*/

const con = {

? COMPANYNAME:"jd",

?

}

export defalut con;

```

在其他文件中使用,如下:

file.js

```

import CONST from "./const"

console.info(CONST.COMPANYNAME)

console.info(CONST.COMPANYNAME1) // 不小心把常量名寫錯(cuò),并不會(huì)有什么特殊的提示效果郑象。

```

改進(jìn)如下:

```

const con = {

? COMPANYNAME:"jd",

?

}

let proxyConst = new Proxy(con, {

? ? get: function (target, key, receiver) {

? ? ? ? if(key in target)

? ? ? ? ? ? return target[key];

? ? ? ? else{

? ? ? ? ? ? throw new Error("error:常量名"+key+"不存在贡这!")

? ? ? ? }

? ? }

});

export defalut proxyConst;

```

### 允許數(shù)組下標(biāo)是負(fù)值

在js中,數(shù)組的有效下表是從0開始的厂榛。

```

var arr = [1,2,3];

console.info(arr[0])? // 1

console.info(arr[-1]) // undefined

console.info(arr[100]) // undefined

```

值得注意的是盖矫,`下標(biāo)越界或者是負(fù)值的情況下,得到的結(jié)果是undefined击奶,而不是報(bào)錯(cuò)`辈双。

如果我們希望數(shù)組可以取負(fù)值下表,且規(guī)則如下:

- -n表示倒數(shù)第n個(gè)元素柜砾。例如:-1表示倒數(shù)第一個(gè)元素辐马。

可以使用Proxy解決如下:

```

var arr = [1,2,3];

var proxyArr = new Proxy(arr,{

? ? get: (target,prop)=>{

? ? ? ? let index = Number(prop);

? ? ? ? if(index < 0){

? ? ? ? ? ? prop = target.length + index;

? ? ? ? }

? ? ? ? return target[prop];

? ? ? ?

? ? }

})

console.info(arr[-1]);? ? ? // undefined

console.info(proxyArr[-1]); // 3

```

注意:

- Number()可以把傳入的值轉(zhuǎn)成數(shù)值型。非數(shù)值 --> NaN;

- 如果是proxyArr.push(3),由于此時(shí)的prop是'push',所以不會(huì)進(jìn)入if分支喜爷。

- 如果是proxyArr[-1],此時(shí)的prop是'-1',所以會(huì)進(jìn)入到if分支:把prop從-1改成 2 ,從而實(shí)現(xiàn)了被代理的效果冗疮。

- 此時(shí),完全可以把proxyArr當(dāng)作一個(gè)數(shù)組來(lái)使用檩帐,sort,push等方法均可以調(diào)用术幔。Array.isArray(proxyArr) === true

當(dāng)然,你也可以進(jìn)一步封裝成工廠函數(shù)湃密。

```

function myArr(...args){

? ? var arr = new Array(...args);

? ? var proxyArr = new Proxy(arr,{

? ? ? ? get: (target,key)=>{

? ? ? ? ? ? let index = Number(key);

? ? ? ? ? ? if(index < 0){

? ? ? ? ? ? ? ? key = target.length + index;

? ? ? ? ? ? }

? ? ? ? ? ? return target[key];

? ? ? ? }

? ? })

? ? return proxyArr;

}

var obj = myArr([1,2,3]);

console.info(obj[0],obj[-1])

```

### 鏈?zhǔn)竭\(yùn)算

```

var double = n => n*2;

var pow2 = n => n*n;

var half = n => n/ 2;

var add1 = n => n+1;

function pipe (num){

? ? let funs = []

? ? let obj = new Proxy({},{

? ? ? ? get:function(target,prop){

? ? ? ? ? ? if(prop === 'end'){

? ? ? ? ? ? ? ? return funs.reduce((val,currentfn)=>currentfn(val),num);

? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? funs.push(window[prop])

? ? ? ? ? ? }

? ? ? ? ? ? return obj;

? ? ? ? }

? ? })

? ? return obj;

};

console.info( pipe(4).double.pow2.end);

console.info( pipe(4).pow.double.pow2.add1.end);

```

這種寫法在很多的工具庫(kù)中都有诅挑,例如[math.js](http://mathjs.org/docs/core/chaining.html)的chain.

說(shuō)明:

- [reduce](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市泛源,隨后出現(xiàn)的幾起案子拔妥,更是在濱河造成了極大的恐慌,老刑警劉巖达箍,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件没龙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缎玫,警方通過(guò)查閱死者的電腦和手機(jī)硬纤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赃磨,“玉大人筝家,你說(shuō)我怎么就攤上這事×诨裕” “怎么了溪王?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)值骇。 經(jīng)常有香客問(wèn)我莹菱,道長(zhǎng),這世上最難降的妖魔是什么雷客? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任芒珠,我火速辦了婚禮桥狡,結(jié)果婚禮上搅裙,老公的妹妹穿的比我還像新娘。我一直安慰自己裹芝,他們只是感情好部逮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嫂易,像睡著了一般兄朋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怜械,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天颅和,我揣著相機(jī)與錄音傅事,去河邊找鬼。 笑死峡扩,一個(gè)胖子當(dāng)著我的面吹牛蹭越,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播教届,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼响鹃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了案训?” 一聲冷哼從身側(cè)響起买置,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎强霎,沒(méi)想到半個(gè)月后忿项,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脆栋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年倦卖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椿争。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怕膛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秦踪,到底是詐尸還是另有隱情褐捻,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布椅邓,位于F島的核電站柠逞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏景馁。R本人自食惡果不足惜板壮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望合住。 院中可真熱鬧绰精,春花似錦、人聲如沸透葛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)僚害。三九已至硫椰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背靶草。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蹄胰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奕翔。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓烤送,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親糠悯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子帮坚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容