React 前端面试基础问题

原文链接:https://www.hezebin.com/publish/668caed8c045ae8026909d34

JavaScript

js 数据类型

JavaScript 中的数据类型可以分为两大类:基本数据类型(Primitive Types)和对象类型(Object Types)。下面是它们的详细介绍:

基本数据类型(Primitive Types)

  • 数字(Number):整数和浮点数统一使用 Number 类型表示,包括整数和浮点数。
  • 字符串(String):字符串表示文本数据,使用单引号 ’ 或双引号 " 括起来的字符序列。
  • 布尔值(Boolean):表示逻辑上的 true 或 false 值。
  • 空值(Null):表示一个空值对象,JavaScript 中的数据类型之一。
  • 未定义(Undefined):表示一个未定义的值或对象,通常在声明变量但未给变量赋值时使用。
  • 符号(Symbol)(ES6 新增):表示唯一的标识符。

对象类型(Object Types)

  • 对象(Object):是 JavaScript 中的基本数据类型之一,表示一个无序的集合,由键值对组成。
  • 函数(Function):JavaScript 中的函数是一种特殊的对象,具有可调用的行为。
  • 数组(Array):表示按序排列的集合,可以通过数字索引访问元素。
  • 日期(Date):表示日期和时间。
  • 正则表达式(RegExp):用于匹配字符串的模式。

特殊类型

  • BigInt:表示任意精度的整数(ES10 新增)。
  • 原始包装类型:通过 new 关键字创建的 Number、String 和 Boolean 类型,实际上是对象,但表现类似于基本数据类型。

类型检测
在 JavaScript 中,可以使用 typeof 运算符来检测数据类型:

typeof null 返回 ‘object’,这是 JavaScript 的一个历史遗留问题,实际上 null 是基本数据类型。

深拷贝、浅拷贝的区别?如何实现深拷贝和浅拷贝?

浅拷贝
浅拷贝是指将原始数据的第一层进行复制,如果数据结构中包含引用类型(如对象或数组),则复制的是引用而不是实际的数据。因此,修改复制后的数据会影响到原始数据。浅拷贝常见的实现方式包括:

  • 使用结构运算符 … 或者 Object.assign() 方法来复制对象。
  • 使用 Array.prototype.slice() 方法来复制数组。

深拷贝
深拷贝是指完全复制原始数据,包括所有层级的对象和数组,修改复制后的数据不会影响到原始数据。深拷贝可以通过递归遍历数据结构,并复制每一个子对象或数组来实现。由于深拷贝涉及到递归复制,因此实现起来可能会比较复杂,也需要考虑循环引用的情况。常见的实现方式包括:

  • 使用递归函数来复制对象或数组的每一层。
  • 使用 JSON.parse(JSON.stringify(obj)) 的方式来实现深拷贝,但要注意该方法对于函数、正则表达式等特殊对象的处理不完全。

ES6的一些新特性?

  • let 和 const 声明: 引入了块级作用域的 let 和常量声明的 const。
  • 箭头函数: 简化了函数的书写,可以更方便地处理 this 的指向问题。
  • 模板字符串: 使用反引号 `` 包裹字符串,可以在字符串中嵌入变量和换行符。
  • 默认参数: 允许函数参数设定默认值,简化函数调用时的参数处理。
  • 解构赋值: 可以从数组或对象中提取值,赋给变量。
  • 扩展运算符和剩余参数: 使用 … 对数组进行展开或收集参数。
  • 对象字面量的增强: 允许在对象字面量中简写属性和方法名。
  • class 和继承: 引入了类的概念,可以更方便地进行面向对象的编程。
  • 模块化: 引入了 import 和 export 关键字,支持模块化的开发方式。
  • Promise: 提供了原生的 Promise 对象,简化了异步编程。
    迭代器和生成器: 提供了更灵活的迭代器和生成器函数,简化了迭代的操作。
  • Symbol: 引入了一种新的原始数据类型 Symbol,表示独一无二的值。
  • Map 和 Set: 引入了 Map 和 Set 数据结构,提供了更好的键值对和集合的操作方式。
  • Proxy 和 Reflect: 提供了更强大的元编程能力,可以拦截和定义对象的基本操作。
  • Promise.allSettled: 返回一个在所有给定的 promise 已经 fulfilled 或 rejected 后解析的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果。

