React相关知识总结

前端面试题

React面试题

1.props和state相同点和不同点?render方法在哪些情况下会执行?
2.shouldComponentUpdate有什么作用?
3.说说React中的虚拟dom?在虚拟dom计算的时候diff和key之间有什么关系?
4.react新出来两个钩子函数是什么?和删掉的will系列有什么区别?
5.React的props.children使用map函数来遍历会收到异常显示,为什么?应该如何遍历?
6.React组件之间如何通信?
7.谈谈你对immutable.js的理解?
8.redux本来是同步的,为什么它能执行异步代码?实现原理是什么?中间件的 实现原理是什么?
9.redux中同步action与异步action最大的区别是什么?

接上述面试题

10.redux-saga和redux-thunk的区别与使用场景?

Redux-Saga和Redux-Thunk是两个常用的Redux中间件,用于处理异步操作,但它们有不同的工作原理和使用场景。

  1. Redux-Thunk:
    • 工作原理:Redux-Thunk允许在Redux中的action中返回一个函数,这个函数可以接收dispatch和getState作为参数,并且可以执行异步操作。当这个函数被dispatch时,Redux-Thunk会拦截这个函数,并且将dispatch和getState作为参数传递给它,从而使得异步操作可以在函数内部执行。
    • 使用场景:Redux-Thunk适合处理简单的异步操作,如发送网络请求、获取数据等。它的使用相对简单,不需要额外的配置,适合入门级别的应用。
  2. Redux-Saga:
    • 工作原理:Redux-Saga使用了ES6的生成器(generator)来处理异步操作。通过创建一个Saga,可以编写一系列的异步操作,其中包括监听action、执行异步任务、发起新的action等。Saga通过监听特定的action来触发异步操作,并且可以使用一些特定的effect来控制异步流程,如延迟、并行、取消等。
    • 使用场景:Redux-Saga适合处理复杂的异步流程或有复杂副作用逻辑的场景。它提供了更强大和灵活的工具来管理异步操作,可以处理并发请求、取消请求、轮询等。Redux-Saga适合大型、复杂的应用,提供了更好的可测试性和可维护性。
      总结来说,Redux-Thunk适用于简单的异步操作,使用简单,适合入门级别的应用;Redux-Saga适用于处理复杂的异步流程和有复杂副作用逻辑的应用,提供了更强大和灵活的工具。选择使用哪个中间件取决于应用的需求和复杂度。

11.在使用redux过程中,如何防止定义的action-type的常量重复?

在Redux中,为了防止定义的action-type常量重复,可以采取以下几种方法:

  1. 使用命名空间(Namespace):在定义action-type常量时,可以为每个模块或组件添加一个命名空间前缀,以确保唯一性。例如,可以使用moduleName/ACTION_TYPE的格式来定义action-type常量,其中moduleName是模块或组件的名称。
  2. 使用统一的命名规范:在整个应用中使用统一的命名规范来定义action-type常量,以确保不同模块或组件之间的唯一性。例如,可以使用全大写字母和下划线的方式定义常量,如MODULE_ACTION_TYPECOMPONENT_ACTION_TYPE
  3. 使用工具库:可以使用第三方的工具库来帮助管理和生成唯一的action-type常量。例如,可以使用redux-actions库提供的createActioncreateActions来自动生成常量,避免手动定义和管理。
  4. 使用枚举类型:可以使用ES6的枚举类型来定义action-type常量。枚举类型具有唯一性,可以有效地防止重复定义。例如,可以使用enum关键字来定义一个枚举类型,并将action-type常量作为枚举的属性。
    通过以上方法,可以有效地避免定义的action-type常量重复,提高代码的可维护性和可读性。同时,建议在项目中建立一个统一的管理文件(如constants.js),用于集中管理所有的action-type常量,方便查找和维护。

12.CDN的特点及意义?

