查看原文
其他

Bean 的生命周期是什么?

编程导航 2024-01-21

The following article is from 面试鸭 Author 编程导航和鱼友们

大家好呀,今天继续给大家分享一些优质面试题,希望对正在找工作的小伙伴有所帮助。

后端

题目一

讲一讲 Spring 框架中 Bean 的生命周期?

官方解析

在 Spring 框架中,Bean 的生命周期包括以下几个阶段:

  1. 实例化(Instantiation):在这个阶段,Spring 将根据配置文件或注解等方式创建 Bean 实例,并将其存储在容器中。

  2. 属性赋值(Populate Properties):在这个阶段,Spring 将会自动将 Bean 的属性值从配置文件或注解等方式中注入到 Bean 实例中。

  3. 初始化(Initialization):在这个阶段,Spring 会调用 Bean 实例的 init-method 方法,完成一些初始化的操作,例如建立数据库连接等。

  4. 使用(In Use):在这个阶段,Bean 实例已经可以正常使用,供应用程序调用。

  5. 销毁(Destruction):在这个阶段,Spring 会调用 Bean 实例的 destroy-method 方法,完成一些资源的释放和清理操作,例如关闭数据库连接等。

具体的实现方式可以通过实现 BeanPostProcessor 和 BeanFactoryPostProcessor 接口来进行扩展。其中,BeanPostProcessor 接口定义了两个方法 postProcessBeforeInitialization 和 postProcessAfterInitialization,分别在 Bean 的初始化前后被调用,用于扩展 Bean 初始化的过程;BeanFactoryPostProcessor 接口则定义了一个方法 postProcessBeanFactory,用于在 Bean 工厂实例化 Bean 定义后对其进行修改。

总之,Spring 的 Bean 的生命周期通过上述阶段进行管理,开发者可以通过实现相关接口和方法来扩展和定制 Bean 的创建和销毁过程,以满足各种业务需求。

鱼友的精彩回答

猿二哈的回答

bean 的生命周期

  1. bean 的生命周期有五个阶段:

  2. 创建前准备:主要是扫描 spring 中的上下文和配置(解析和查找)bean 的一些扩展配置

  3. 创建实例:在这个阶段调用 bean 的构造器进行实例的创建。这是通过反射进行实例的创建的,并且会扫描和解析 bean 的一些属性

  4. 依赖注入:实例化的 bean 可能依赖其他的 bean,这个阶段就是注入实例的 bean 依赖的 bean,如用注解 @Autowired 进行依赖的注入

  5. 容器缓存:这个阶段中,将实例化的 bean 放入容器和 spring 的缓存中,此时开发者可以使用实例化的 bean

  6. 销毁实例:当 spring 的上下文关闭时,这些上下文的 bean 也会被销毁

关键词:创建前准备,创建实例,依赖注入,容器缓存,销毁实例,反射,扩展配置,上下文

Starry 的回答

(1)默认情况下,IOC容器中 Bean 的生命周期分为五个阶段:

  • 实例化 Bean:调用构造器或者是通过工厂的方式创建 Bean 对象。
  • 属性注入:当 Bean 实例化后,Spring会给 Bean 对象的属性注入值。
  • 初始化:调用初始化方法,进行初始化,初始化方法是通过 init-method 来指定的。
  • 使用 Bean
  • 销毁:IOC 容器关闭时,调用 Bean 的 destroy 方法,销毁 Bean 对象。

