ReflectProxy的理解为什么会有Reflect的出现Proxy的原理
一、Reflect
ES6 提供了一个全新的选择 API-Reflect,Reflect 和 Proxy 是相对的,我们可以用 Reflect 操作对象。
1.1 Reflect 存在的意义1)将 Object 对象一些内部的方法,放到 Reflect 对象上。比如 Object.defineProperty
现阶段这些方法存在于 object 和 Reflect 对象上,未来只存在于 Reflect 对象上。
意义:也就是说,从 Reflect 对象上可以拿到语言内部的方法。 2)操作对象出现报错返回 false
比如:Object.defineProperty(obj,name,desc) 在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj,name,desc)则会返回 false,这样会更合理一些。 // 旧写法 try { Object.defineProperty(target, property, attributes); } catch (err) { //failure } // 新写法 if (Reflect.defineProperty(target, property, attributes)) { //success } else { //failure }3)让操作对象的编程变为函数式编程
说明:老写法有的是命令式编程,比如下面这个例子 // 老写法 "assign" in Object; // true // 新写法 Reflect.has(Object, "assign"); //true4)保持和 Proxy 对象的方法一一对应
说明:Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。 Proxy(target, { set: function (target, name, value, receiver) { var success = Reflect.set(target, name, value, receiver); if (success) { console.log("property" + name + "on" + target + "set do " + value); } return success; }, });
这就让 Proxy 对象可以方便地调用对应的 Reflect 方法,完成默认行为,作为修改行为的基础。也就是说,不管 Proxy 怎么修改默认行为,你总可以在 Reflect 上获取默认行为。
综上所述:Reflect 对象有 4 个意义: 从 Reflect 对象上可以拿到语言内部的方法。 操作对象出现报错时返回 false 让操作对象都变为函数式编程 保持和 proxy 对象的方法一一对应 1.2 Reflect 常用 API
静态方法
Reflect 一共有 13 个静态方法
它可以一部分是原来在 Object 上的方法,将它转移到了 Reflect 上,并做了小改动,让方法更加合理。 Reflect.defineProperty(target,name,desc)与 Object.defineProperty(target,name,desc)类似,当时对象无法定义时,Object.defineProperty 会报错,而 Reflect.defineProperty 不会,它会返回 false,成功时返回 true,如果不是对象还是会报错。 Reflect.getPrototypeOf(target) 与 Object.getPrototypeOf 一样,返回指定的对象的原型。 Reflect.setProtoTypeOf(target,prototype) 和 Object.setPrototypeOf(target,prototype) 一样,它将指定对象的原型设置为另一个对象。 Reflect.getOwnPropertyDescriptor(target,name)和 Object.getOwnPropertyDescriptor(target,name) 一样,如果在对象中存在,则返回给定的属性的属性描述符。 Reflect.isExtensible(target) 与 Object.isExtensible(target) 类似,判断一个对象是否可扩展(是否可以在它上面添加新的属性),它们的不同点是,当参数不是对象时(原始值),Object 将它强制转换为一个对象,Reflect 直接报错。 Reflect.preventExtensions(target) 与 Object.preventExtensions(target) 类似,阻止新属性添加到对象,不同点和上一条一样。 Reflect.apply(target,thisArg,args)与 Function.prototype.apply.call(fn,obj,args) 一样 Reflect.ownKeys(target) 与 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)) 一样,返回一个包含所有自身属性(不包含继承属性)的数组。
另一部分是将原来操作符的功能,变成函数行为 Reflect.has(target,name) 与 in 操作符一样,让判断操作都变成函数行为 Reflect.deleteProperty(target,name) 与 delete 操作符一样,让删除操作变成函数行为,返回布尔值代表成功或者失败 Reflect.construct(target,args[,newTarget]) 与 new 操作符一样,target 构造函数,第二参数是构造函数的参数类数组,第三参数是 new.target 的值。 Reflect.get(target,name[,receiver]) 与 Obj[key] 一样,第三个参数是要取值的 key 部署了 getter 时,访问其函数的 this 绑定为 receiver 对象。 Reflect.set(target,name,value[,receiver]) 设置 target 对象的 key 属性等于 value,第三个参数和 set 一样。返回一个布尔值。 // 老写法 "assign" in Object; // true // 新写法 Reflect.has(Object, "assign"); // true // 老写法 Function.prototype.apply.call(Math.floor, undefined, [1.75]); // 1 // 新写法 Reflect.apply(Math.floor, undefined, [1.75]); // 1 // 旧写法 delete myObj.foo; // 新写法 Reflect.deleteProperty(myObj, "foo"); // new 的写法 const instance = new Greeting("张三"); // Reflect.construct 的写法 const instance = Reflect.construct(Greeting, ["张三"]); // 旧写法 Object.defineProperty(MyDate, "now", { value: () => Date.now(), }); // 新写法 Reflect.defineProperty(MyDate, "now", { value: () => Date.now(), }); Reflect.get(1, "foo"); // 报错 Reflect.get(false, "foo"); // 报错 Reflect.set(1, "foo", {}); // 报错 Reflect.set(false, "foo", {}); // 报错 var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, }; var myReceiverObject = { foo: 4, bar: 4, }; Reflect.get(myObject, "baz", myReceiverObject); // 8
ES6 为 new 命令引入了一个new.target属性,该属性一般用在构造函数之中,返回 new 命令作用于的那个构造函数。如果构造函数不是通过 new 命令或Reflect.construct()调用的,new.target会返回 undefined,因此这个属性可以用来确定构造函数是怎么调用的。function Person(name) { if (new.target === Person) { this.name = name; } else { throw new Error("必须使用 new 命令生成实例"); } }二、Proxy
Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等),等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程语言进行编程。
Proxy 就像在目标对象之间的一个代理,任何对目标的操作都要经过代理。代理就可以对外界的操作进行过滤和改写。
Proxy 是构造函数,它有两个参数 target 和 handler。
target是用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler 是一个对象,其属性是当执行一个操作时定义代理的行为函数。 var obj = new Proxy( {}, { get: function (target, key, receiver) { console.log(`getting ${key}`); return Reflect.get(target, key, receiver); }, set: function (target, key, value, receiver) { console.log(`setting ${key}`); return Reflect.set(target, key, value, receiver); }, } ); obj.count = 1; // setting count ++obj.count; // getting count setting count 2
Proxy 只有一个静态方法revocable(target, handler)可以用来创建一个可撤销的代理对象。两个参数和构造函数的相同。它返回一个包含了所生成的代理对象本身以及该代理对象的撤销方法的对象。
一旦某个代理对象被撤销,它将变的几乎完全不可用,在它身上执行任何的可代理操作都会抛出 TypeError 异常(注意,可代理操作一共有 14 种,执行这 14 种操作以外的操作不会抛出异常)。一旦被撤销,这个代理对象永远不可能恢复到原来的状态,同时和它关联的目标对象以及处理器对象将有可能被垃圾回收掉。调用撤销方法多次将不会有任何效果,当然,也不会报错。 var revocable = Proxy.revocable( {}, { get(target, name) { return "[[" + name + "]]"; }, } ); // revocable => {"proxy":proxy,"revoke":revoke} var proxy = revocable.proxy; proxy.foo; // "[[foo]]" revocable.revoke(); // 执行撤销方法 proxy.foo; // TypeError proxy.foo = 1; // TypeError delete proxy.foo; // TypeError typeof proxy; // "object" 因为typeof 不属于代理操作
handler 参数是代理函数对象,它一共支持 13 种拦截函数。和 Reflect 的相同。如果没有定义某种操作,那么这种操作会被转发到目标对象身上。 const proxy = new Proxy( {}, { get: function (target, property, receiver) { return receiver; // receiver 总是指向原始的读操作所在的那个对象,一般情况下就是 Proxy 实例。 }, } ); proxy.getReceiver === proxy; // true
如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错。 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
apply 方法拦截函数的调用、call 和 apply 操作。 var target = function () { return "I am the target"; }; var handler = { apply: function () { return "I am the proxy"; }, }; var p = new Proxy(target, handler); p(); // "I am the proxy"
defineProperty 方法拦截了 Object.defineProperty 操作。 var handler = { defineProperty(target, key, descriptor) { return false; }, }; var target = {}; var proxy = new Proxy(target, handler); proxy.foo = "bar"; // 不会生效 // defineProperty 方法返回 false,导致添加新属性总是无效。
注意,如果目标对象不可扩展(non-extensible),则 defineProperty 不能增加目标对象上不存在的属性,否则会报错。另外,如果目标对象的某个属性不可写(writable)或不可配置(configurable),则 defineProperty 方法不得改变这两个设置。
getPrototypeOf方法主要用来拦截获取对象原型,会拦截以下这些操作: Object.prototype.__proto__ Object.prototype.isPrototypeOf() Object.getPrototypeOf() Reflect.getPrototypeOf() instanceof
ownKeys 方法用来拦截对象自身属性读取的操作,会拦截以下操作: Object.getOwnPropertyNames() Object.getOwnPropertySymbols() Object.keys() for...in
通过代理,你可以轻松地验证向一个对象的传值。 let validator = { set: function (obj, prop, value) { if (prop === "age") { if (!Number.isInteger(value)) { throw new Error("The age is not an integer"); } if (value > 200) { throw new Error("The age seems invalid"); } } // The default behavior to store the value obj[prop] = value; }, }; let person = new Proxy({}, validator); person.age = 100; console.log(person.age); // 100 person.age = "young"; // 抛出异常: Uncaught TypeError: The age is not an integer person.age = 1000; // 抛出异常: Uncaught RangeError: The age seems invalidthis 指向
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的 this 关键字会指向 Proxy 代理。 const target = { m: function () { console.log(this === proxy); }, }; const handler = {}; const proxy = new Proxy(target, handler); target.m(); // false proxy.m(); //trueconst 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("2021-07-28"); 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(); // 28
好声音不必出自大价钱余音SG01入手推荐近年来国产耳机越来越优秀,成为很多人的选择。这些新兴的产品通常定价不高,但在声音素质外部做工等方面却非常出色,成为了音响大厂的HiFi平替品。在这些品牌中,余音的发展不容小视,他们
海贝R6PROII无损音乐播放器开售4998元IT之家3月31日消息,海贝R6PROII高清无损HiFi音乐播放器此前已发布,零售价4998元(IT之家注上一代为3900元),于3月31日上市开售,于4月7日开始发货。海贝R6
OPPOFindX6Pro使用续航实测轻松满足一天使用所需在3月21日,OPPOFindX6系列被正式推出,作为全新一代的旗舰产品,OPPOFindX6系列在配置上可谓十分硬核。以OPPOFindX6Pro为例,此款产品配备了骁龙8Gen
凤县交警借力桃花节开展交通安全宣传阳光讯(记者张伟通讯员高棋)春暖留凤关梦里桃花源。3月29日,2023凤县踏青赏花旅游季暨留凤关镇第二届桃花节在酒铺村桃花岭网红打卡地举行。凤县交警联合留凤关幼儿园的师生和家长,走
十大淄博品牌食品发布淄博日报淄博晚报博览新闻记者杨秋云见习记者胡明3月30日,2023淄博食品产业发展大会暨淄博市食品企业联合会第二届会员大会在高青召开。会上,淄博10家企业入选十大淄博品牌食品6家企
海外业务占比提升加码新能源专用车中集车辆在业绩发布会上还说了什么?每经记者王晶每经实习记者孔泽思每经编辑张海妮3月28日,中集车辆(SZ301039,股价10。25元,市值207亿元)举办了2022年度业绩发布会。在宏观经济增速放缓专用车行业低迷
一个人,如果占了这四种长相,关系再亲也要减少往来人在江湖,不可避免的一件事,就是跟别人打交道。有三个问题,是值得我们注意的。到底要跟谁打交道?为什么要跟他们打交道?如何跟目标对象打交道?在这三个问题当中,最重要的,其实是第一个问
清明好时节,祭拜先祖求平安每逢清明时节,人们纷纷前往祭拜自己的先祖。清晨,阳光透过树叶洒在墓地上,清风吹拂,为这个特殊的日子增添了一分神圣和庄严。我也来到了祖先的墓地,为我的先祖们默哀致敬。我走在石板路上,
给青年的十二封信永不过时的金玉良言现代生活忙碌,而青年人又多浮躁。你站在这潮流里,自然也难免跟着旁人乱嚷。不过忙里偶然偷闲,闹中偶然习静,于身于心,都有极大裨益。朱光潜人在青年时总是易于迷茫的,学业前途理想现实爱情
人世间选择,才让你的努力有意义有人说,幸福是可以靠努力争取的。不可否认,努力对人生有巨大的作用,但要彻底改变人生方向或生活状态,光靠努力是不行的,甚至可以说只是杯水车薪,不过去聊胜于无罢了。人生的方向和生活的状
这样说话的人,不要深交为人处事品高低,言谈举止论素质。尊人看本质,敬人看人品!很多时候,我们会善良的以为,一个人说话难听只是因为他心直口快,一个人满嘴负能量只是因为他日子过得不顺。其实,在交谈之中让对方