CDN(Content Delivery Network)是一种分布式的服务器网络,通过将内容缓存到离用户更近的边缘节点上,实现更快的内容传输和更好的用户体验。CDN具有以下特点和意义:

  1. 提高网站性能:CDN通过将内容缓存到离用户更近的边缘节点上,可以大大减少内容传输的延迟,提高网站的加载速度和响应时间。用户可以更快地获取所需的内容,提供更好的用户体验。
  2. 减轻源服务器负载:CDN将内容分发到多个边缘节点上,可以将用户的请求分散到不同的节点上,从而减轻源服务器的负载压力。源服务器只需处理少量的请求,可以更好地应对高并发和大流量的情况。
  3. 提高可靠性和可用性:CDN的分布式架构可以提高系统的可靠性和可用性。当某个边缘节点发生故障或网络中断时,其他节点仍然可以提供内容,保证用户的访问不受影响。
  4. 节约带宽费用:CDN可以通过缓存静态内容,减少源服务器的带宽消耗,从而节约带宽费用。CDN的边缘节点之间可以通过就近交换数据,减少跨网络传输的成本。
  5. 支持全球分发:CDN的边缘节点分布在全球各地,可以实现全球范围的内容分发,提供更快的响应速度和更好的用户体验。尤其对于全球化的网站或应用程序,CDN是必不可少的。
    总之,CDN通过分布式架构、内容缓存和就近传输等技术,提高了网站性能、减轻服务器负载、提高可靠性和可用性,节约带宽费用,并支持全球分发。在当今的互联网环境中,CDN已经成为许多网站和应用程序优化性能的重要工具之一。

13.为什么for循环比forEach性能高?

在某些情况下,使用普通的for循环可能比使用forEach循环性能更高。这是由于for循环的底层实现方式和forEach循环的特性导致的。

  1. 循环机制:for循环是一种原始的循环机制,通过指定循环起始条件、循环终止条件和循环递增/递减表达式来控制循环的执行。这种机制直接操作索引,可以更高效地控制循环的流程。
  2. 代码执行:for循环的代码块是在同一个作用域中执行的,对循环变量的访问和操作更加直接。而forEach循环是通过回调函数来执行的,每次循环都会创建一个新的函数作用域,对于大规模的循环操作,这可能会导致一定的性能开销。
  3. 语法糖:forEach循环是一个高阶函数,使用了函数式编程的思想,提供了更简洁、可读性更高的语法。但它的底层实现可能包含了额外的函数调用和上下文切换,对于大规模的循环来说,这些额外的开销可能会影响性能。
    需要注意的是,这种性能差异可能在循环次数较大或循环体内部逻辑较复杂的情况下才会显著。对于简单的循环操作,两者的性能差异可能并不明显。在实际开发中,选择使用哪种循环方式应该考虑到代码的可读性、维护性和性能需求的平衡。

14.说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?

@reduxjs/toolkit是一个官方提供的Redux工具包,旨在简化和加速Redux应用程序的开发过程。它提供了一些常用的Redux库和模式的封装,包括创建Redux store、定义Redux reducer、处理异步操作等,以及提供了一些实用的工具函数和约定,帮助开发者更快地编写和组织Redux代码。
@reduxjs/toolkit的主要特点和功能包括:

  1. 简化的Redux配置:通过createSlice函数,可以更简洁地定义Redux reducer和actions,无需手动编写大量的模板代码。
  2. 集成的不可变性:@reduxjs/toolkit内置了Immer库,可以直接在reducer中进行可变的写操作,而不需要手动编写不可变性逻辑。
  3. 内置的异步处理:@reduxjs/toolkit集成了Redux Thunk和Redux Saga,提供了处理异步操作的方案,使得处理异步逻辑更加方便。
  4. DevTools集成:@reduxjs/toolkit自动为你配置了Redux DevTools,可以方便地进行调试和监控Redux应用程序的状态变化。
    相比之下,react-redux是一个用于将Redux与React应用程序集成的库。它提供了一些用于连接Redux store和React组件的API,包括Provider组件和connect函数。react-redux主要用于管理Redux的状态和操作,并将其传递给React组件进行渲染和交互。
    区别在于,@reduxjs/toolkit是一个专注于简化和加速Redux开发的工具包,提供了一些Redux的常用功能和模式的封装。而react-redux则是一个用于Redux和React集成的库,提供了一些与React组件交互的API。
    在实际开发中,通常会同时使用@reduxjs/toolkit和react-redux来提高开发效率。@reduxjs/toolkit用于简化Redux的配置和操作,而react-redux用于将Redux状态和操作传递给React组件,并处理React组件与Redux之间的交互。