(2)当加入了 Bean 的后置处理器后,IOC 容器中 bean 的生命周期分为七个阶段:

  • 实例化 Bean:当 Spring 容器启动时,根据 Bean 的定义(配置文件或注解)创建 Bean 的实例。在这个阶段,Spring 会使用 Java 反射或 CGLIB等技术创建 Bean 的实例。
  • 属性注入:当 Bean 实例化后,Spring 会注入 Bean 的属性,包括基本类型、引用类型、集合类型等。属性注入可以通过构造函数注入、Setter 方法注入、注解注入等方式实现。
  • BeanPostProcessor 的前置处理:当属性注入完成后,Spring 会调用所有实现了 BeanPostProcessor 接口的类的postProcessBeforeInitialization 方法,可以在这个方法中对 Bean 进行一些自定义的初始化操作。
  • 初始化:在 BeanPostProcessor 的前置处理后,Spring 会调用 Bean 的 init 方法(可以是自定义的 init 方法或实现了 InitializingBean 接口的 afterPropertiesSet 方法),进行 Bean 的初始化工作。
  • BeanPostProcessor 的后置处理:当 Bean 的初始化完成后,Spring 会调用所有实现了 BeanPostProcessor 接口的类的postProcessAfterInitialization 方法,可以在这个方法中对 Bean 进行一些自定义的后续处理。
  • 使用 Bean

  • 销毁:当 Spring 容器关闭时,Spring 会调用 Bean 的 destroy 方法(可以是自定义的 destroy 方法或实现了 DisposableBean 接口的 destroy 方法),进行 Bean 的销毁工作。





Algorithm 的回答

一、容器启动阶段

1、BeanDefinitionReader 读取 Bean 的配置信息(如XML等),将读取到每个 Bean 的配置信息使用 BeanDefinition 表示,同时注册到相应的 BeanDefinitionRegistry 中

2、之后会有 BeanFactory 的后置处理器,即 实现 BeanFactoryPostProcessor 的类,自定义修改 BeanDefinition 中的信息(此时 Bean 已经注册好了,现在做的只是修改已经注册好的 Bean 的信息)

二、Bean 的实例化阶段

1、Bean 开始实例化, 通过反射或 CGLIB(没听过)完成

2、Bean 实例化完成后,触发回调检测该 Bean 实现了哪个 Aware 接口,如实现了 BeanNameAware 子接口的可以获取到 Bean 的名称并 setBeanName。即根据 BeanDefinition 中的信息,使用 Aware 接口的实现类获取并 set,然后填充 Bean 的属性和依赖

3、Bean 依赖注入完毕后,执行 BeanPostProcessor 后置处理器,该接口有两个方法 postProcessBeforeInitialization 和 postProcessAfterInitialization,分别是在 Bean 初始化前和初始化后执行。

4、Bean 初始化前,执行 Bean 后置处理器的 postProcessBeforeInitialization 用来完成指定 Bean 的定制初始化任务

5、Bean 开始初始化,postProcessBeforeInitialization 执行完毕后,开始执行 Bean 的初始化方法,此时如果实现了 InitializingBean 接口和 自定义了 init-method 方法,则在 Bean 初始化期间执行其方法

6、Bean 初始化结束,执行 Bean 后置处理器的 postProcessAfterInitialization,执行初始化之后的任务

7、Bean 开始使用

8、Bean 销毁,容器关闭时,上下文销毁,如果实现了 DisposableBean 接口,则执行对应的 destroy 方法,如果自定义了 destory-method,则执行对应的自定义销毁方法

Wbbp 的回答

Spring 单例 Bean 的生命周期

  1. 实例化(Instantiation):Spring 会根据配置信息或注解创建 Bean,通常调用构造函数(还有工厂方法)实现,实例化完成后将 Bean 放入工厂中进行管理
  2. 属性赋值(Population):Spring 为Bean 的属性进行赋值,包括依赖注入(Dependency Injection)、属性注入(Property Injection)、方法注入(Method Injection)等
  3. 初始化(Initialization):Spring 调用 Bean 的初始化方法进行初始化操作
  4. 使用(Using):Bean 正常使用

  5. 销毁(Destruction):Spring 调用 Bean 的销毁方法完成一些资源的释放和清理操作





生命周期中的扩展方法:

  • Awar e接口:让 Bean 获取容器的资源,例如 BeanNameAware 的 setBeanName(),BeanFactoryAware()的setBeanFactory()
  • BeanPostProcessor 处理器:进行初始化前置处理和后置的处理,对应 postProcessBeforeInitialization()和postProcessAfterInitialization()
  • 生命周期接口:定义初始化方法和销毁方法的,例如 InitializingBean 的 afterPropertiesSet(),以及 DisposableBean 的 destory()
  • 配置生命周期方法:通过配置文件自定义初始化和销毁方法,例如配置文件配置的 init()和 destoryMethod()

