当前位置: 首页 > news >正文

经典营销型网站二手网站建设论文答辩

经典营销型网站,二手网站建设论文答辩,网站建设 售后服务,十堰网站优化排名目录 一、监听对象的操作二、Proxy基本使用2.1 创建空代理2.2 定义捕获器2.2.1 Proxy的set和get捕获器2.2.2 Proxy(handler)的13个捕获器 三、Reflect的作用3.1 Reflect的使用3.2 Reflect其余方法(9个)3.3 Proxy与Reflect中的receiver参数3.4 Reflect中的construct方法 ECMAScr… 目录 一、监听对象的操作二、Proxy基本使用2.1 创建空代理2.2 定义捕获器2.2.1 Proxy的set和get捕获器2.2.2 Proxy(handler)的13个捕获器 三、Reflect的作用3.1 Reflect的使用3.2 Reflect其余方法(9个)3.3 Proxy与Reflect中的receiver参数3.4 Reflect中的construct方法 ECMAScript 6 新增的代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力。具体地说可以给目标对象定义一个关联的代理对象而这个代理对象可以作为抽象的目标对象来使用。在对目标对象的各种操作影响目标对象之前可以在代理对象中对这些操作加以控制。代理是目标对象的抽象。从很多方面看代理类似 C 指针因为它可以用作目标对象的替身但又完全独立于目标对象。目标对象既可以直接被操作也可以通过代理来操作。但直接操作会绕过代理施予的行为。注意 ECMAScript 代理与 C 指针有重大区别后面会再讨论。不过作为一种有助于理解的类比指针在概念上还是比较合适的结构。 Proxy 与 Reflect 在逆向补环境的时候有大量应用建议重点掌握。 一、监听对象的操作 我们先来看一个需求有一个对象我们希望监听这个对象中的属性被设置或获取的过程通过 2024最新版JavaScript逆向爬虫教程-------基础篇之面向对象 一文中的 2.2 对象属性操作的控制 小节了解到我们可以通过属性描述符中的存储属性描述符 Object.defineProperty() 方法来做到在 2024最新版JavaScript逆向爬虫教程-------基础篇之面向对象 一文 2.2 对象属性操作的控制 小节有学习到访问器与数据两种不同的属性描述符而通过其访问器分支 get、set实现监听对象属性的操作。 const person {name: amo,age: 18 }let old_age person.ageObject.defineProperty(person, age, {get: function () {console.log(监听到该age属性被访问: , old_age);},set: function (value) {console.log(监听到该age属性被设置, value);old_age value;} })person.age //监听到该age属性被访问 console.log(person.age);//监听到该age属性被访问 undefined person.age 20//监听到该name属性被设置运行结果如下图所示 这里能够看到不管是查看属性还是设置属性都能够被 get 与 set 所捕获到。但这里有一个问题那就是监听到属性了为什么没返回正确的 age 属性的值而是 undefined这是因为查看属性会触发 get而 get 没有返回内容相当于 return 了一个 undefined关于这一点我们可以 return old_age 来试一下如下图且该描述在 MDN 文档中也有进行说明。 通过 get 监听案例我们了解到如何监听到对象的操作(查看或者改变)也清楚的知道这是如何返回值的在 get 监听案例中我们返回了正确的 value 值只需要拿到监听的 key然后从对象中针对性获取即可同时处理下当改变值时在 set 中新值覆盖旧值就行关键在于该方式的局限性较大只能监听一个属性而一个对象中的属性在大多数情况下都不止一个此时有什么办法呢我们目的从监听对象属性到监听对象的全部属性首先我们需要先获取全部的 key 属性然后遍历 key 属性填入 defineProperty 方法中实现监听全部属性示例代码 const person {name: amo, age: 18, hobbies: [drink] }Object.keys(person).forEach(key {let old_value person[key]Object.defineProperty(person, key, {get: function () {console.log(监听到${key}属性被访问);return old_value}, set: function (value) {console.log(监听到${key}属性被设置值);old_value value}}) })person.name//监听到name属性 person.age//监听到age属性 person.hobbies//监听到hobbies属性person.name paul//监听到name属性被设置值 person.age 28//监听到age属性被设置值 person.hobbies [read]//监听到hobbies属性被设置值console.log(person.name);//paul console.log(person) console.log(person.hobbies);通过这种方式解决了单独使用 defineProperty() 方法只能监听单一属性的难点但是这样做是有缺点的首先defineProperty() 方法设计的初衷不是为了去监听截止一个对象中所有的属性的。这种做法很像是利用了该方法的特性另辟蹊径去实现在达成监听目的是同时将初衷原本是定义普通的属性强行将它变成了数据属性描述符。其次如果我们想监听更加丰富的操作比如新增属性、删除属性那么 defineProperty() 方法是无能为力的所以我们要知道访问器描述符设计的初衷并不是为了去监听一个完整的对象用在实现监听对象操作上属于比较勉强就像不合身的衣服能穿但穿着难受不贴合会妨碍我们的一些行动在这一方面有更加合适的 APIProxy用初衷与用法一致的 API能让我们的意图更加明确可靠。 二、Proxy基本使用 2.1 创建空代理 最简单的代理是空代理即除了作为一个抽象的目标对象什么也不做。默认情况下在代理对象上执行的所有操作都会无障碍地传播到目标对象。因此在任何可以使用目标对象的地方都可以通过同样的方式来使用与之关联的代理对象。代理是使用 Proxy 构造函数创建的这个构造函数接收两个参数目标对象和处理程序对象。缺少其中任何一个参数都会抛出 TypeError。要创建空代理可以传一个简单的对象字面量作为处理程序对象 从而让所有操作畅通无阻地抵达目标对象。如下面的代码所示在代理对象上执行的任何操作实际上都会应用到目标对象。唯一可感知的不同就是代码中操作的是代理对象。 const target {id: target };const handler {};const proxy new Proxy(target, handler);// id属性会访问同一个值 console.log(target.id); // target console.log(proxy.id); // target//给目标属性赋值会反映在两个对象上 //因为两个对象访问的是同一个值 target.id foo; console.log(target.id); // foo console.log(proxy.id); // foo//给代理属性赋值会反映在两个对象上 //因为这个赋值会转移到目标对象 proxy.id bar; console.log(target.id); // bar console.log(proxy.id); // bar// hasOwnProperty()方法在两个地方 //都会应用到目标对象 console.log(target.hasOwnProperty(id)); // true console.log(proxy.hasOwnProperty(id)); // true// Proxy.prototype是undefined //因此不能使用instanceof操作符 // TypeError: Function has non-object prototype undefined in instanceof check console.log(target instanceof Proxy); // TypeError: Function has non-object prototype undefined in instanceof check console.log(proxy instanceof Proxy); //严格相等可以用来区分代理和目标 console.log(target proxy); // false2.2 定义捕获器 从 2.1 创建空代理 我们了解到在 ES6 中新增了一个 Proxy 类(构造函数) 这个类从名字就可以看出来是用于帮助我们创建一个代理的代理在维基百科上解释为代表授权方处理事务在这里可以理解为 Proxy 代表对象处理监听的相关操作也就是说如果我们希望监听一个对象的相关操作那么我们可以先创建一个代理对象Proxy 对象之后对该对象的所有操作都通过代理对象来完成代理对象可以监听我们想要对原对象进行哪些操作。我们可以将 一、监听对象的操作 小节中的 defineProperty 案例用 Proxy 来实现一次(监听 person 对象) const p new Proxy(target, handler)//监听的对象 处理对象其 target 参数为授权方也就是要使用 Proxy 包装的目标对象(侦听对象)该目标对象可以是任何类型的对象包括原生数组函数甚至另一个代理。首先我们需要 new Proxy() 构造函数并且传入需要侦听的对象以及一个处理对象可以称之为 handler在我们这个案例中objProxy 一开始是没有和 obj 对象产生联系的通过 target 参数去代理 obj 对象此时对 obj 的所有操作都应该通过 objProxy 操作了我们对 objProxy 的所有操作最终效果都会作用在 obj 对象身上(这个在 2.1 创建空代理 小节已经演示过这里不再赘述)那多了一层代理像 defineProperty 方法完成正常 查询、改动 操作时我们也能够在这一些关键时机去处理一些事情。 const person {name: amo,age: 18 }//1.创建一个Proxy对象Proxy对象是一个构造函数所以使用new创建 let handler {} // 暂时还是先定义一个空的处理程序对象 const objProxy new Proxy(person, {}) //2.对person的所有操作都应该去操作objProxy console.log(objProxy.name); // amo objProxy.age 20 console.log(objProxy.age) // 20 console.log(person) // 已经被修改了 { name: amo, age: 20 } // console.log(person.name);我们之后的操作都是直接对 Proxy 的操作而不是原有的对象而对 Proxy 的操作需要使用到第二个参数 handler通常 handler 被称为处理对象可以理解为处理 Proxy 操作的对象也有 handler 被译为处理器的在 handler 中有众多的捕获器用来捕捉 查找、改动 这个变化过程的一些时机不同的捕获器针对于不同的时机和具备不同作用默认情况下在我们还不了解有什么捕获器时我们可以给一个空对象依旧可以通过操作 objProxy 代理从而作用于 person 对象身上这并不影响代理的作用只是没有使用捕获器介入这一过程做出其他事情。 2.2.1 Proxy的set和get捕获器 我们如果想要介入该操作过程最主要的是以下两个捕获器 handler.get属性读取操作的捕捉器handler.set属性设置操作的捕捉器 这两捕获器作用和一开始我们使用 defineProperty 的 get、set 是相同的在这个基础上我们可以继续完善 Proxy 的这个案例代码这两个捕获器也会接受到对应的参数gettrapTarget(监听对象)、property(监听对象的属性)receiver(代理对象)settrapTarget(监听对象)、property(监听对象的属性)、value(改动的新值)receiver(代理对象)通过两个捕获器的这些参数实现 查找 与 改变 操作在这里能够看到 trapTarget 在模板字符串中被解析为 [object Object]会被以 toString() 方法的形式进行解析(当对象需要被转换成字符串形式如在模板字符串或字符串拼接中使用时JS 自动调用这个 toString() 方法)所以如果需要清楚知道 trapTarget 的信息内容可以单独抽离出来。 const person {name: amo,age: 10,hobbies: drink }const objProxy new Proxy(person, {get: function (trapTarget, property, receiver) {console.log(监听到${trapTarget}对象的${property}属性被访问了);console.log(trapTarget, 单独抽离出来);//{ name: jerry, age: 10, hobbies: drink } 单独抽离出来return trapTarget[property]},set: function (trapTarget, property, value, receiver) {console.log(监听到${trapTarget}对象的${property}属性被设置值了);trapTarget[property] value} })objProxy.name jerry//监听到[object Object]对象的name属性被设置值了 console.log(objProxy.name);//监听到[object Object]对象的name属性被访问了 jerry此时我们再来回顾 handler 参数一个对象其属性是定义代理在对其执行操作时的行为的函数首先这是一个对象默认情况下是空对象在该情况下对 Proxy 代理的任何操作都和直接对监听对象的操作没有任何区别在该对象里面有各种属性这些属性会在我们对 Proxy 代理执行操作时拦截下对应的一些操作handler 对象内的属性又称为拦截器一共13个都可以算成 handler 对象的实例方法每一个 handler 实例方法都是和正常对象的某个实例方法对应上从而实现拦截。例如我们实现监听对象的 查找、改动这两个操作不管是 defineProperty 还是 Proxy 的实现方式都是使用 set 与 get 方法我们可以简单理解为狸猫换太子。最后我们还要说明 set 与 get 分别对应的是函数类型最后的参数 receiver该参数指Proxy 或者继承 Proxy 的对象具体做什么的我们暂时跳过等下再回头来看。 2.2.2 Proxy(handler)的13个捕获器 一个空的处理器handler将会创建一个与被代理对象行为几乎完全相同的代理对象。通过在 handler 对象上定义一组函数我们可以自定义被代理对象的一些特定行为在这里需要注意几乎完全相同的代理对象这意味使用方式和行为是一致的每个 捕获器(trap) 对应于一个特定的对象操作。如果处理器对象有相应的捕获方法则该方法会被调用如果没有操作会直接转发给目标对象捕获器中的逻辑决定了是否将操作重定向至目标对象是否修改操作的行为或是否直接返回一个自定义的结果。在正式讲解这13个捕获器之前了解更为重要 捕获器的拦截机制JS引擎会在内部为Proxy对象维护一个关联的目标对象和处理器对象。当对Proxy对象进行操作时这些操作首先被送到处理器对象方法查找与执行对于每种可以拦截的操作如get、set、apply等处理器对象可以提供一个同名的方法来拦截相应的操作在处理器对象中查找到对应方法进行执行。 在13个捕获器中有4个常用的其中两个是已经讲过的set、get另外两个是 has 与 deleteProperty这四个捕获器涵盖了对对象进行读取、写入和属性检查的基本操作这些是日常编程中最常见的操作。几乎所有涉及对象属性的交互都会触及到这些操作包括访问、修改、检查属性是否存在以及删除属性如下图所示 示例代码 const obj {name: amo,age: 20 }const objProxy new Proxy(obj, {//代理objset: function (target, property, value) {console.log(监听监听${property}的设置值);target[property] value},get: function (target, property) {console.log(监听:监听${property}的获取);return target[property]},deleteProperty: function (target, property) {console.log(监听监听删除${property}属性);delete obj.name},has: function (target, property) {console.log(监听监听in判断${property}属性);return property in target} })delete objProxy.name console.log(age in objProxy);以及如下方剩余的9个捕获器方法其对应方法来源几乎都来自 Object使用方式一致因此我们不再进行赘述。在这些方法中我们同样看到了 defineProperty 方法那为什么还要多此一举将set、get单独抽离出来在讲解监听对象操作的末尾时我们说这并不符合 defineProperty() 方法的初衷因此具备一定的局限性一个 Proxy 可以拦截其目标对象上的所有 get 和 set 操作而不仅仅是单个属性。非常适合于创建一个全面拦截和操作对象访问行为的模型而我们通过 defineProperty() 方法时还需要 forEach 遍历一下手动进行一些较为复杂的操作需要对每个属性和对象重复定义而且 Proxy 可以根据属性名、目标对象的状态或其他外部条件动态地改变属性的行为。这提供了比 Object.defineProperty() 方法更大的灵活性。 Object.defineProperty() 方法的初衷是在对象初始化时用于设定属性的特殊行为一旦初始化结束后就不再频繁变动(固定下来除非再次使用 defineProperty 进行修改JS 也不希望轻易进行变动)属于是静态的行为更适用于那些对象结构已知且不需要动态改变访问行为的情况。而监听对象一有变化立刻行动是属于动态调整的范畴需要随时准备拦截对象的操作Proxy 在这方面更具备优势可以根据条件动态地修改拦截行为无需重新定义属性或对象能够应对复杂的或动态变化的应用场景。在执行方面由于 Proxy 的设计和实现是作为 ECMAScript 语言标准的一部分JS 引擎会专门进行优化(如内联缓存)具备更独特的优势而 Proxy 作为代理用途比 defineProperty() 方法更加广泛毕竟 defineProperty() 方法在 handler 中也只是13个拦截器之一况且如果在一个对象上频繁使用 Object.defineProperty() 方法尤其是在其原型链上可能会导致性能下降因为每次属性访问都可能需要解析更复杂的定义和条件一旦打算使用 defineProperty() 方法来实现该监听操作所监听对象的性质就必须被迫变为访问属性描述符哪怕原本是数据属性描述符也会被迫转变这是不合理的。 三、Reflect的作用 Reflect 也是 ES6 新增的一个 API它是一个对象字面的意思是反射通常配合 Proxy 进行使用需要注意 Reflect 不是类也不是构造函数或者函数对象而是一个标准的内置对象所以我们可以直接 Reflect.xxx 的方式进行使用而不能通过 new 调用Reflect 中的所有方法都是静态方法就像 Math 对象一样。Reflect 主要提供了很多操作 JavaScript 对象的方法有点像 Object 中操作对象的方法比如 Reflect.getPrototypeOf(target) ⇒ Object.getPrototypeOf() Reflect.defineProperty(target, propertyKey, attributes) ⇒ Object.defineProperty()这里我们能够看到连方法名都是一样的如果我们有 Object 可以做这些操作那么为什么还需要有 Reflect 这样的新增对象呢这是因为在早期的 ECMA 规范中没有考虑到这种对对象本身的操作如何设计会更加规范所以将这些 API 放到了 Object 上面但后续 Object 上的新东西越来越多Object 越来越重对于最顶层的 Object 来说身为所有类的父类他本身不应该包含太多的东西的因为父类里的东西是会被继承到子类中的太多的东西必然会加重子类的负担而过于臃肿且 Object 作为一个构造函数这些语言内部操作即元编程操作的方法操作实际上放到它身上并不合适另外还包含一些类似于 in、delete 操作符让 JS对 象看起来是会有一些奇怪的所以在 ES6 中新增了 Reflect让我们这些操作都集中到了 Reflect 内置对象上这和 Proxy 中的 handler 是对应起来的一模一样的13个方法有些方法是 Reflect 新增的并非全部来自 Object 对象。 通过 MDN 文档所对应的关系能够看到其关系紧密相连并且在该文档中也详细对比了 Object 和 Reflect Reflect 的方法通常返回更标准化的结果。在成功时许多 Reflect 的操作会返回操作的结果例如返回 true 或者属性值在失败时返回 false而不是抛出异常。这和 Object 的某些方法如 Object.defineProperty在遇到错误时抛出异常的行为不同。这种设计在面对错误处理更加一致和可控Reflect 内的方法作为和 Math 一样的静态方法它的方法不会被任何对象继承。这种设计避免了在对象原型链中可能出现的混乱和冗余确保了 Reflect 的方法仅用于反射和底层操作而不会被意外地用于业务逻辑或其他目的Reflect 和 Proxy 进行配合使用也非常的顺手一一对应的关系从使用角度上看非常契合因为在一开始设计的时候这两者就注定相辅相成 和 Proxy 进行对应简单练习一下最常见的 set、get、has、deleteProperty 这四个方法示例代码 // 定义一个简单的对象 const obj {name: amo,age: 18 };// 使用 Reflect.has() 检查对象中是否存在指定的属性 console.log(检查 name 是否存在:, Reflect.has(obj, name)); // 输出 true console.log(检查 gender 是否存在:, Reflect.has(obj, gender)); // 输出 false// 使用 Reflect.get() 获取对象属性的值 console.log(Name:, Reflect.get(obj, name)); // Name: amo console.log(Age:, Reflect.get(obj, age)); // Age: 18// 如果属性不存在可以提供一个默认值 测试失败 // console.log(Gender:, Reflect.get(obj, gender, {gender: Not specified})); // 输出 Not specified // 实际输出undefined// 使用 Reflect.set() 设置对象属性的值 Reflect.set(obj, age, 19); // 设置 age 属性为 19 console.log(Updated Age:, obj.age); // 输出 19// 使用 Reflect.deleteProperty() 删除对象的一个属性 Reflect.deleteProperty(obj, name); // 删除 name 属性 console.log(Name after deletion:, obj.name); // 输出 undefined// 再次使用 Reflect.has() 检查 name 属性是否还存在 console.log(检查 name 删除后是否存在:, Reflect.has(obj, name)); // 输出 false3.1 Reflect的使用 那么我们可以将之前 Proxy 案例中对原对象的操作都修改为 Reflect 来操作修改成 Reflect 进行操作肯定是有好处例如返回值失败情况下明确 false 而非抛出异常这是更可预测的错误处理方式也不需要使用 try-catch 来捕获错误更加动态灵活更加函数式编程(Reflect 方法全是函数)且 Reflect 的主要应用场景也是配合 Proxy 进行处理但其他 Object 中的相同方法也可以用 Reflect 进行取代使用示例代码 const obj {name: amo, age: 20 }const objProxy new Proxy(obj, {set: function (target, property, value, receiver) {//下面这种写法好不好,规范吗? 有点奇怪因为直接操作原对象了// target[key] value//代理对象的目的:不再直接操作原对象,所以我们采用间接操作的方式(好处一)//从语言层面通过反射去操作const isSuccess Reflect.set(target, property, value)//Reflect.set会返回布尔值,可以判断本次操作是否成功(好处二)if (!isSuccess) {throw new Error(set${property}failure)}}, get: function (target, property, receiver) {} })//操作代理对象 objProxy.name jerry console.log(obj);//{ name: jerry, age: 20 },修改成功3.2 Reflect其余方法(9个) Reflect 剩余的9个方法此处只会简单进行说明有用到再来翻阅即可 //1.用于获取一个对象的原型也称为 prototype该方法返回该对象的原型对象即该对象继承的对象。 //如果该对象没有继承任何对象则返回 null。 Reflect.getPrototypeOf(target) ⇒ Object.getPrototypeOf() //2.设置对象原型的函数. 返回一个 Boolean 如果更新成功则返回true Reflect.setPrototypeOf(target, prototype) //3.用于判断一个对象是否可以被扩展即是否可以添加新的属性 //该方法返回一个布尔值表示该对象是否可以被扩展即是否可以通过,Object.defineProperty()或者直接赋值添加新的属性 Reflect.isExtensible(target) ⇒ Object.isExtensible() //4.返回一个Boolean用于阻止一个对象被扩展即不允许添加新的属性 //该方法返回一个布尔值表示该对象是否被阻止了扩展即是否不允许添加新的属性 //其中target 是要阻止扩展的对象。 Reflect.preventExtensions(target) ⇒ Object.preventExtensions() //5.如果对象中存在该属性则返回对应的属性描述符, 否则返回 undefined Reflect.getOwnPropertyDescriptor(target, propertyKey) ⇒ Object.getOwnPropertyDescriptor() //6.如果设置成功就会返回 true Reflect.defineProperty(target, propertyKey, attributes) ⇒ Object.defineProperty() //7.返回一个包含所有自身属性(不包含继承属性)的数组。(类似于Object.keys(), 但不会受enumerable影响) Reflect.ownKeys(target) //8.对一个函数进行调用操作同时可以传入一个数组作为调用参数。和 //Function.prototype.apply() 功能类似 Reflect.apply(target, thisArgument, argumentsList) //9.对构造函数进行 new 操作相当于执行 new target(...args) Reflect.construct(target, argumentsList[, newTarget])3.3 Proxy与Reflect中的receiver参数 receiver 参数较难理解是位于 Proxy、Reflect 这两者的 get 与 set 方法中的最后一个参数那么它的作用是什么如果我们的源对象obj有 setter、getter 的访问器属性那么可以通过 receiver 来改变里面的 this。在正式讲解前我们需要了解 getter 与 setter 正确的使用逻辑示例代码 const obj {_name: amo,get name() {return this._name},set name(newValue) {this._name newValue} }obj.name jerry console.log(obj.name); // jerry对象的 setter、getter 的监听层效果 此时加上我们的 Proxy 代理层和对应的 get、set 捕获器以及对应的 Reflect 的 set、get 方法来实现相对于一开始的最初数据源、监听层、实际使用多了一层代理层(Proxy)示例 const obj {_name: amo,get name() {return this._name},set name(newValue) {this._name newValue} }//代理层 const objProxy new Proxy(obj, {get: function (target, property) {return Reflect.get(target, property)},set: function (target, property, value) {Reflect.set(target, property, value)} })//实际使用 objProxy.name jerry console.log(objProxy.name); // jerry console.log(obj.name); // jerryProxy 代理层拦截效果 在这里能够看到Reflect 借用 Proxy 拦截下实际使用到监听层的时机进行真正的处理查询获取顺序 实际使用 ⇒ Proxy 代理层 get 拦截 ⇒ Reflect.get 触发 ⇒ 监听层 getter 触发 ⇒ 从数据源中获取到数据 ⇒ 返回查询结果。改动顺序 实际使用 ⇒ Proxy 代理层 set 拦截 ⇒ Rflect.set 触发 ⇒ 监听层 setter 触发 ⇒ 修改数据源。在这里能够看到Proxy 的 get 方法和 obj 对象的 name 属性的 getter 方法都会触发而这个过程的 Reflect 方法只是搭上顺风车在 Proxy 的内部进行操作在这个过程中get、set 的触发顺序分别为 Proxy、Reflect、监听层 setter。那 Reflect.set 和 get 不会和 obj 中的 setter 和 getter 产生冲突吗这就需要理解它们是如何相互作用的首先是不会冲突这是一个协作的过程Reflect.set 本质上是在 请求 对属性的设置如果属性有 setter它就会触发这个 setter。因此如果在 setter 中有额外的逻辑处理或者修改值那么最终的属性值会是 setter 执行后的结果。在这个过程中Reflect.set 只是作为触发器Reflect.get 也是如此本质上是在 请求 获取属性值如果属性有 getter它就会触发这个 getter。因此返回的值将是 getter 执行后的结果包括任何逻辑处理或值的修改Reflect 身上的 get 与 set 会尊重对象身上的 getter 和 setter最终的决定权依旧在 setter 和 getter 身上但如果我们已经在 Reflect 中进行操作也就没有继续操作 getter 和 setter 的动机。 此时有一个问题obj 对象中的 getter 与 setter 此时内部的 this._name 指向的是哪一个对象是 obj 对象还是 objProxy 对象呢obj 对象中的 name 方法作为普通函数而非箭头函数其 this 是会受到影响的那此时的 this 对象指向的是谁答案是 obj 对象而这又是为什么呢根据我们刚才的查找顺序与改动顺序能够确定数据的最终处理权依旧在 obj 对象身上此时 obj 与 objProxy 代理对象的关系并不够紧密。this 指向于 obj而非 objProxy 代理该情况下在处理继承或原型链时可能会导致 this 指向问题 const obj {_name: amo,get name() {return this._name},set name(newValue) {this._name newValue} }const objProxy new Proxy(obj, {get: function (target, property) {console.log(被访问, target, property);return Reflect.get(target, property)},set: function (target, property, newValue) {console.log(被设置, target, property);Reflect.set(target, property, newValue)} }) //这在Reflect.set触发之前打印的所以输出的_name为未修改状态在浏览器控制台则为最终结果amo objProxy.name jerry//被设置 { _name: amo, name: [Getter/Setter] } name console.log(objProxy.name);//被访问 { _name: jerry, name: [Getter/Setter] } name // jerry如果我们使用了 Proxy 代理我们肯定是希望代理更加完善的尤其是要用 Proxy 进行包装的目标对象范围是任何类型的对象包括原生数组函数甚至另一个代理。而对象的背后存在着继承等因素此时就需要 this 层面的拦截这就必须要说到我们的 Receiver 参数的(Proxy、Reflect的get、set最后参数)Receiver 参数类似各种数组方法中的最后参数 thisArg不同之处在于Proxy 和 Reflect 的 Receiver 参数需要结合起来我们拿这两者的 get 方法举例 // Proxy.get方法的Receiver参数是: Proxy自身代理 // Reflect.get方法是Receiver参数是: 如果target对象中指定了getterreceiver则为getter调用时的this值这就可以结合起来了Proxy.get 的 Receiver 参数提供确定的 this 值Reflect.get 的 Receiver 参数提供 this 放置的位置在这方面上get 与 set 是一样的此时我们再来进行刚才的 set 设置等实践完成我们再来进行说明 const obj {_name: amo,get name() {return this._name},set name(newValue) {this._name newValue} }const objProxy new Proxy(obj, {get: function (target, property, receiver) {console.log(被访问, target, property);return Reflect.get(target, property, receiver)},set: function (target, property, newValue, receiver) {console.log(被设置, target, property);Reflect.set(target, property, newValue, receiver)} }) //这在Reflect.set触发之前打印的所以输出的_name为未修改状态在浏览器控制台则为最终结果amo objProxy.name jerry // 被设置 { _name: amo, name: [Getter/Setter] } name // 被设置 { _name: amo, name: [Getter/Setter] } _name console.log(objProxy.name); // jerry // 被访问 { _name: jerry, name: [Getter/Setter] } name // 被访问 { _name: jerry, name: [Getter/Setter] } _name可以看到在加上 receiver 之后objProxy 代理的 get、set 方法都被调用了两次在这两次结果中通过 key我们能察觉到有所不同一个是 name一个是 _name在第一次拦截中是正常在 Proxy 调用了 Reflect 的 set 与 get这是与监听层形成交互此时的 property 是指 obj 中的 name 方法(setter、getter)Proxy 代理层调用 Reflect 与监听层形参交互 主要在第二次拦截中监听层的 this 被 Reflect 的 receiver 所改变变为 Proxy 代理本身此时在 obj 中的代码就会变为如下形式 const obj {_name: amo,get name() {return objProxy._name//{ _name: amo, name: [Getter/Setter] }._name},set name(newValue) {objProxy._name newValue} }而 objProxy 的内容是 { _name: amo, name: [Getter/Setter] }此时访问的则是里面的 _name这时候我们再来回头看打印内容就会发现一目了然。两次输出意味着 objProxy 在两个不同的地方被调用了一次在 Proxy 代理层一次在监听层Proxy 和 Reflect 的 receiver 做到了替换掉 obj 对象中的 this从而进一步提高拦截的完整度 objProxy.name jerry // 被设置 { _name: amo, name: [Getter/Setter] } name // 被设置 { _name: amo, name: [Getter/Setter] } _name console.log(objProxy.name); // jerry // 被访问 { _name: jerry, name: [Getter/Setter] } name // 被访问 { _name: jerry, name: [Getter/Setter] } _nameProxy 与 Reflect 中的 receiver 配合作用 最后我们来总结一下 receiverReflect 中的 receiver 更加重要是改变 this 的核心而 Proxy 中的 receiver 虽然与 Reflect 更搭但值不一定就必须使用 Proxy 代理对象而是根据自己实际需求决定在这里我们能够看到Proxy 的 receiver 通常表示 Proxy 本身那为什么不直接使用 Proxy而是还专门设计一个 receiver 出来呢这和 this 的原因很像Proxy 所返回的代理是固定的例如我们的 objProxy虽然在大多数情况下可能是期望的行为但这已经是限制死了并不是动态决定this 绑定总是指向 objProxy一旦涉及继承或多层代理就可能会出现问题。所以在 MDN 文档中的描述中receiver 的值除了本身之外还包括了继承 Proxy 的对象从这点也说明了其动态性直接写死(固定)并不是一个好的选择当代理对象继承自另一个对象时通过 receiver 传递正确的 this 可以确保在整个原型链中方法和访问器属性的调用上下文正确。这确保方法或访问器在访问 this 时能够访问到正确的属性而不是错误地访问到代理对象或基对象的属性Proxy 与 Reflect 中的 receiver 对比 3.4 Reflect中的construct方法 Reflect.construct() 方法的行为有点像 new 操作符构造函数相当于运行 new target(…args) //target被运行的目标构造函数 //argumentsList类数组目标构造函数调用时的参数 //newTarget作为新创建对象的原型对象的 constructor 属性 Reflect.construct(target, argumentsList[, newTarget])//有返回值想要知道 construct 方法的作用我们需要举一个应用场景来说明在下方案例中Student 是一个构造函数通过 Student 所 new 出来的对象自然是 Student 类型现在有一个需求我希望 new Student结果是 Teacher 类型在以前是很难做到的需要进行额外的操作例如使用工厂函数 function Student(name, age) {this.name namethis.age age }function Teacher(name, age) {}const stu new Student(amo, 20) console.log(stu);//Student { name: amo, age: 20 } console.log(stu.__proto__ Student.prototype);//true但 Reflect.construct 方法可以实现该操作只需要一行代码即可实现参数1是我们的目标对象参数2是原先目标对象内的参数数据参数3是要改变为的类型并且能够发现 teacher 的隐式原型等于 Teacher 的显示原型而这意味着该类型并不是简单的改变构造函数和原型的分离意味着任何在 Teacher.prototype 上定义的方法或属性都可以被 teacher 对象访问造就了一个使用 Student 的构造逻辑和 Teacher 的原型这是非常灵活的继承类型打破了传统构造函数和原型继承的限制。但同时我们也应该清楚越是灵活就越是双刃剑在一般情况下我们是用不到该方法的 const teacher Reflect.construct(Student, [amo, 18], Teacher) console.log(teacher);//Teacher { name: amo, age: 18 } console.log(teacher.__proto__ Teacher.prototype);//true在 Babel 源码的 ES6 转 ES5 的继承中就使用了该方式该函数主要用来生成一个 超类 构造函数也就是用于在派生类中调用基类超类的构造函数通常是在派生类的构造函数中通过 super() 实现的而在该源码中不允许使用 super 去调用父类的构造函数(逻辑数据)因为在其他地方做出限制使用 super 会报错此时就通过 Reflect.construct 方法将 super 目标(父类)作为目标对象以新创建的当前构造函数进行继承实现了当前构造函数的原型是自身而内在构造逻辑是 super 目标(父类)另类的实现了和 super 调用一样的效果 function _createSuper(Derived) {var hasNativeReflectConstruct _isNativeReflectConstruct();return function _createSuperInternal() {var Super _getPrototypeOf(Derived),result;if (hasNativeReflectConstruct) {var NewTarget _getPrototypeOf(this).constructor;//Reflect体现NewTarget为接下来要使用的构造函数类型借用了父类的构造逻辑形成了更加灵活的result初始结果result Reflect.construct(Super, arguments, NewTarget);} else {result Super.apply(this, arguments)}return _possibleConstructorReturn(this, result)} }
http://www.dnsts.com.cn/news/153294.html

