《每天十分钟》-红宝书第4版-代理与反射(二)

2024-06-15 22:12

本文主要是介绍《每天十分钟》-红宝书第4版-代理与反射(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

代理另一个代理
代理可以拦截反射 API 的操作,而这意味着完全可以创建一个代理,通过它去代理另一个代理。这样就可以在一个目标对象之上构建多层拦截网:

 foo: 'bar' 
}; 
const firstProxy = new Proxy(target, { get() { console.log('first proxy'); return Reflect.get(...arguments); } 
}); 
const secondProxy = new Proxy(firstProxy, { get() { console.log('second proxy'); return Reflect.get(...arguments); } 
}); 
console.log(secondProxy.foo); 
// second proxy 
// first proxy 
// ba

代理的问题与不足

  1. 代理中的 this
 thisValEqualsProxy() { return this === proxy; } 
} 
const proxy = new Proxy(target, {}); 
console.log(target.thisValEqualsProxy()); // false 
console.log(proxy.thisValEqualsProxy()); // true

以上代码符合预期

const wm = new WeakMap(); 
class User { constructor(userId) { wm.set(this, userId); } set id(userId) { wm.set(this, userId); } get id() { return wm.get(this); } 
}
const user = new User(123); 
console.log(user.id); // 123 
const userInstanceProxy = new Proxy(user, {}); 
console.log(userInstanceProxy.id); // undefined

以上代码 ,User 实例一开始使用目标对象作为 WeakMap 的键,代理对象却尝试从自身取得这个实例。要解决这个问题,就需要重新配置代理,把代理 User 实例改为代理 User 类本身。

const UserClassProxy = new Proxy(User, {}); 
const proxyUser = new UserClassProxy(456); 
console.log(proxyUser.id);

其实一个走的是构造函数,一个是触发的 set ,而不同的this 导致的

  1. 代理与内部槽位
    一个典型的例子就是 Date 类型。根据 ECMAScript 规范,Date 类型方法的执行依赖 this 值上的内部槽位[[NumberDate]]。代理对象上不存在这个内部槽位,而且这个内部槽位的值也不能通过普通的 get()和 set()操作访问到,于是代理拦截后本应转发给目标对象的方法会抛出 TypeError:
const target = new Date(); 
const proxy = new Proxy(target, {}); 
console.log(proxy instanceof Date); // true 
proxy.getDate(); // TypeError: 'this' is not a Date object

简单说,代理就是无法代理内部插槽
Map,Set,Date,Promise 等,都使用了所谓的“内部插槽”。

let map = new Map();let proxy = new Proxy(map, {});proxy.set('test', 1); // Error

解决办法

let map = new Map();let proxy = new Proxy(map, {get(target, prop, receiver) {let value = Reflect.get(...arguments);
return typeof value == 'function' ? value.bind(target) : value;}
});proxy.set('test', 1);
alert(proxy.get('test')); // 1(工作了!)

代理捕获器与反射方法

  1. get()
const myTarget = {}; 
const proxy = new Proxy(myTarget, { get(target, property, receiver) { console.log('get()'); return Reflect.get(...arguments) } 
}); 
proxy.foo; 
// get()
  1. set()
const myTarget = {}; 
const proxy = new Proxy(myTarget, { set(target, property, value, receiver) { console.log('set()'); return Reflect.set(...arguments) } 
}); 
proxy.foo = 'bar'; 
// set()
  1. has()
const myTarget = {}; 
const proxy = new Proxy(myTarget, { has(target, property) { console.log('has()'); return Reflect.has(...arguments) } 
}); 
'foo' in proxy; 
// has()
  1. defineProperty()
  2. getOwnPropertyDescriptor()
  3. deleteProperty()
  4. ownKeys()
  5. getPrototypeOf()
  6. setPrototypeOf()
  7. isExtensible()
  8. preventExtensions()
  9. apply()
  10. construct()

代理模式
举一些有用的编程模式
14. 跟踪属性访问

const user = { name: 'Jake' 
}; 
const proxy = new Proxy(user, { get(target, property, receiver) { console.log(`Getting ${property}`); return Reflect.get(...arguments); }, set(target, property, value, receiver) { console.log(`Setting ${property}=${value}`); return Reflect.set(...arguments); } 
}); 
proxy.name; // Getting name 
proxy.age = 27; // Setting age=27
  1. 隐藏属性