HeiHei 的回答

Bean 的生命周期之5步

  • 实例化 Bean(调用无参数构造方法)
  • Bean 属性赋值(调用 set 方法)
  • 初始化 Bean(调用 Bean 的 init 方法)
  • 使用 Bean

  • 销毁 Bean(调用 Bean 的 destroy 方法)


Bean 的生命周期之7步

在以上的 5 步中,第 3 步是初始化 Bean,如果想在初始化前和初始化后添加代码,可以加入“Bean 后处理器”。

Bean 的生命周期之10步

增加步骤:

  1. 在 Bean 后处理器 before 之前
  2. 在 Bean 后处理器 before 之后

  3. 在使用 Bean 之后,或者销毁之前





Aware相 关的接口包括:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware

  • 当 Bean 实现了 BeanNameAware,Spring 会将 Bean 的名字传递给 Bean。
  • 当 Bean 实现了 BeanClassLoaderAware,Spring 会将加载该Bean的类加载器传递给 Bean。
  • 当 Bean 实现了 BeanFactoryAware,Spring 会将 Bean工厂对象传递 给Bean。

会冒泡的可乐的回答

在 Spring 框架中,Bean 的生命周期包括以下几个阶段:

  1. Spring 对 Bean 进行实例化

  2. Spring 将值和 Bean 的引用注入进 Bean 对应的属性中

  3. 如果 Bean 实现了 BeanNameAware 接口,Spring 将 Bean 的 ID 传递给 setBeanName()方法

  4. 如果 Bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanDactory(BeanFactory bf)方法并把 BeanFactory 容器实例作为参数传入。

  5. 如果 Bean 实现了 ApplicationContextAwaer 接口,Spring 容器将调用 setApplicationContext(ApplicationContext ctx)方法,把 y 应用上下文作为参数传入.

  6. 如果 Bean 实现了 BeanPostProcess 接口,Spring 将调用它们的 postProcessBeforeInitialization(预初始化)方法,作用是在 Bean 实例创建成功后对进行增强处理,如对 Bean 进行修改,增加某个功能

  7. 如果 Bean 实现了 InitializingBean 接口,Spring 将调用它们的 afterPropertiesSet 方法.

  8. 如果 Bean 实现了 BeanPostProcess 接口,Spring 将调用它们的 postProcessAfterInitialization(后初始化)方法

  9. 经过以上的工作后,Bean 将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁

  10. 如果 Bean 实现了 DispostbleBean 接口,Spring 将调用它的 destory 方法,作用与在配置文件中对 Bean 使用 destory-method 属性的作用一样,都是在 Bean 实例销毁前执行的方法。

Spring 中 Bean 生命周期过程图如下:

题目二

Redis 有哪些数据类型?基础数据结构有几种?你还知道哪些 Redis 的高级数据结构?

官方解析

Redis 支持多种数据类型,不同的数据类型可以满足不同的需求。下面是 Redis 中常用的数据类型:

  1. String(字符串):Redis 中最基本的数据类型,可以存储任何形式的数据,例如整数、浮点数、二进制数据等。
  2. Hash(哈希):Redis 中的一种键值对类型,可以存储多个键值对,每个键值对又是一个键值对结构。
  3. List(列表):Redis 中的一个有序列表类型,可以存储多个元素,每个元素都有一个索引,支持多种列表操作,例如插入、删除、查找等。
  4. Set(集合):Redis 中的一种无序集合类型,可以存储多个元素,每个元素都是唯一的,支持多种集合操作,例如交集、并集、差集等。
  5. Sorted Set(有序集合):Redis 中的一种有序集合类型,可以存储多个元素,每个元素都有一个分值,支持根据分值进行排序和查询等操作。Redis 的基础数据结构有三种:字符串、列表和哈希,其他的数据类型都是基于这三种数据结构进行扩展和衍生的。例如,Redis 的 Set 数据类型就是基于字符串实现的。

