ES6 Proxy用法详解

Proxy

前不久换工作的时候面试某大厂被问到关于Proxy的问题,脑子里有点印象但是又说不出具体使用方法,主要还是自己平时积累不够,所以赶紧来恶补一下。

Metaprogramming

  • 正式开始之前,先一起来理解下什么是Metaprogramming(元编程),因为Proxy就是一种基于Metaprogramming概念的对象 ,维基百科上这样解释:

Metaprogramming is a programming technique in which computer programs have the ability to treat other programs as their data.

  • 翻译过来就是

元编程就是可以使用其他程序来作为输入数据的一种编程技术

Proxy

Proxy wraps objects and intercepts their behavior through traps

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

简单说来就是Proxy可以用来改变对象的默认操作,比如自行定义set和get等,常用的有以下这些:

  • apply
  • construct
  • defineProperty
  • deleteProperty
  • get
  • getOwnPropertyDescriptor
  • getPrototypeOf
  • has
  • isExtensible
  • ownKeys
  • preventExtensions
  • set
  • setPrototypeOf

Proxy中需要理解的三个属性:

  • target: an Object which the proxy virtualizes.(目标对象)
  • handler: a Placeholder Object which contains traps.(包含重写方法的对象)
  • trap: the Methods that provide property access of the target object.(重写的方法,比如get和set)

例子

新建一个Proxy对象

let p = new Proxy(target, handler);

新建一个employee对象然后输出其中的一些属性

const employee = {
    firstName: 'Tapas',
    lastName: 'Adhikary'
};

console.group('employee');
console.log(employee.firstName);
console.log(employee.lastName);
console.log(employee.org);
console.log(employee.fullName);
console.groupEnd()

上面的输出如下所示:

employee
Tapas
Adhikary
undefined
undefined
输出结果

接下来使用Proxy来更改一些默认行为:

  • 第一步 新建一个使用trap的handler
    以下是一个覆盖get方法的handler,当target中取不到相应的fieldName时,你可以自定义输出:
let handler = {
    get: function(target, fieldName) {        

        if(fieldName === 'fullName' ) {
            return `${target.firstName} ${target.lastName}`;
        }

        return fieldName in target ?
            target[fieldName] :
                `No such property as, '${fieldName}'!`

    }
};
  • 第二步 新建一个Proxy对象,并把之前定义的employee作为target传入
let p = new Proxy(employee, handler);
  • 第三步 输出p的属性
console.group('proxy');
console.log(p.firstName);
console.log(p.lastName);
console.log(p.org);
console.log(p.fullName);
console.groupEnd()

然后就可以看到输出结果和之前不一样了,期望结果与实际结果如下所示:

proxy
  Tapas
  Adhikary
  No such property as, 'org'!
  Tapas Adhikary
输出结果

现在,清楚Proxy的作用了吧,其实就是用来更改对象默认方法的。

下面是改变set方法来实现一个验证的例子:
新建一个handler,重新命名为validator:

const validator = {
    set: function(obj, prop, value) {
        if (prop === 'age') {
            if(!Number.isInteger(value)) {
                throw new TypeError('Age is always an Integer, Please Correct it!');
            }
            if(value < 0) {
                throw new TypeError('This is insane, a negative age?');
            }
        }
    }
};

然后再新建一个Proxy:

let pr = new Proxy(employee, validator);

然后可以尝试设置一个不合法的属性,比如:

pr.age = "test";

你将会得到如下报错:

Uncaught TypeError: Age is always an Integer, Please Correct it!
    at Object.set (<anonymous>:5:23)
    at <anonymous>:1:7
set @ VM2381:5
(anonymous) @ VM2434:1

或者:

pr.age = -1;

结果:

Uncaught TypeError: This is insane, a negative age?
    at Object.set (<anonymous>:8:23)
    at <anonymous>:1:7
set @ VM2381:8
(anonymous) @ VM2531:1

由上可见,Proxy的功能还是很强大也非常实用,常用应用场景有:

  • 保护ID字段不受删除(重写deleteProperty)
  • 取值和写值(数据绑定)(重写get,set)
  • 改变 in 操作的默认行为等

this指向

  • 需要注意的是,proxy会改变target中的this指向,一旦proxy代理了target,target内部的this则指向了proxy,而不是target。
const target = {
  get: function () {
    console.log(this === proxy);
  }
};
const handler = {};
const proxy = new Proxy(target, handler);

target.get() // false
proxy.get()  // true

而有些原生对象的内部属性,只有通过正确的this才能拿到,所以proxy也无法代理这些原生对象的属性。

const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler);

proxy.getDate();
// TypeError: this is not a Date object.

对于以上的代码,getDate方法只能在Date对象的实例上面拿到,如果this不是Date就会报错。可以通过this绑定原始对象来解决这个问题。

const target = new Date('2019-07-11');
const handler = {
  get(target, prop) {
    if (prop === 'getDate') {
      return target.getDate.bind(target);
    }
    return Reflect.get(target, prop);
  }
};
const proxy = new Proxy(target, handler);

proxy.getDate() // 11

这里又引入了另一个ES6的新增的对象Reflect,Reflect对象就是用来获取对象中默认方法的,这个下篇文章再具体讨论。

总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • defineProperty() 学习书籍《ECMAScript 6 入门 》 Proxy Proxy 用于修改某...
    Bui_vlee阅读 648评论 0 1
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,081评论 1 32
  • let and constDestructuring 解构赋值Spread and Reset Spre...
    小宝薯阅读 1,497评论 0 13
  • Proxy 概述 Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(met...
    pauljun阅读 3,249评论 0 1
  • 泪止不住的流 思念波涛汹涌 爱岂又在朝朝暮暮 一年又一年 坚定不移的心 多了沧桑 少了盼望 日复一日 年复一年 多...
    阿月浑子i阅读 178评论 2 4