js箭头函数?

  • 语法简洁:箭头函数的语法比传统的函数表达式更为简洁,特别是在只有一个参数和单一表达式的情况下,可以省略括号和 return 关键字。
  • 词法作用域:箭头函数不会创建自己的 this、arguments、super 或 new.target,它们会继承外围作用域的这些值。
  • 没有自己的 this:箭头函数内部的 this 是词法上绑定的,指向定义时所在的作用域的 this 值,而不是执行时的 this。
  • 不能用作构造函数:箭头函数没有 [[Construct]] 方法,不能用作构造函数,不能通过 new 关键字调用。

js中常用的数组相关方法?

  • push(): 在数组末尾添加一个或多个元素,返回新数组的长度。
  • pop(): 从数组末尾移除最后一个元素,返回该元素。
  • shift(): 从数组开头移除第一个元素,返回该元素。
  • unshift(): 在数组开头添加一个或多个元素,返回新数组的长度。
  • concat(): 连接两个或多个数组,返回一个新的数组。
  • slice(): 返回一个从开始到结束选择的数组的一部分,原数组不变。
  • splice(): 通过删除现有元素和/或添加新元素来修改数组,返回被删除的元素。
  • forEach(): 对数组的每个元素执行一次提供的函数。
  • map(): 创建一个新数组,其结果是该数组中的每个元素调用一个提供的函数后的返回值。
  • filter(): 创建一个新数组,其包含通过所提供函数实现的测试的所有元素。
  • reduce(): 对数组中的每个元素执行一个提供的函数(从左到右),将其结果汇总为单个返回值。
  • find(): 返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined。
  • findIndex(): 返回数组中满足提供的测试函数的第一个元素的索引,否则返回 -1。
  • indexOf(): 返回数组中第一次出现的指定元素的索引,如果不存在则返回 -1。
  • includes(): 判断一个数组是否包含一个指定的值,如果是则返回 true,否则返回 false。
  • join(): 将数组的所有元素连接成一个字符串,返回这个字符串。
  • reverse(): 将数组中元素的位置颠倒,返回该数组(改变原数组)。
  • sort(): 对数组的元素进行排序,返回该数组(改变原数组)。
  • some(): 测试数组中的某些元素是否通过了由提供的函数实现的测试。
  • every(): 测试数组中的所有元素是否都通过了由提供的函数实现的测试。

js 中Map和obj的区别?

键类型
Object: 键必须是字符串或 Symbol,其他类型会被隐式转换为字符串。
Map: 键可以是任何类型,包括对象、函数、原始数据类型。
键的顺序
Object: 键的顺序不严格保证是插入顺序,但数字键会按数值排序。
Map: 键的顺序完全按照插入顺序。
属性和方法
Object: 继承自 Object.prototype,有许多默认的属性和方法,可能会与自定义的键冲突。
Map: 没有默认的属性和方法,只包含明确的键值对,不会与任何内置属性冲突。
性能
Object: 在频繁增删键值对时性能可能不如 Map 高效。
Map: 设计用于存储和管理键值对,通常在频繁增删键值对时比 Object 更高效。
大小
Object: 没有直接的方法获取对象中键值对的数量,必须手动计算。
Map: 提供了 size 属性,直接返回键值对的数量。
序列化和解析
Object: 更容易被 JSON 序列化和解析。
Map: 需要转换为数组或对象才能被 JSON 序列化和解析。

cookie localstorage和sessionstorage的区别,在域名上的区别?

  • Cookie:存储在客户端浏览器,有大小限制,用于会话标识和跟踪用户状态。
  • Session:存储在服务器端,无大小限制,用于保持用户会话状态。
  • LocalStorage:长期存储在客户端浏览器,用于持久化数据。
  • SessionStorage:临时存储在客户端浏览器,仅在当前会话有效。