除了基础数据结构之外,Redis 还提供了多种高级数据结构,例如:

  1. HyperLogLog:一种基数估计算法,用于估计一个数据集合的基数。
  2. GeoHash:一种地理位置编码算法,可以对地理位置信息进行编码和查询。
  3. Pub/Sub:一种消息队列机制,可以实现消息的订阅和发布。
  4. Bitmaps:一种位图数据结构,可以进行高效的位运算,用于统计用户在线时长、网站访问量等。
  5. Lua 脚本:Redis 中可以使用 Lua 脚本进行扩展和定制,可以实现一些复杂的业务逻辑和算法。

鱼友的精彩回答

爱吃鱼蛋的回答

数据类型/结构简介是否基础类型应用场景
String(字符串)最基本的数据类型,用于存储字符串、整数或浮点数。缓存数据,如页面缓存、对象缓存等;计数器,如网站访问量、用户在线时长等;分布式锁,如限流、秒杀等。
List(列表)一组有序的元素,可以在头部或尾部添加元素,适用于存储有序的元素集合。消息队列,如异步任务、日志收集等;队列、栈等数据结构的实现,如任务队列、聊天记录等。
Set(集合)一组无序的元素,可以进行交集、并集、差集等操作。对象存储,如用户信息、商品信息等;缓存数据,如页面缓存、对象缓存等。
Hash(哈希表)一种键值对的集合,适用于存储对象。去重、标签、好友列表等。
Sorted Set(有序集合)一组有序的元素,每个元素都有一个分值,可以按照分值排序和范围查询。排行榜,如热门商品、网站排名等;分数排序,如商品价格、评分等。
Bitmaps(位图)一种特殊的字符串,可以进行位运算,用于存储二进制数据。存储用户签到信息、在线状态等。
HyperLogLog(基数统计)一种基于概率算法的数据结构,用于统计元素的数量。统计网站的 UV、PV 等指标。
Geo(地理位置)一种存储地理位置信息的数据结构,可以进行地理位置相关的操作。存储商家、用户的地理位置信息实现附近商家推荐、地理位置搜索等。
Streams(流)一种类似于消息队列的数据结构,可以进行发布和订阅操作。实现消息队列、事件流等。
Lua scripting(Lua 脚本)可以通过 Lua 脚本扩展 Redis 的功能。分布式锁,如限流、秒杀、限流、自动补全等。

题目三

有哪些主流的消息队列,它们分别有什么优缺点、各自的适用场景是什么?

官方解析

以下是几种主流的消息队列:

  1. RabbitMQ 优点:可靠性高、性能优秀、支持多种协议、有完善的管理界面。缺点:部署和维护较为复杂。适用场景:适用于高可靠性、高吞吐量、多协议、多语言的分布式系统场景。
  2. Kafka 优点:性能优秀、可扩展性好、可靠性高、支持多种数据处理模式。缺点:管理界面不够完善、复杂度较高。适用场景:适用于高吞吐量、高并发、数据处理流程复杂的场景,例如大数据处理、实时日志处理等。
  3. ActiveMQ 优点:功能齐全、易于使用、支持多种协议。缺点:性能相对较低、可靠性不如 RabbitMQ。适用场景:适用于需要使用多种协议、支持多种消息类型的场景,例如 Web 服务、企业应用集成等。
  4. RocketMQ 优点:性能优秀、可靠性高、支持海量数据存储和传输。缺点:社区相对较小、功能不够完善。适用场景:适用于海量数据存储和传输场景,例如电商、金融等领域。
  5. Redis 优点:速度极快、支持多种数据结构、支持事务操作、支持发布/订阅模式。缺点:可靠性不如 RabbitMQ 和 Kafka。适用场景:适用于对性能要求极高、需要使用多种数据结构和事务操作的场景,例如缓存、计数器、实时消息等。

总的来说,选择适合自己的消息队列需要根据具体业务需求和场景进行综合评估和选择。

鱼友的精彩回答

苏打饼干的回答

猫十二懿的回答

主流的消息队列如下

飞扬的回答

消息队列是在消息的传输过程中保存消息的容器,简单点理解就是传递消息的队列,具备先进先出的特点,一般用于异步、解耦、流量削锋等问题,实现高性能、高可用、高扩展的架构。