15.React render方法的原理,在什么时候会触发?

React的render方法是React组件的一个重要方法,用于将组件渲染到DOM中。render方法的原理如下:

  1. 虚拟DOM:React使用虚拟DOM(Virtual DOM)来表示组件的UI结构。虚拟DOM是一个轻量级的JavaScript对象树,与实际的DOM结构相对应。
  2. 初始渲染:当组件首次被挂载到DOM中时,React会调用组件的render方法,生成组件的虚拟DOM树。
  3. 虚拟DOM比对:React将新生成的虚拟DOM树与之前的虚拟DOM树进行比对,找出两者之间的差异。
  4. DOM更新:根据差异,React通过最小化的操作来更新实际的DOM结构,使其与新的虚拟DOM树保持一致。
  5. 渲染完成:在DOM更新完成后,React会触发一系列生命周期方法,以及可能的副作用(如componentDidMount)。
    render方法会在以下情况下被触发:
  6. 组件首次挂载:当组件首次被挂载到DOM中时,会调用组件的render方法进行初始渲染。
  7. 组件状态或属性变化:当组件的状态(state)或属性(props)发生变化时,React会重新调用组件的render方法,生成新的虚拟DOM树,并进行比对和更新。
  8. 父组件重新渲染:当组件的父组件重新渲染时,会导致子组件也重新渲染,即重新调用render方法。
    需要注意的是,虽然render方法在组件的生命周期中可能会多次被调用,但React会通过虚拟DOM的比对算法,尽量减少实际的DOM操作,以提高性能。因此,即使render方法被调用多次,实际的DOM更新可能是有限的。

16.![] == ![],![] == [],结果是什么?为什么?

结果是false,true。
1、根据运算符优先级 ,! 的优先级是大于 == 的,所以先会执行 ![]

!可将变量转换成boolean类型,null、undefined、NaN以及空字符串(‘’)取反都为true,其余都为false。

所以 ! [] 运算后的结果就是 false

也就是 [] == ! [] 相当于 [] == false

2、根据上面提到的规则(如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1),则需要把 false 转成 0

也就是 [] == ! [] 相当于 [] == false 相当于 [] == 0

3、根据上面提到的规则(如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较,如果对象没有valueOf()方法,则调用 toString())

而对于空数组,[].toString() -> ‘’ (返回的是空字符串)

也就是 [] == 0 相当于 ‘’ == 0

4、根据上面提到的规则(如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值)

Number(‘’) -> 返回的是 0

相当于 0 == 0 自然就返回 true了

17.什么是闭包,应用场景是什么?