域名上的区别总结

  • Cookie:可以设置为特定域名及其子域名下共享访问。
  • LocalStorage:每个域名下的所有页面共享相同的存储空间,不能跨域名访问。
  • SessionStorage:仅在当前会话中有效,也不能跨域名访问。

若要并行发送 2 个网络请求,只要其中一个失败就失败该渲染失败页面,如何实现?

Promise.all 是用于并行执行多个异步操作,并等待它们全部完成后再执行后续操作的工具。它接收一个包含多个 Promise 的数组作为参数,当所有的 Promise 都成功(resolve)时,它才会触发成功(resolve)状态,返回一个包含每个 Promise 结果的数组;如果任意一个 Promise 失败(reject),则会立即触发失败(reject)状态,返回第一个失败的 Promise 的结果。

  • Promise.all 方法接收一个 Promise 数组作为参数,并在所有 Promise 都成功完成(即全部变为 resolved 状态)或有一个 Promise 失败(变为 rejected 状态)时返回一个新的 Promise。
  • Promise.race 方法同样接收一个 Promise 数组作为参数,但是它只关注第一个完成的 Promise(无论成功还是失败),并返回一个新的 Promise。
  • Promise.allSettled 方法接收一个 Promise 数组作为参数,与 Promise.all 不同的是,它会等待所有 Promise 完成(不论成功或失败),并返回一个包含所有 Promise 状态(fulfilled 或 rejected)的对象数组。

谈谈对js事件循环的理解?

JavaScript 事件循环(Event Loop)是 JavaScript 运行时机制的一部分,用于处理异步操作。理解事件循环是理解 JavaScript 异步编程和非阻塞 I/O 操作的关键。以下是对事件循环的详细解释:

事件循环的基本概念
JavaScript 是单线程的,这意味着它一次只能执行一个任务。然而,JavaScript 通过事件循环机制能够处理异步操作,从而在执行长时间任务时仍然保持响应。

栈(Call Stack)
调用栈是一个栈结构,用于存储待执行的函数调用。当一个函数被调用时,它会被压入栈中。当函数执行完毕后,它会从栈中弹出。

堆(Heap)
堆是一个用来存储对象和函数等引用类型的内存空间。

队列(Queue)
队列用于存储待处理的消息或回调函数。这些消息通常来自于异步操作,例如事件处理、定时器、HTTP 请求等。

事件循环的工作机制

  1. 执行同步代码:所有同步任务会在调用栈上按顺序执行。
  2. 处理异步代码:异步任务的回调函数会被放入任务队列中。
  3. 事件循环:事件循环不断检查调用栈是否为空,如果为空,则检查任务队列中是否有待处理的回调函数。如果任务队列中有回调函数,则将其压入调用栈中并执行。

js 任务队列

在 JavaScript 中,任务队列(Task Queue)是一个用于管理待执行任务的队列,它是实现异步执行的重要机制之一。理解任务队列对于理解 JavaScript 异步编程模型至关重要。以下是关于任务队列的几个关键概念和工作原理:
任务队列的概念
任务队列是用来存放异步任务的队列,这些任务会在特定条件下执行。在 JavaScript 中,任务队列主要包括以下几种:

  • 宏任务队列(Macro Task Queue):由浏览器提供的事件队列,比如 setTimeout、setInterval、setImmediate(Node.js 环境)、I/O、UI 渲染等操作。
  • 微任务队列(Micro Task Queue):用来存放需要立即执行的任务,比宏任务执行优先级高。典型的微任务包括 Promise 的 then 方法和 MutationObserver 的回调函数。
    执行过程
    JavaScript 引擎会持续执行任务队列中的任务,按照以下步骤执行:
  • 执行同步任务:按照顺序执行当前调用栈中的所有同步任务。
  • 执行微任务:当前调用栈中的所有同步任务执行完毕后,会立即执行微任务队列中的所有任务。微任务执行完毕后不会立即执行下一个宏任务,而是继续检查是否有新的微任务需要执行,直到微任务队列为空。
  • 执行宏任务:微任务队列为空后,从宏任务队列中取出一个任务执行。执行完宏任务后,再次执行微任务,如此循环。
    示例
console.log('Start');

setTimeout(() = {
  console.log('Timeout');
}, 0);