常见的消息队列使用场景有 6 个:应用解耦,异步处理,流量削锋,日志处理,消息通讯,消息广播.

主流的消息队列有 ActiveMQ,RabbitMQ、RocketMQ、Kafka。

前端

题目一

有哪些 CSS 性能优化的操作或技巧?

官方解析

以下是一些常用的 CSS 性能优化操作和技巧:

  1. 使用合适的选择器:选择器的复杂度会影响 CSS 渲染性能。尽量使用简单的选择器,避免使用通配符和后代选择器等复杂的选择器。
  2. 避免使用 @import:@import 可以在 CSS 文件中导入其他 CSS 文件,但它会阻塞页面的渲染,影响性能。建议使用 link 标签来引入 CSS 文件。
  3. 避免使用: 会影响 CSS 属性的优先级,而且会增加解析和渲染时间。建议尽量避免使用。
  4. 避免使用 inline 样式:inline 样式的优先级最高,但它会增加 HTML 文件的大小,降低页面的加载速度。建议使用外部 CSS 文件和内部样式表。
  5. 压缩和合并 CSS 文件:压缩和合并 CSS 文件可以减小文件大小,提高页面加载速度。可以使用工具例如 CSSMin 和 YUI Compressor 等来进行压缩和合并操作。6.** 使用 CSS Sprites**:CSS Sprites 可以将多个小图片合并成一张大图片,并使用 CSS 来显示不同的部分,减少 HTTP 请求次数,提高页面加载速度。
  6. 避免过度继承:过度继承会导致样式的冗余和继承链的深度增加,影响 CSS 的解析和渲染性能。
  7. 避免使用高消耗的属性和值:某些属性和值的计算成本比较高,例如 box-shadow、border-radius 等,应该尽量避免使用。

以上是一些常用的 CSS 性能优化操作和技巧,可以帮助提高页面的加载速度和渲染性能。

鱼友的精彩回答

codexgh 的回答

分享一些 CSS 性能优化小技巧:

预加载样式表:

该  标记允许您在引用 CSS 之前启动下载,当样式表引用出现在其他资源之后,或者你已经嵌套了@导入指令:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>

<!-- preload CSS file -->
<link rel="preload" href="styles.css" as="style" />

<!-- more code -->

<!-- use preloaded styles -->
<link rel="stylesheet" href="styles.css" />
删除未使用的样式和文件:

删除所有不使用的样式表。你可能能够识别不再使用的页面、小部件或框架代码。如果您将样式表拆分为具有明确责任级别和适当文档的单独文件,则此任务会更容易。以下工具可以通过分析 HTML 和 CSS 来识别冗余代码:● 清除CSS:https://purgecss.com/ ● 未使用CSS:https://unused-css.com/ 单独的 HTML 分析通常是不够的,但是您可以配置白名单样式,比如那些由 JavaScript 激活的样式。

使用HTML<链接>而不是CSS@导入

@import at-rule允许你在css中加载样式表:

/* main.css */
@import url("reset.css");
@import url("base.css");
@import url("grid.css");

这允许将样式表拆分为更小,更易于管理样式表,但是每个 @import 都会阻塞渲染,浏览器必须依次下载和解析每个文件。使用 HTML link 标签效率更高,因为每个样式表都是并行加载的。

<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="grid.css">

简化选择器:现代浏览器解析长选择器没有问题,但是降低复杂性将减小文件大小,提高性能,并使代码更易于维护。我们还应该考虑新的:is、:where 和:has 选择器,它们可以像这样转换 CSS 选择器:

article section.primary:not(:first-child) h1,
article section.primary:not(:first-child) h2,
article section.primary:not(:first-child) p,
article section.secondary:not(:first-child) h1,
article section.secondary:not(:first-child) h2,
article section.secondary:not(:first-child) p {
  color: red;
}

/* 变成一句简洁的话: */

article section:not(:first-child):where(.primary, .secondary) :where(h1, h2, p) {
  color: red;
}

题目二

JS 在什么情况下会存在数字精度丢失的问题,如何解决?