const hiddenProperties = ['foo', 'bar']; 
const targetObject = { foo: 1, bar: 2, baz: 3 
}; 
const proxy = new Proxy(targetObject, { get(target, property) { if (hiddenProperties.includes(property)) { return undefined; } else { return Reflect.get(...arguments); } }, has(target, property) {if (hiddenProperties.includes(property)) { return false; } else { return Reflect.has(...arguments); } } 
}); 
// get() 
console.log(proxy.foo); // undefined 
console.log(proxy.bar); // undefined 
console.log(proxy.baz); // 3 
// has() 
console.log('foo' in proxy); // false 
console.log('bar' in proxy); // false 
console.log('baz' in proxy); // true
  1. 属性验证
const target = { onlyNumbersGoHere: 0 
}; 
const proxy = new Proxy(target, { set(target, property, value) { if (typeof value !== 'number') { return false; } else { return Reflect.set(...arguments); } } 
}); 
proxy.onlyNumbersGoHere = 1; 
console.log(proxy.onlyNumbersGoHere); // 1 
proxy.onlyNumbersGoHere = '2'; 
console.log(proxy.onlyNumbersGoHere); // 1
  1. 函数与构造函数参数验证
function median(...nums) { return nums.sort()[Math.floor(nums.length / 2)]; 
} 
const proxy = new Proxy(median, { apply(target, thisArg, argumentsList) { for (const arg of argumentsList) { if (typeof arg !== 'number') { throw 'Non-number argument provided'; } } return Reflect.apply(...arguments); } 
}); 
console.log(proxy(4, 7, 1)); // 4 
console.log(proxy(4, '7', 1)); 
// Error: Non-number argument provided
class User { constructor(id) { this.id_ = id; } 
} 
const proxy = new Proxy(User, { construct(target, argumentsList, newTarget) { if (argumentsList[0] === undefined) { throw 'User cannot be instantiated without id'; } else { return Reflect.construct(...arguments); } } 
}); 
new proxy(1); 
new proxy(); 
// Error: User cannot be instantiated without id
  1. 数据绑定与可观察对象
const userList = []; 
class User { constructor(name) { this.name_ = name; } 
} 
const proxy = new Proxy(User, { construct() { const newUser = Reflect.construct(...arguments); userList.push(newUser); return newUser; } 
}); 
new proxy('John'); 
new proxy('Jacob'); 
new proxy('Jingleheimerschmidt'); 
console.log(userList); // [User {}, User {}, User{}]

代理是 ECMAScript 6 新增的令人兴奋和动态十足的新特性。尽管不支持向后兼容,但它开辟出了一片前所未有的 JavaScript 元编程及抽象的新天地。
从宏观上看,代理是真实 JavaScript 对象的透明抽象层。代理可以定义包含捕获器的处理程序对象,而这些捕获器可以拦截绝大部分 JavaScript 的基本操作和方法。在这个捕获器处理程序中,可以修改任何基本操作的行为,当然前提是遵从捕获器不变式。与代理如影随形的反射 API,则封装了一整套与捕获器拦截的操作相对应的方法。可以把反射 API看作一套基本操作,这些操作是绝大部分 JavaScript 对象 API 的基础。
代理的应用场景是不可限量的。开发者使用它可以创建出各种编码模式,比如(但远远不限于)跟踪属性访问、隐藏属性、阻止修改或删除属性、函数参数验证、构造函数参数验证、数据绑定,以及可观察对象。

咏牡丹
宋·王溥
枣花至小能成实,桑叶虽柔解吐丝。
堪笑牡丹如斗大,不成一事又空枝。