Promise.resolve().then(() = {
  console.log('Promise');
});

console.log('End');

输出顺序是:

Start
End
Promise
Timeout

什么情况下会导致内存泄漏没法垃圾回收

内存泄漏是指程序中已经不再需要使用的内存,却由于某种原因没有被释放。JavaScript中的内存泄漏通常是由于以下情况导致的:

未清理的计时器或回调函数:

如果设置了定时器或者回调函数,但是在不再需要它们的时候没有被清理,它们会继续占用内存。

function startTimer() {
  setInterval(function() {
    // do something
  }, 1000);
}

startTimer(); // 没有停止定时器的代码,会导致定时器一直存在,内存不会被释放

闭包:

闭包可以使得函数保持对其外部作用域的引用,导致外部作用域中的变量无法被垃圾回收。

function createClosure() {
  let bigObject = new Array(1000000); // 大对象
  return function() {
    // 使用 bigObject,但函数执行完后 bigObject 仍然被引用,无法被回收
  };
}

let closureFn = createClosure();
closureFn();

全局变量:

全局变量一旦定义,会一直存在于内存中直到页面关闭或者被显式释放。

let globalVar = new Array(1000000); // 大对象,全局变量

未释放的DOM节点引用:

如果JavaScript代码中保留了对DOM节点的引用,即使DOM节点从页面中移除,也无法被垃圾回收。

let element = document.getElementById('someElement');
element.addEventListener('click', function() {
  // element 还持有对 DOM 节点的引用
});

循环引用:

对象之间形成相互引用,即使它们彼此不再使用,也无法被垃圾回收。

let obj1 = {};
let obj2 = {};
obj1.ref = obj2;
obj2.ref = obj1;

内存泄漏的常见情况:

  • 长时间运行的Web应用程序,例如单页应用(SPA),可能会因为长时间保持大量数据或对象在内存中而导致内存泄漏。
  • 未处理的异常和错误,可能导致程序逻辑无法正常结束,使得某些资源未能正确释放。
    避免内存泄漏的方法:
  • 及时清理定时器和回调函数,确保不再需要时及时停止。
  • 避免过度使用闭包,在不需要时手动释放闭包引用的外部变量,使用完后主动设置为 null
  • 减少全局变量的使用,尽量使用局部变量或模块化管理。
  • 小心处理DOM节点引用,确保不再需要时及时解除引用。
  • 避免循环引用,设计数据结构时注意避免对象之间相互引用。

理解和避免内存泄漏是优化JavaScript应用程序性能和稳定性的重要步骤之一。

CSS

width:auto 和 width:100%的区别?

  • width: auto
    默认宽度:width: auto 是元素的默认宽度设置。对于块级元素(如 div),它意味着元素将尽可能地宽,占据其父容器的全部可用宽度,减去 padding、border 和 margin。
    适应内容:对于内联元素(如 span)或者非块级元素(如 img),width: auto 通常意味着元素的宽度将根据其内容的宽度自动调整。
    应用场景:适用于希望元素根据其内容或父容器的变化自动调整宽度的情况。特别是在不确定内容宽度或不希望强制占满父容器时使用。
  • width: 100%
    占满父容器:width: 100% 表示元素的宽度将占据其父容器的 100%,减去 padding、border 和 margin。无论父容器有多宽,子元素都将适应这个宽度。
    固定宽度:相对于父容器的固定宽度设置,而不是根据内容自动调整。
    应用场景:适用于希望元素始终占满父容器的情况,例如创建响应式布局时。

css position属性的值有哪些?各个值是什么含义?