官方解析

在 JavaScript 中,数字类型是采用 IEEE 754 标准的双精度浮点数表示的,由于双精度浮点数只有 52 位精度,因此在进行精度较高的数值计算时,可能会出现数字精度丢失的问题。例如:

console.log(0.1 + 0.2); // 输出结果为 0.30000000000000004

可以看到,实际上 0.1 + 0.2 的结果并不等于 0.3,这是因为双精度浮点数的精度限制导致的。在处理货币等需要高精度计算的场景时,这种精度丢失的问题可能会导致计算错误。

为了解决这个问题,可以采用以下几种方法:

使用整数进行计算:可以将数字转换成整数进行计算,例如将货币的单位从元转换成分进行计算,避免小数计算带来的精度问题。

使用第三方库:例如 decimal.js 和 big.js 等第三方库,可以提供更高精度的数字计算功能,避免精度丢失的问题。

使用 toFixed 方法:可以使用 JavaScript 中的 toFixed 方法将数字转换成指定位数的字符串,例如将 0.1 和 0.2 转换成一位小数的字符串再进行相加,避免精度丢失的问题。

console.log((0.1 + 0.2).toFixed(1)); // 输出结果为 "0.3"

总之,在进行精度较高的数值计算时,需要注意 JavaScript 中存在的数字精度丢失问题,并根据具体情况选择合适的解决方案。

题目三

ES6 中的 Reflect 对象有什么用?

官方解析

在 ES6 中,Reflect 对象是一个内置的对象,提供了一组用于操作对象的方法。它的作用是对对象的某些操作进行拦截和修改,从而提供更加灵活和强大的对象操作方式。

以下是一些 Reflect 对象的常用方法和用途:

  1. Reflect.get(target, property, receiver):读取对象的属性值。与对象的点运算符和方括号运算符相比,这个方法提供了更加灵活和可控的属性读取方式。
  2. Reflect.set(target, property, value, receiver):设置对象的属性值。与对象的点运算符和方括号运算符相比,这个方法提供了更加灵活和可控的属性设置方式。
  3. Reflect.has(target, property):检查对象是否具有指定的属性。与 in 运算符相比,这个方法提供了更加灵活和可控的属性检查方式。
  4. Reflect.deleteProperty(target, property):删除对象的属性。与 delete 运算符相比,这个方法提供了更加灵活和可控的属性删除方式。
  5. Reflect.construct(target, argumentsList[, newTarget]):使用给定的参数列表创建对象。与 new 运算符相比,这个方法提供了更加灵活和可控的对象创建方式。
  6. Reflect.apply(target, thisArgument, argumentsList):调用对象的方法。与函数调用运算符相比,这个方法提供了更加灵活和可控的方法调用方式。

除了上述方法之外,Reflect 对象还提供了许多其他的方法,例如 Reflect.defineProperty、Reflect.getOwnPropertyDescriptor、Reflect.getPrototypeOf、Reflect.setPrototypeOf 等,都可以用于对对象进行拦截和修改。

总之,Reflect 对象提供了一组灵活和强大的对象操作方法,可以用于实现代理、拦截、元编程等功能,使 JavaScript 更加强大和灵活。

鱼友的精彩回答

luckythus的回答

ES6 中的 Reflect 对象有什么用?

Reflect 对象是一个内置对象,它提供了一组用于操作对象的方法

  1. 简化了操作对象:Reflect方 法是函数式的,与 Object 方法是命令式的不同
const person = { name: 'Tom' }
const objName = Object.getOwnPropertyDescriptor(person,'name').value
const reflectName = Reflect.get(person,'name')
console.log(objName) // 'Tom'
console.log(reflectName) // 'Tom'
  1. Reflect 方法的返回值更加明确,定义属性成功后直接返回布尔值来表示是否设置成功,而 Object.defineProperty()会返回 obj
