自收藏前端面试八股文(一),共勉

1.React闭包陷阱产生的原因是什么,如何解决?

在React组件中使用了异步操作时,闭包会保留对旧状态的引用,导致更新后的状态无法正确地被获取或使用。 解决问题: 将状态设置到依赖项中,并添加清理函数。在React组件中,将状态设置为依赖项,这样每次使用状态时都会检查依赖项是否发生变化。如果依赖项发生变化,就会执行新的函数,引用新的状态。同时,还要注意清理上次的定时器、事件监听器等,以避免内存泄漏。 如果是在函数组件中遇到闭包陷阱问题,每次点击按钮都会使得计数自增一次,导致Demo1函数重新执行一遍,形成一个新的闭包。每个闭包中的计数值是相互独立的。因此,在点击按钮的这一瞬间形成的闭包中计数的值为5,3秒之后弹出来的计数值也为5。而后面再次自增形成的是新的闭包,互不干扰。在Class组件中由于都是在this的上下文中改变计数值,而不会形成隔离的闭包,因此虽然点击按钮那一时刻计数的值为5,但是随着后面点击add count按钮,计数值不断自增。修改的都是this上面的值,3秒后弹出的也就是计数最新值了。 总的来说,要避免闭包陷阱的出现,需要正确设置依赖数组以确保在回调函数中使用的状态和props始终是最新的值。如果回调函数中使用了组件的状态或props,应该将其添加到依赖数组中,以便在它们发生变化时触发回调函数的更新。

2.TypeScript 的内置数据类型有哪些?

TypeScript 是一种强类型语言,这意味着所有的数据都必须被定义为一种特定的类型。以下是 TypeScript 中的一些基本数据类型:
any:代表任意类型,是所有类型的联合。any 类型可以表示一个变量是字符串、数字、对象、布尔值、null 或 undefined 等。
null 和 undefined:null 表示一个空或不存在的引用,而 undefined 表示变量未定义。
boolean:表示布尔值,即 true 或 false。
number:表示数值,包括整数和浮点数。
string:表示文本数据。
symbol:表示唯一且不可变的数据类型。
object:表示一个复杂的类型,可以包含各种属性和方法。
never:表示一个永不存在的值的类型。
void:表示一个无返回值的函数的返回类型。
tuple:表示一组有序的项,每个项可以有不同的类型。
enum:表示一组命名的数字常量。
array:表示一个有序的项的集合,每个项可以是任意类型。
function:表示一个函数类型,可以带参数和返回值。
this:表示当前类的实例类型。
unknown:表示未知的类型,是所有类型的联合。

3.Typescript中泛型是什么?

在TypeScript中,泛型是一种创建可重用的组件的方式,这些组件可以在多种数据类型上工作。泛型允许您编写灵活的代码,可以在多种数据类型之间共享行为,而无需为每种类型重复编写代码。
在TypeScript中,您可以使用泛型函数、泛型类、泛型接口和泛型方法来创建泛型代码。
以下是泛型的一些基本概念和用法:
泛型函数:泛型函数是一种可以处理多种数据类型的函数。您可以在函数签名中使用类型参数来定义泛型函数。
泛型类:泛型类是一种可以处理多种数据类型的类。您可以在类定义中使用类型参数来定义泛型类。
泛型接口:泛型接口是一种可以描述多种数据类型的接口。您可以在接口定义中使用类型参数来定义泛型接口。
泛型方法:泛型方法是一种可以在类或对象中使用的方法,该方法可以处理多种数据类型。您可以在方法定义中使用类型参数来定义泛型方法。
总之,泛型是一种强大的工具,允许您编写更加灵活和可重用的代码。通过使用泛型,您可以编写适用于多种数据类型的函数、类、接口和方法,从而减少重复的代码和增加代码的可重用性

4.Typescript中 interface 和 type 的区别是什么?

定义方式:
Interface:使用interface关键字定义。它允许你定义对象的结构,包括属性、方法和索引签名。
Type:使用type关键字定义。它更像是一个类型别名,允许你为已有的类型(如其他接口、类、类型别名或基础类型)定义一个新的名称。

用途:
Interface:主要用于描述对象的形状,确保对象具有某些属性或方法。它经常与类一起使用,以确保类的实例满足特定的结构要求。
Type:主要用于简化复杂的类型或为类型定义别名。例如,你可以为数组、函数或其他接口定义类型别名。