CSS 的 position 属性用于指定元素的定位方式。不同的值会影响元素在文档中的定位和布局方式。以下是 position 属性的主要值及其含义:

  1. static (默认值)
    描述:这是元素的默认定位方式,元素按照正常的文档流进行布局。
    特性:元素位置由页面布局决定,不受top、right、bottom、left 属性影响。
    使用场景:通常用于标准文档流中的元素。
  2. relative
    描述:元素相对于其正常位置进行定位。
    特性:元素仍然占据在文档流中的空间,但可以通过top、right、bottom、left 属性进行相对移动。
    使用场景:当需要在保留元素原位置的情况下对其进行微调时。
  3. absolute
    描述:元素相对于最近的已定位(relative、absolute、fixed、sticky)的祖先元素进行定位。如果没有已定位的祖先元素,则相对于初始包含块(通常是视口)。
    特性:元素脱离文档流,不占据原位置的空间,可以通过top、right、bottom、left 属性精确定位。
    使用场景:用于精确定位元素,不影响其他元素布局。
  4. fixed
    描述:元素相对于视口(viewport)进行定位。
    特性:元素脱离文档流,不占据原位置的空间,始终固定在视口的指定位置,即使滚动页面也不会移动。
    使用场景:用于创建固定在屏幕上的元素,如导航栏、返回顶部按钮等。
  5. sticky
    描述:元素根据用户的滚动位置进行定位,在relative和fixed定位之间切换。
    特性:元素在跨越特定阈值之前是相对定位的,之后变为固定定位。阈值通常由top、right、bottom、left 属性定义。
    使用场景:用于创建在滚动时粘性效果的元素,如粘性头部或侧边栏。

有使用过 Flex 布局吗?聊聊 Flex 布局?

主要关注剩余空间自适应,元素比例关系,对齐方式

React

React 常用的 Hook?

useRef、useState、useMemo、memo、useCallback、useEffect

useState 函数式状态更新和非函数式的区别?setCount(count+1);setCount(count-1) 结果是什么?

答案:-1,React 状态更新是异步的,改变值后不会立即重新渲染,而是放入一个队列中,在同一个渲染周期内,相同 state 做 setState 操作时为了避免重复渲染会做一定优化处理,非函数式调用时是最后一个操作覆盖前面的,函数式时为了传递最新的值,会依次调用队列中的 set 操作。

useEffect 依赖项是一个数组,改变数组中的元素是否会触发重新副作用?

答案:引用类型数据依赖的是其引用,而不是值,值改变不会触发副作用

React的组件间如何数据通信?

  • 父组件可以通过 props 向子组件传递数据。子组件通过 props 接收这些数据。
  • 子组件向父组件传递数据通常通过回调函数实现。父组件将一个回调函数作为 prop 传递给子组件,子组件在需要的时候调用这个回调函数,并将数据作为参数传递给父组件。
  • 全局状态管理
  • Context 提供了一种在组件树中共享数据的方法,不必显式地通过组件树的每一层传递 props。
  • Refs 提供了一种访问 DOM 节点或在 class 组件中访问实例方法的方式。

React合成事件

  • 跨浏览器兼容性:合成事件在不同浏览器中表现一致,隐藏了底层浏览器实现的差异性。
  • 事件委派(Event Delegation):React 使用单一事件监听器(event delegation)管理所有事件。这意味着在组件树中的顶层可以捕获所有的事件,而不是在每个节点上都添加事件监听器。
  • 自动绑定:合成事件会自动绑定事件处理函数的上下文(即 this 的指向),无需手动使用 .bind() 或箭头函数绑定。
  • 事件池:React 会对事件进行池化(pooling),以提高性能。在事件处理函数执行结束后,合成事件对象会被重用,避免了在事件处理期间频繁创建和销毁事件对象。

TypeScript

聊聊你对 ts 的使用?

关注常见的类型 type 和 接口 interface 、常用的基础内置泛型Record、Partial、Omit

框架

用过 umi 吗?通常情况下接口都有一个/api 的公共前缀,但是如果在本地开发环境运行的服务接口都没有带/api前缀,在 umi 下该如何解决

config 下配置 proxy,重写路径,需要认证信息的携带所需 header

解决方案

防抖节流的概念?如何实现防抖和节流

防抖(Debounce)和节流(Throttle)是两种常用的优化技术,用于限制某些事件(如滚动、窗口调整大小、按键输入等)的频繁触发,从而提高性能。

防抖(Debounce)
防抖是一种在事件触发后一段时间内不再触发事件的技术。如果在这段时间内再次触发事件,则重新开始计时。通常用于减少高频率触发的事件处理,比如用户输入时的自动保存或搜索提示。