const person = { name: 'Tom'}
const result1 = Object.defineProperty(person,'gender',{
    value: 'man',
    writable: false,
    enumerable: true,
    configurable: true
})
const result2 = Reflect.defineProperty(person,'address',{
    value: 'Beijing',
    writable: false,
    enumerable: true,
    configurable: true
})
console.log(result1) // 返回对象 {name: 'Tom',gender: 'man'}
console.log(result2) // 返回布尔值 true 表示操作成功
  1. 统一了对象操作的 API:通过 Reflect 方法可以操作对象的属性,与 Object方 法基本类似

  2. 与 Proxy 对象进行配合使用:Reflect 对象和 Proxy 对象配合使用,可以实现拦截对象操作,例如使用 Reflect.get()方法获取被代理对象的属性,在获取属性值之前可以先进行一些拦截操作,从而实现拦截对象操作的效果

const obj = { name: 'Tom' }
const proxy = new Proxy(obj,{
    get(target,key,receiver) {
        console.log(`获取属性${key}`)
        return Reflect.get(target,key,receiver)
    }
})
console.log(proxy.name)
  1. Reflect 内置对象里面还有许许多多的方法 API,Reflect 对象提供了许多灵活和强大的对象操作方法

luckythus的回答

Reflect 可以用于获取目标对象的行为,他与 Objectl 类似,但是更易读,为操作对象提供了一种更优雅的方式,它的方法与 Proxy 是对应的。

代替Object的某些方法
const obj = {}
Reflect.defineProperty(obj, "name", {
  value:"codexgh",
  writable: false
})
修改某些Object方法的返回结果
// 老写法
try {
  Object.defineProperty(target, property, attributes);
}catch(e) {
  // fail
}

// 新写法
if(Reflect.defineProperty(target, property, attributes)){
  // success
else {
  // fail
}
命令式变为函数行为
const obj1  = {
  name: "codexgh",
}
// 老写法
console.log("name" in obj1); // true
// 新写法
console.log(Reflect.has(obj1, "name")); // true

// 老写法
delete obj1.name
// 新写法
Reflect.deleteProperty(obj1, "name")
配合Proxy使用:
const set = new Set();
let proxy = new Proxy(set, {
  get(target, key){
    // 判断如果是方法,修正this指向
    let value = Reflect.get(target, key);
    if(value instanceof Function){
      return value.bind(target)
    }
    return value
  },
  set(){
    Reflect.set(...arguments)
  }
});

星球活动

1.欢迎参与 30 天面试题挑战活动 ,搞定高频面试题,斩杀面试官!

2.欢迎已加入星球的同学 免费申请一年编程导航网站会员 !

3.欢迎学习 鱼皮最新原创项目教程,手把手教你做出项目、写出高分简历!

加入我们

欢迎加入鱼皮的编程导航知识星球,鱼皮会 1 对 1 回答您的问题、直播带你做出项目、为你定制学习计划和求职指导,还能获取海量编程学习资源,和上万名学编程的同学共享知识、交流进步。

💎 加入星球后,您可以:

1)添加鱼皮本人微信,向他 1 对 1 提问,帮您解决问题、告别迷茫!点击了解详情

2)获取海量编程知识和资源,包括:3000+ 鱼皮的编程答疑和求职指导、原创编程学习路线、几十万字的编程学习知识库、几十 T 编程学习资源、500+ 精华帖等!点击了解详情

3)找鱼皮咨询求职建议和优化简历,次数不限!点击了解详情

4)鱼皮直播从 0 到 1 带大家做出项目,已有 50+ 直播、完结 3 套项目、10+ 项目分享,帮您掌握独立开发项目的能力、丰富简历!点击了解详情

外面一套项目课就上千元了,而星球内所有项目都有指导答疑,轻松解决问题

星球提供的所有服务,都是为了帮您更好地学编程、找到理想的工作。诚挚地欢迎您的加入,这可能是最好的学习机会,也是最值得的一笔投资!

长按扫码领优惠券加入,也可以添加微信 yupi1085 咨询星球(备注“想加星球”):



往期推荐

编程导航,火了!

RPC是什么?

什么是零拷贝?

Redis 有哪些内存淘汰策略?

TCP 和 UDP 协议有什么区别?

什么是单例模式?



继续滑动看下一个

Bean 的生命周期是什么?

向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存