闭包是指在一个函数内部定义的函数,并且该内部函数可以访问外部函数的变量和参数,即使外部函数已经执行完毕,这些变量和参数依然可以被内部函数访问和使用。闭包可以理解为函数和其相关的引用环境的组合。
闭包的应用场景包括:

  1. 封装私有变量:通过闭包可以创建私有变量,将一些变量隐藏在函数作用域内,外部无法直接访问和修改。这种封装可以提高代码的安全性和可维护性,常见于模块化开发。
  2. 延迟执行:通过闭包可以实现延迟执行函数,将函数的执行逻辑封装在闭包内部,并在需要的时候调用内部函数来执行。这种延迟执行的方式可以控制函数的执行时机,常见于事件处理、定时器等场景。
  3. 创建特定环境:闭包可以创建一个特定的执行环境,将函数和其相关的上下文保存在内存中,供后续使用。这种特定环境的创建可以用于解决一些作用域和变量访问的问题,常见于回调函数、异步操作等场景。
  4. 实现函数柯里化:闭包可以实现函数柯里化(Currying),即将一个接受多个参数的函数转换为一系列接受一个参数的函数。通过闭包,可以将函数的部分参数保存在闭包中,返回一个新的函数,后续调用这个新函数时,可以继续传入剩余的参数。
    需要注意的是,闭包会占用内存,并且在不恰当的使用情况下,可能会导致内存泄漏。因此,在使用闭包时,需要注意合理管理内存,避免不必要的资源消耗。

18.谈谈你是如何做移动端适配的?

在移动端适配中,以下是一些常见的做法和技术:

  1. 使用响应式布局:使用CSS媒体查询和弹性布局等技术,根据不同的屏幕尺寸和设备类型,调整页面的布局和样式。可以通过设置不同的CSS规则,使得页面在不同的设备上呈现合适的布局。
  2. 使用Viewport标签:通过设置Viewport标签的属性,控制页面在移动设备上的显示效果。常见的设置包括设置视口的宽度、缩放比例、禁止缩放等。
  3. 使用百分比和弹性单位:在设计和开发中,尽量使用百分比和弹性单位(如em、rem)来指定元素的尺寸和间距,以适应不同屏幕尺寸和设备像素密度的变化。
  4. 使用媒体查询:根据不同的屏幕尺寸和设备特性,使用CSS媒体查询来针对性地调整页面的样式和布局。可以针对不同的屏幕尺寸、设备方向、像素密度等设置不同的CSS规则。
  5. 使用flex布局:使用CSS的flex布局可以方便地实现弹性和自适应的布局效果,适应不同屏幕尺寸和设备方向的变化。
  6. 使用CSS预处理器:使用CSS预处理器(如Sass、Less)可以简化样式的管理和编写,提供变量、函数、嵌套等特性,方便进行移动端适配的样式编写。
  7. 使用移动端框架:使用流行的移动端框架(如Bootstrap、Ant Design Mobile)可以快速搭建适应移动端的页面和组件,减少开发工作量。
    在实际开发中,根据具体需求和项目情况,可以选择适合的适配方案和技术,并结合测试和调试,确保页面在不同设备上的良好显示效果和用户体验。

19.移动端1像素的解决方案?

在移动端开发中,由于不同设备的像素密度不同,1像素的线条在某些设备上可能会显示模糊或变粗。为了解决这个问题,常见的解决方案有以下几种:

  1. 使用border-image或background-image:通过使用border-image或background-image来实现1像素线条的效果。可以使用一张高度为1像素的图片作为线条的背景,或者使用CSS的linear-gradient渐变实现线条效果。
  2. 使用transform: scale():通过CSS的transform属性的scale()方法,将1像素的线条进行缩放,以适应不同设备的像素密度。例如,可以将一个高度为1像素的div元素缩放至0.5倍或0.3333倍,来实现细线条的效果。
  3. 使用伪元素和伪类:通过在元素的before或after伪元素上设置1像素的线条样式,来实现细线条的效果。可以利用CSS的content属性来插入一个空内容,并设置其宽度为1像素,高度为所需的线条宽度。
  4. 使用viewport单位:使用vw单位(相对于视口宽度的百分比)来设置线条的宽度。通过将1像素的线条宽度设置为0.01vw,可以在不同设备上实现近似1像素的线条效果。
  5. 使用CSS预处理器的mixin或函数:在使用CSS预处理器(如Sass、Less)时,可以编写一些mixin或函数来生成特定设备像素密度下的1像素线条样式。根据设备的像素比例,动态计算生成线条的宽度和颜色。
    需要注意的是,以上解决方案中的一些技术可能需要根据具体的项目需求和设备适配情况进行调整和优化。在实际使用时,可以结合测试和调试,确保1像素线条在各种设备上都能正常显示。