继承:
Interface:可以继承其他接口,实现多重继承。
Type:不能继承其他类型。

扩展性:
Interface:可以添加新的属性或方法,以扩展其定义。
Type:一旦一个类型被定义,就不能添加新的属性或方法。

可选性:
Interface:属性或方法可以是可选的,使用?标记。
Type:不能标记属性或方法为可选的。

初始化和实例化:
Interface:不能被初始化为实例对象或被实例化。它们只是描述了对象的形状。
Type:可以作为变量、参数、返回值等的类型定义。

与类结合:
Interface:经常与类一起使用,描述类的形状和行为。
Type:也可以与类结合使用,但不如接口那么常见。

与泛型的结合:
Interface:可以与泛型结合,定义具有泛型参数的类型结构。
Type:也可以与泛型结合,但通常与接口结合得更多。

静态性:
Interface:在编译时解析和验证,具有静态类型检查的优点。
Type:在运行时解析和验证,没有静态类型检查的优点。

使用场景:
Interface:当你想描述对象的结构或确保对象满足某些条件时使用。在复杂和大型项目中,接口对于确保代码的一致性和可维护性非常有用。
Type:当你需要为现有类型定义一个简短的别名时使用,例如函数类型、复杂对象的简化表示等。

5.如何将 unknown 类型指定为一个更具体的类型?

在TypeScript中,如果一个变量的类型被标记为 unknown,你可以使用类型断言将其转换为更具体的类型。 假设你有以下情况: let value: unknown = "some string"; 如果你知道这个值实际上是一个字符串,你可以使用类型断言将其转换为字符串类型: let value: unknown = "some string"; let strValue: string = value as string; // 使用类型断言 在这个例子中,value 的类型被断言为 string。 你也可以使用非空断言运算符 ! 来断言一个值是非空的。例如: let value: unknown = "some string"; let strValue: string = value!; // 使用非空断言运算符 注意,非空断言运算符可能会导致运行时错误,如果值实际上是 null 或 undefined。因此,你应该确保你知道值在运行时不会是 null 或 undefined。\

6.react出现 新生命周期的原因

在 React v16.3 版本之前,React 类组件中常用的生命周期方法包括 componentWillMountcomponentDidMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdate 等。

旧的生命周期十分完整,基本可以捕捉到组件更新的每一个state/props/ref,没有什从逻辑上的毛病。但是官方react打算在17版本推出新的Async Rendering,提出一种可被打断的生命周期,而可以被打断的阶段正是实际dom挂载之前的虚拟dom构建阶段,也就是要被去掉的三个生命周期。

生命周期一旦被打断,下次恢复的时候又会再跑一次之前的生命周期,因此componentWillMount,componentWillReceiveProps, componentWillUpdate都不能保证只在挂载/拿到props/状态变化的时候刷新一次了,所以这三个方法被标记为不安全

然而,这些生命周期方法在使用上存在一些问题,比如:

  1. 可能引发副作用:在 componentWillMountcomponentWillUpdate 等方法中,开发人员经常会进行网络请求、手动 DOM 操作或订阅事件,这可能导致不一致的状态和意外的副作用。

  2. 命名冲突:React 的生命周期方法命名容易与其他第三方库或开发者定义的方法冲突。

  3. 过于复杂:对于初学者来说,理解和使用这些生命周期方法可能会造成一定的困惑和复杂性。

为了解决上述问题,React 在 v16.3 版本引入了新的生命周期方法。主要原因包括:

  1. 分离副作用:为了减少副作用并提高组件可维护性,React 推荐将副作用(例如网络请求、DOM 操作等)移至 componentDidMountcomponentDidUpdatecomponentWillUnmount 等生命周期方法之外的地方。这使得代码更具可读性和可预测性。

  2. 减少不必要的渲染:新的 shouldComponentUpdate 方法让开发者能够自定义组件是否应该重新渲染,以优化性能并避免无谓的重渲染。

  3. 组件实例化与更新的统一:新的生命周期方法更符合组件实例化和更新的一致性,使开发者更容易理解和使用。例如,componentWillMount 被废弃,取而代之的是 constructor 方法,并且 componentWillReceivePropsgetDerivedStateFromPropscomponentDidUpdate 替代。

