es6 proxy

# ES6之proxy

## 是什么

Proxy是一个构造器。通过new Proxy(原对象,{代理列表})的方式去创建对象,创建的这个对象我们称之为代理对象。

即:

> 代理对象 = new Proxy(原对象,{代理列表})

之所以要额外产生这么一个代理对象,好处在于可以保持原对象不变,在代理对象中添加新的功能,或者是改造某些功能。而这个原对象则可以在适当的时机回滚回去。可以与设计模式中的代理模式对比理解。

## 使用格式

```

var obj;

var proxyObj = new Proxy(obj, {

    对obj的操作1: 函数1,

    对obj的操作2: 函数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对象是在obj对象的基础之上创建的一个新对象。

- proxyObj.name是要去获取proxy对象的name属性。`.操作符会自动去调用get()方法`。这一点非常重要,`在js中,对象是属性的无序集合。对象只有属性,其他什么都没有`. 而我们经常说的调用对象的某个方法:例如数组对象arr的sort方法:arr.sort(),这里的sort也是arr对象的属性(更严谨一点,sort是arr.__proto__这个对象的属性),与length属性相比,sort属性的属性值是一个函数,所以在它的后面加()来执行这个函数,而length属性的值是一个数值,所以不需要加()就可以直接使用。再次强调一下:`对象的.操作,会自动去调用get`。当然,我们平时使用.操作时,是没有感知到这一点的。

- 在new Proxy的第二个参数中,明确设置了get的方法:当访问proxyObj的任意属性时,输出target,key,receiver的值,并统一返回no。所以proxyObj.name和proxyObj.abc都会得到no。

写到这里你会觉得原对象与代理对象之间有什么关系呢?为什么叫`代理`呢?

### 理解代理的作用

代理对象可以理解为明星的经纪人。

```

外界 <----> 原对象;

外界 <----> 代理对象 <------> 原对象;

```

还以上面的代码为例,改进一下需求:如果有人问obj的名字,就直接告诉对方; 如果有人问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函数中的三个参数:target,key,receiver。 target就是原对象j,keys是当前的属性名;receiver是代理对象。你可以在get方法中做任意的自定义的处理。

## 代理对象与原对象的关系

```

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

```

以上代码中,这个代理对象并没有做任何的特殊操作。理解为明星的经理人消极怠工:原封不动地转告外界的信息给明星本身。所以在proxyArr上做到操作会直接影响到arr上。

同理,在arr上的操作,也会影响proxyArr。

但是要注意:proxyArr与arr是两个不同的对象:arr !== proxyArr。

你可能会想一想:为什么proxyArr能够直接使用push这个方法呢?原因是:

```

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

```

前一个等式成立的原因是由new Proxy的基因决定的:原对象被代理了嘛。

## 代理列表

在new Proxy的第二个参数中,可以设置的代理属性如下:

```

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() 代理的应用

### 访问不存在的属性名时给出更加优雅的提示

在我们的系统中,通常会把所有的用到的标志字符串写在成常量的格式.一般的做法是单独写在一个文件中,然后在其他需要用到的地方引入这个文件。用es6的模块化的写法:

const.js

```

/*const.js 系统中所有的常量设置*/

const con = {

  COMPANYNAME:"jd",

 

}

export defalut con;

```

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

file.js

```

import CONST from "./const"

console.info(CONST.COMPANYNAME)

console.info(CONST.COMPANYNAME1) // 不小心把常量名写错,并不会有什么特殊的提示效果。

```

改进如下:

```

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;

```

### 允许数组下标是负值

在js中,数组的有效下表是从0开始的。

```

var arr = [1,2,3];

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

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

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

```

值得注意的是,`下标越界或者是负值的情况下,得到的结果是undefined,而不是报错`。

如果我们希望数组可以取负值下表,且规则如下:

- -n表示倒数第n个元素。例如:-1表示倒数第一个元素。

可以使用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()可以把传入的值转成数值型。非数值 --> NaN;

- 如果是proxyArr.push(3),由于此时的prop是'push',所以不会进入if分支。

- 如果是proxyArr[-1],此时的prop是'-1',所以会进入到if分支:把prop从-1改成 2 ,从而实现了被代理的效果。

- 此时,完全可以把proxyArr当作一个数组来使用,sort,push等方法均可以调用。Array.isArray(proxyArr) === true

当然,你也可以进一步封装成工厂函数。

```

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])

```

### 链式运算

```

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);

```

这种写法在很多的工具库中都有,例如[math.js](http://mathjs.org/docs/core/chaining.html)的chain.

说明:

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容