20.弹性盒中的缩放机制是怎样的?

在弹性盒(Flexbox)布局中,缩放(flex shrink)是指当容器空间不足时,如何分配弹性项目的缩小空间。缩放机制是通过flex-shrink属性来控制的。
具体来说,缩放机制遵循以下原则:

  1. 默认值:flex-shrink的默认值为1,表示项目可以缩小。如果所有项目的flex-shrink属性都为1,则它们将等比例缩小,以适应父容器的空间。
  2. 值的比例关系:如果某个项目的flex-shrink属性为2,而其他项目的flex-shrink属性都为1,则前者在缩小空间时会比其他项目缩小得更多(2:1的比例)。
  3. 限制缩小:可以使用flex-shrink属性的值为0来禁止某个项目缩小。这样的项目将保持其原始大小,而其他项目将按比例缩小。
  4. 负值处理:如果某个项目的flex-shrink属性设置为负值,该项目将被视为比其他项目更不容易缩小。负值的绝对值越大,它的缩小空间会越小。
    需要注意的是,flex-shrink属性只在容器空间不足时才会生效。当容器空间足够容纳所有项目时,项目不会被缩小。此外,flex-shrink属性的具体值并不代表具体的缩小比例,而是相对于其他项目的比例关系。
    通过合理设置flex-shrink属性,可以调整弹性项目在缩小空间时的优先级和比例,以实现灵活的布局效果。

21.说说你对Object.defineProperty()的理解?

Object.defineProperty()是JavaScript中的一个方法,用于在一个对象上定义一个新的属性或修改已存在的属性的特性。
它的语法如下:

Object.defineProperty(obj, propName, propDesc)
  • obj:要定义属性的对象。
  • propName:要定义或修改的属性的名称。
  • propDesc:一个描述符对象,用于定义或修改属性的特性。
    描述符对象(propDesc)包含以下属性:
  • value:属性的值。
  • writable:属性的值是否可写,默认为false。
  • enumerable:属性是否可枚举,默认为false。
  • configurable:属性是否可配置,默认为false。
    使用Object.defineProperty()方法,可以实现以下操作:
  • 定义一个新的属性,并指定其值和特性。
  • 修改已存在的属性的特性,例如将一个属性从可写改为只读,或从不可配置改为可配置。
  • 控制属性的可枚举性,决定是否会出现在对象的遍历中。
    需要注意的是,如果属性已经存在于对象上,并且是可配置的,那么Object.defineProperty()将会修改该属性的特性。如果属性已经存在且不可配置,或者只有getters/setters而没有value属性,那么将会抛出一个错误。
    Object.defineProperty()是用于实现高级对象操作的重要方法,它可以让我们更精细地控制对象的属性特性,进而实现对象的数据封装、属性访问控制等功能。

22.ts中抽象类的理解?

在TypeScript中,抽象类是一种用于作为其他类的基类的特殊类。抽象类本身不能被实例化,只能用作其他类的基类。
通过使用抽象类,我们可以定义一些通用的属性和方法,然后让其他具体的子类继承这些属性和方法,并在子类中实现自己的具体逻辑。
抽象类通过关键字abstract来进行定义,可以包含抽象方法和非抽象方法。抽象方法是一种没有具体实现的方法,只有方法的签名,而非抽象方法则有具体的实现。
抽象类的主要特点包括:

  • 无法被实例化:抽象类不能被直接实例化,只能被用作其他类的基类。
  • 可以包含抽象方法:抽象类可以包含抽象方法,这些方法在抽象类中只有方法声明,没有具体实现。子类必须实现这些抽象方法。
  • 可以包含非抽象方法:抽象类可以包含非抽象方法,这些方法有具体的实现,子类可以直接继承并使用这些方法。
  • 子类继承并实现抽象方法:子类继承抽象类后,必须实现抽象类中的所有抽象方法,否则子类也必须定义为抽象类。
    抽象类的使用场景一般是在需要定义一个共享通用行为的基类,但是这个基类本身并不需要被实例化的情况下。通过抽象类,可以将共用的属性和方法定义在基类中,提高代码的复用性,并且在子类中实现具体逻辑,达到代码的扩展性和灵活性。