相关文章:

  • 做兼职什么网站淘宝客手机网站
  • pc端网站建设联系方式可以赚零花钱的小程序
  • 网站里面的超链接怎么做网站开发用
  • 寒亭营销型网站建设太仓做网站的公司
  • 兰州市七里河建设局网站天眼查官网入口网页版
  • 邢台集团网站建设价格衡水网站制作公司
  • 如何引用网站图片怎么查看一个网站的后台
  • 重庆长寿网站建设海门建网站公司
  • 顺企网浙江网站建设如何开发游戏辅助软件
  • php网站建设实例视频教程wordpress 生成ppt
  • 网站怎么做三个页面网站门户是什么意思
  • 网站开发最佳组合移动互联网开发课件
  • 郑州网站推建设什么是移动端网站适配
  • 网站建设需要了解的信息工程分包网
  • 网站直接跳转移动终端网站建设
  • 创新的网站微信小程序代码大全
  • 企业网站建立公司济南网上注册公司流程
  • 广州网站建设联享科技剑阁县规划和建设局网站
  • 传诚信网站建设普宁17网站一起做网店
  • 仿网站开发最美情侣免费高清视频
  • 建设工程主管部门网站徐州人才网最新招聘
  • 企业客户信息管理软件湖北网站优化公司
  • 云定制网站dedecms做电商网站
  • 德尔普网站建设网站建设福
  • 连云港关键词优化排名seoaoo
  • 山西网站开发公司优书网
  • 重庆荣昌网站建设报价湛江网站建设优化建站
  • 哪些知名网站域名在国内注册做网站的哪家公司好
  • 网站外链怎么购买动漫网页设计代码
  • 越秀低价网站建设上海网站制作网站建设