实现防抖,以下是一个防抖函数的实现:

function debounce(func, wait) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}

使用示例:

const handleResize = debounce(() => {
  console.log('Window resized');
}, 500);

window.addEventListener('resize', handleResize);

在这个例子中,handleResize 函数会在窗口调整大小事件触发 500 毫秒后执行。如果在这段时间内再次触发事件,计时器会重新开始。

节流(Throttle)
节流是一种限制事件触发频率的技术。在特定时间段内,事件处理函数只会执行一次。常用于限制滚动、鼠标移动等高频率事件的处理。

实现节流,以下是一个节流函数的实现:

function throttle(func, limit) {
  let inThrottle;
  return function() {
    const context = this;
    const args = arguments;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

使用示例:

const handleScroll = throttle(() => {
  console.log('Scrolled');
}, 1000);

window.addEventListener('scroll', handleScroll);

在这个例子中,handleScroll 函数会每隔 1000 毫秒最多执行一次,即使在这段时间内多次触发滚动事件。

React.js 是一种非常流行的 JavaScript 库,用于构建用户界面组件化的应用程序。如果你正在准备 React 相关的前端面试,那么可能会遇到以下类型的题目。这里列举了一些常见的面试问题以及解答建议,帮助你做好准备: ### 基础知识 1. **简述 React 的设计理念和优势** - **设计原则**:组件化、虚拟 DOM、状态管理和响应式更新。 - **优势**:性能优化(只渲染变化的部分)、易于维护和扩展、丰富的生态系统、社区活跃。 2. **React 中的生命周期函数有哪些?分别是什么作用?** - 初始化阶段:`constructor`、`componentDidMount` - 更新阶段:`shouldComponentUpdate`、`getDerivedStateFromProps`、`render`、`getSnapshotBeforeUpdate` - 渲染后:`componentDidUpdate`、`componentWillUnmount` 3. **描述一下虚拟 DOM 和真实 DOM,以及 React 使用虚拟 DOM 的原因是什么?** - 虚拟 DOM 是内存中的一个轻量级版本的真实 DOM 树。 - **原因**:减少直接修改真实 DOM 的频率,提高性能;通过比较新旧虚拟 DOM 来确定哪些部分需要实际更新,仅更新必要的部分,而不是整个页面。 ### 高级特性 1. **讲解一下 Redux 在 React 应用中的用途和工作原理?** - **用途**:用于应用的状态管理,尤其是大型、复杂的应用程序。 - **工作原理**:基于 store 进行状态集中管理,action 触发 reducer 更改 state,state 变更后自动触发 component render。 2. **阐述一下 React Hooks 的应用场景及其重要性。** - **应用场景**:替代类组件中的生命周期方法,例如 `useState` 管理局部状态,`useEffect` 执行副作用操作,`useContext` 用于 context 消息传递等。 - **重要性**:使得函数组件可以有状态和生命周期,简化代码结构,提高复用性。 3. **解释一下高阶组件(HOC)和 Composition 的区别和联系?** - **高阶组件**:通过接受其他组件作为 props 并返回一个新的组件,实现了功能增强。 - **Composition**:通过组合多个组件来创建复杂的行为,是函数式编程的思想,提倡模块化设计。 ### 组件管理和路由 1. **React Router 的主要特点和使用场景是什么?** - **主要特点**:路由配置简单、支持嵌套路由、支持历史模式(history API)和浏览器模式(hash-based routing),适配不同部署环境。 - **使用场景**:用于动态生成视图、导航控制和状态管理。 ### 代码片段解析和调试 1. **给出一段错误的代码示例,并说明其中的错误以及如何修正?** - 示例:`const {name} = user; console.log(user.name);` - 错误:如果 `user` 对象中不存在 `name` 属性,则访问该属性会产生 undefined。 - 修改:使用解构赋值时应检查是否存在对应属性,如 `const { name } = user || {};` 或者使用默认值如 `const { name = "defaultName" } = user;`。 以上这些问题涵盖了基础知识、高级特性和实践应用等多个方面,可以帮助你在准备 React 相关的面试时全面复习并深入理解关键概念和技术细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值