23.redux实现原理?

Redux是一个用于JavaScript应用程序的状态管理库。它的核心思想是将应用程序的状态存储在一个可预测的单一数据源中,并使用纯函数来处理状态的变化。
Redux的实现原理可以概括为以下几个关键概念和步骤:

  1. Store:Redux应用程序的状态存储在一个单一的JavaScript对象中,称为Store。Store是通过调用Redux提供的createStore函数来创建的,它包含了应用程序的状态以及一些用于管理状态变化的方法。
  2. Action:Action是一个普通的JavaScript对象,用于描述状态的变化。它必须包含一个type字段,用于指定要执行的操作的类型,并可以包含其他自定义的数据字段。通过调用Redux提供的dispatch方法,我们可以将Action传递给Store,触发状态的变化。
  3. Reducer:Reducer是一个纯函数,用于根据Action的类型来处理状态的变化。它接收当前的状态和要执行的Action作为参数,并返回一个新的状态。Reducer不会直接修改原始的状态对象,而是创建一个新的状态对象。Redux通过调用Reducer来更新应用程序的状态。
  4. Subscribe:通过调用Redux提供的subscribe方法,我们可以注册一个监听函数,用于在状态发生变化时执行特定的逻辑。当状态发生变化时,Redux会自动调用所有注册的监听函数。
  5. Middleware:Redux允许使用中间件来处理异步操作、日志记录等。中间件是一个函数,它接收Store的dispatch方法和getState方法作为参数,并返回一个函数,用于处理特定的逻辑。中间件可以在处理Action之前或之后进行一些额外的操作。
    总结来说,Redux的实现原理是通过一个单一的数据源Store来存储应用程序的状态,通过Action来描述状态的变化,通过Reducer来处理状态的变化,通过订阅机制来监听状态的变化,并且可以使用中间件来处理特定的逻辑。这种架构可以使应用程序的状态变化可预测、可追踪,并且易于测试和维护。

24.找出数组【1,2,3,4,5,3,2,2,4,2,2,3,1,3,5】中出现次数最多的数,并统计出现多 少次,编写一个函数?

可以使用JavaScript编写一个函数来找出数组中出现次数最多的数,并统计出现的次数。以下是一个可能的实现:

function findMostFrequentNumber(arr) {
  // 创建一个空对象用于存储每个数出现的次数
  let count = {};
  // 遍历数组,统计每个数出现的次数
  for (let i = 0; i < arr.length; i++) {
    let num = arr[i];
    count[num] = count[num] ? count[num] + 1 : 1;
  }
  let mostFrequentNum;
  let maxCount = 0;
  // 遍历统计结果,找出出现次数最多的数
  for (let num in count) {
    if (count[num] > maxCount) {
      mostFrequentNum = num;
      maxCount = count[num];
    }
  }
  // 返回结果
  return {
    number: mostFrequentNum,
    count: maxCount
  };
}
// 示例用法
const arr = [1, 2, 3, 4, 5, 3, 2, 2, 4, 2, 2, 3, 1, 3, 5];
const result = findMostFrequentNumber(arr);
console.log(`出现次数最多的数是 ${result.number},出现了 ${result.count}`);

运行以上代码,输出结果为:

出现次数最多的数是 2,出现了 5 次

这个函数的实现思路是通过遍历数组,使用一个对象来统计每个数出现的次数。然后再遍历统计结果,找出出现次数最多的数和对应的次数。最后返回包含最频繁数和次数的对象。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值