总的来说,React 引入新的生命周期方法是为了提供更清晰、更可控且更一致的组件生命周期体验,并帮助开发者编写更易于维护和性能更好的代码。

7.说说javascript内存泄漏的几种情况

JavaScript 内存泄漏通常发生在不再需要的对象仍然被保留在内存中的情况下。以下是几种常见的 JavaScript 内存泄漏情况:

1. 未及时释放引用:当使用闭包、事件监听、定时器等特性时,如果没有适时地释放对对象的引用,这些对象将无法被垃圾回收,导致内存泄漏。

2. 循环引用:当对象之间形成循环引用时,即使它们已经不再使用,垃圾回收机制也无法正确地回收它们。例如,在对象 A 中引用了对象 B,同时对象 B 中也引用了对象 A,这种循环引用会导致内存泄漏。

3. 未清理的事件监听:如果在 DOM 元素上绑定了事件监听,但没有及时解绑或移除绑定事件的元素,这些元素会一直被保留在内存中,即使它们已经被移除或不再需要。

4. 大量缓存数据:如果在客户端中大量使用缓存,但没有有效的清理机制和过期处理,缓存的数据会持续占用内存空间,导致内存泄漏。

5. 闭包中的变量未释放:当在闭包内部引用了外部作用域的变量,即使外部作用域的函数执行完毕,闭包中的变量也不会被立即释放,可能导致外部作用域中的变量无法被垃圾回收。

6. 内存泄漏的第三方库:某些第三方库存在内存泄漏问题,可能是库本身的Bug或者使用不当导致的。在使用第三方库时,需要留意其文档或官方建议,以避免潜在的内存泄漏。

为避免内存泄漏,可以采取以下措施:

- 确保在不再需要对象时及时解除引用;
- 避免循环引用;
- 仔细管理事件监听,确保在不需要时及时清除;
- 合理使用缓存,设置过期时间和清理机制;
- 避免滥用闭包,减少不必要的引用;
- 注意第三方库的使用和潜在问题。

通过这些实践,可以有效地减少 JavaScript 内存泄漏的发生。同时,使用开发者工具进行内存分析也是发现和解决内存泄漏问题的一种有效方法。

8.说说如何借助webpack来优化前端性能

使用 webpack 可以通过多种方式优化前端性能。以下是一些常见的优化实践:

  1. 代码压缩:通过使用 webpack 的内置插件(如 UglifyJsPlugin、TerserPlugin)可以将 JavaScript 代码进行压缩和混淆,减小文件大小,提高加载速度。

  2. 按需加载:使用 webpack 的代码分割(code splitting)功能,将应用程序拆分为多个小块(chunks),并在需要时按需加载,以减少初始加载时间和资源传输量。

  3. 模块懒加载:将应用程序拆分为多个异步加载的模块,只在需要时动态加载。这可以减小初始包大小,并且可以延迟加载不常用的代码,提升首次加载速度。

  4. 图片优化:使用 webpack 的 file-loader 或 url-loader 可以对图片进行优化,如压缩、缩放或使用 WebP 格式,以减小图片文件大小,提高加载速度。另外,使用 image-webpack-loader 可以在构建过程中优化图片。

  5. 缓存管理:通过配置 webpack 的输出文件名、使用 contenthash 等方式,确保文件内容发生变化时,生成的文件名也会发生变化。这样可以利用浏览器缓存机制,减少对不变文件的重复加载,提高访问速度。

  6. 剔除未使用代码:使用 webpack 的 tree shaking 功能可以自动识别和剔除未使用的代码,减小构建产物的体积。

  7. 模块作用域提升:通过使用 webpack 的 Scope Hoisting 或者使用 ES6 的模块语法,可以将模块之间的依赖关系简化,减少函数声明的数量,从而提升代码在浏览器中的执行速度。

  8. 缓存 vendor bundle:将第三方库(如 React、lodash 等)单独打包为一个 vendor bundle,并使用长期缓存策略(contenthash)来确保在不变时,浏览器可以从缓存中加载。

  9. 并行构建:使用 webpack 的 parallel-webpack 或者使用多核 CPU 运行构建工具,可以加快构建速度。

  10. CDN 加速:将静态资源部署到 CDN,可以利用 CDN 的分布式网络加速资源的传输,减少用户的响应时间。