这篇关于《每天十分钟》-红宝书第4版-代理与反射(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1064718

相关文章

大型网站架构演化(六)——使用反向代理和CDN加速网站响应

随着网站业务不断发展,用户规模越来越大,由于中国复杂的网络环境,不同地区的用户访问网站时,速度差别也极大。有研究表明,网站访问延迟和用户流失率正相关,网站访问越慢,用户越容易失去耐心而离开。为了提供更好的用户体验,留住用户,网站需要加速网站访问速度。      主要手段:使用CDN和反向代理。如图。     使用CDN和反向代理的目的都是尽早返回数据给用户,一方面加快用户访问速

Spring 内部类获取不到@Value配置值问题排查(附Spring代理方式)

目录 一、实例问题 1、现象 2、原因 3、解决 二、Spring的代理模式 1、静态代理(Static Proxy) 1)原理 2)优缺点 3)代码实现 2、JDK动态代理(JDK Dynamic Proxy) 1)原理 2)优缺点 3)代码实现 3、cglib 代理(Code Generation Library Proxy) 1)原理 2)优缺点 3)代码实

Java反射详细总结

什么是反射?         反射,指的是加载类的字节码到内存,并以编程的方法解刨出类中的各个成分(成员变量、方法、构造器等)。         反射获取的是类的信息,那么反射的第一步首先获取到类才行。由于Java的设计原则是万物皆对象,获取到的类其实也是以对象的形式体现的,叫字节码对象,用Class类来表示。获取到字节码对象之后,再通过字节码对象就可以获取到类的组成成分了,这些组成成分其实也

Vue2配置前端代理

在8080向5000请求数据 cli+vue2 一、cli内配置前端代理 1、使用 发送请求时写8080 在配置文件中配置 vue.config.js  2、缺点 无法配置多个代理无法控制某个请求知否要代理  二、方式二 module.exports = {devServer: {proxy: {'/api1':{ //匹配所有以'/api1'开头的请求路径targe

Java代理-动态字节码生成代理的5种方式

上篇讲到了代理模式出现的原因,实现方式以及跟其他相似设计模式的区别。传送门@_@ http://blog.csdn.net/wonking666/article/details/79497547 1.静态代理的不足 设计模式里面的代理模式,代理类是需要手动去写的。但是手写代理的问题颇多 1.如果不同类型的目标对象需要执行同样一套代理的逻辑,比如说在方法调用前后打印参数和结果,那么仍然需要为每

vue2和vue3数据代理的区别

前言: vue2 的双向数据绑定是利⽤ES5的⼀个 API ,Object.defineProperty( )对数据进行劫持结合发布订阅模式的方式来实现的。 vue3 中使⽤了 ES6的Proxy代理对象,通过 reactive() 函数给每⼀个对象都包⼀层Proxy,通过 Proxy监听属性的变化,从而实现对数据的代理操作。 一,Object.defineProperty( ) let

基于RAG的知识库AI代理机器人,问题思考

基于RAG的知识库AI代理机器人,问题思考 知识库内容分类 对于普通非qa问答格式的知识内容 在分段存储时,需要手动调整,保证每个分段的内容意思完整,不被分割,当然段落也不宜过长,保证内容表达的意思到不可分割为止就行 对于qa问答格式的知识内容 通常需要对问题增加格外索引,因为fastgpt的模式是将问题和回答,作为完整的文本作为向量化的坐标,当问题和回答的内容过长时,使用问题向量化匹配

Gradle代理设置

修改 ~/.gradle/gradle.properties 文件,添加代理配置。 对于http和https代理,添加如下内容: systemProp.http.proxyHost=<host>systemProp.http.proxyPort=<port># systemProp.http.proxyUser=<user># systemProp.http.proxyPassword=

深入理解go语言反射机制

1、前言        每当我们学习一个新的知识点时,一般来说,最关心两件事,一是该知识点的用法,另外就是使用场景。go反射机制作为go语言特性中一个比较高级的功能,我们也需要从上面两个方面去进行学习,前者告诉我们如何去使用,而后者告诉我们为什么以及什么时候使用。 2、反射机制 2.1 反射机制的基本概念         在 Go 语言中,反射机制允许程序在运行时获取对象的类型信息、访问对

注解+反射 参数校验更加简洁

背景 做RPC接口的时候 我们需要对一些字段做非空校验 在字段很多的情况下 如果一个一个的用if判断 代码会很恶心 所以我们需要有一种便捷的方式去实现这个功能 比如使用注解+反射的方式 怎么做? 首先定义注解 非空注解: package com.api.annotation;import java.lang.annotation.*;/*** 非空校验注解*/@Target({Elemen