这些是一些常见的优化实践,具体的优化策略可以根据项目的需求和特点进行调整和扩展。

9. 说说webpack中常见的loader?解决了什么问题?

在 webpack 中,loader 是用于对模块源代码进行转换的工具。它们可以在 webpack 构建过程中的模块加载阶段对文件进行预处理,将它们转换为 webpack 可以识别和处理的格式,以便在最终的 bundle 文件中包含。

以下是 webpack 中常见的一些 loader:

1. babel-loader:用于将 ES6+ 的 JavaScript 代码转换为向后兼容的 JavaScript 代码,以便在旧版浏览器中正确运行。

2. css-loader:用于解析 CSS 文件,处理其中的 `@import` 和 `url()` 引用,并将解析后的 CSS 转换为 JavaScript 对象。

3. style-loader:用于将解析后的 CSS 通过动态生成 `<style>` 标签的方式插入到 HTML 页面中。

4. file-loader:用于处理文件(如图像、字体等),可以将文件复制到输出目录,并返回文件的 URL 地址。

5. url-loader:类似于 file-loader,但对于较小的文件,可以将文件转换为 data URL,以减少 HTTP 请求次数。

6. sass-loader:用于将 Sass/SCSS 文件编译为 CSS 文件。

这些 loader 解决了以下问题:

1. 处理不同类型的模块:通过 loader,webpack 可以处理各种不同类型的模块,例如 JavaScript、CSS、图片等,使得这些模块能够被正确地加载和使用。

2. 转换和处理代码:通过 loader,可以对源代码进行转换,例如将 ES6+ 的语法转换为 ES5,将 Sass/SCSS 文件编译为 CSS 等,从而解决浏览器兼容性和开发效率的问题。

3. 模块解析和依赖管理:loader 可以解析模块之间的依赖关系,并将依赖的模块正确地加载和处理,使得各个模块之间能够正常地协同工作。

总而言之,通过使用不同的 loader,webpack 能够将各种类型的资源统一处理,并将其转换为可以在浏览器中正常运行的格式,从而大大提高了开发效率和项目的可维护性。

10.React性能优化的手段有哪些?

在 React 中,有几种常见的性能优化手段可以帮助我们提高应用的性能:

  1. 使用合适的组件更新策略:在 React 中存在两种组件更新策略,即基于类的组件和基于函数的组件。对于函数组件,可以使用 React.memo 来对组件进行浅层比较,避免不必要的重新渲染。对于类组件,可以使用 PureComponent 或 shouldComponentUpdate 方法来确保只有在 props 或 state 发生变化时才进行重新渲染。

  2. 使用列表组件的 key 属性:在渲染列表时,为每个列表项提供一个唯一的 key 属性。这样可以帮助 React 更准确地追踪列表项的变化,提高列表的更新性能。

  3. 避免不必要的渲染:在组件的生命周期方法(如 shouldComponentUpdate、useMemo、useCallback)或钩子函数中,可以根据需要进行条件判断,避免不必要的渲染。例如,可以使用 useCallback 来缓存函数,避免组件重新渲染时创建新的回调函数。

  4. 分割组件:将大型组件拆分为更小的组件,提高组件的可维护性和重用性。这样可以减少不必要的渲染和更新操作,并更好地控制组件的性能。

  5. 使用虚拟化技术:对于长列表或大型表格等需要渲染大量数据的场景,可以使用虚拟化技术(如 react-virtualized、react-window)来优化性能。虚拟化技术只在可视区域内渲染组件,减少 DOM 操作和内存占用。

  6. 使用 memoization(记忆化):通过使用 memoization 技术,可以缓存函数的计算结果,避免重复计算,提高性能。可以使用 memoize-one、reselect 等库来实现记忆化。

  7. 使用异步加载:对于大型应用,可以考虑使用异步加载(代码分割)来延迟加载不必要的模块,提高应用的初始加载速度。

需要注意的是,并不是所有的优化手段都适用于每个场景。具体的优化策略需要根据应用的实际需求和具体情况进行选择和实施。在实施优化策略之前,可以使用性能分析工具(如 React DevTools、Chrome 开发者工具)来评估应用的性能瓶颈,并找到最适合的优化方案。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值