前端百问面试题之鱼龙混杂(第三篇),Web前端面试刷题

let maxNum = null;

let maxCount = 0;

for (const num in counter) {

 if (counter[num] > maxCount) {

  maxNum = num;

  maxCount = counter[num];

}

}


return { number: maxNum, 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} 次);


## 22大文件如何做断点续传?(2)


\1. 将大文件分割为小块:将大文件分割成多个固定大小的小块,例如每个小块的大小可以设置为 1MB。这样可以方便进行分片上传和管理。


\2. 记录上传进度:在客户端和服务端都需要记录上传进度。客户端可以通过记录已成功上传的分片信息来跟踪上传进度,服务端可以通过记录已接收的分片信息来跟踪接收进度。


\3. 分片上传:将分割后的小块逐个上传到服务器,并记录已成功上传的分片信息。可以使用 HTTP 或其他协议来进行分片上传。


\4. 上传失败处理:如果某个分片上传失败,需要记录下该分片的状态,以便后续重新上传。可以保存上传失败的分片信息,并将其标记为未完成。


\5. 断点续传:在下次上传时,检查之前的上传进度和已完成的分片信息。根据记录的信息,跳过已成功上传的分片,只上传未完成的分片。确保已上传的分片不会重复上传。


\6. 合并分片:当所有分片都上传完成后,在服务端将这些分片按顺序合并为完整的文件。




## 23.元素js如何实现上拉加载下拉刷新?(2)


<script> var content = document.getElementById('content'); var items = document.getElementsByClassName('item');



var maxItems = 10; // 总共可加载的项
var visibleItems = 5; // 每次加载可见的项
var loadedItems = visibleItems; // 已加载的项

// 监听滚动事件,判断是否需要上拉加载
content.addEventListener(‘scroll’, function() {
if (content.scrollTop + content.clientHeight >= content.scrollHeight) {
  loadMoreItems();
}
});

// 下拉刷新
content.addEventListener(‘touchstart’, handleTouchStart, false);        
content.addEventListener(‘touchmove’, handleTouchMove, false);

var startY;
function handleTouchStart(event) {
startY = event.touches[0].clientY;
}

function handleTouchMove(event) {
var touch = event.touches[0].clientY;
var diff = touch - startY;
if (diff > 0 && content.scrollTop === 0) {
  event.preventDefault();
  refreshItems();
}
}

// 上拉加载更多项
function loadMoreItems() {
if (loadedItems >= maxItems) {
  return; // 如果已加载的项达到了总共可加载的项,就不再加载
}

for (var i = loadedItems; i < loadedItems + visibleItems; i++) {
  var newItem = document.createElement(‘div’);
  newItem.className = ‘item’;
  newItem.textContent = 'Item ’ + (i + 1);
  content.appendChild(newItem);
}

loadedItems += visibleItems;
}

// 下拉刷新项
function refreshItems() {
// 清空已加载的项
while (content.firstChild) {
  content.firstChild.remove();
}

loadedItems = visibleItems; // 重置已加载的项计数

for (var i = 0; i < loadedItems; i++) {
  var newItem = document.createElement(‘div’);
  newItem.className = ‘item’;
  newItem.textContent = 'Item ’ + (i + 1);
  content.appendChild(newItem);
}
}


</script>




## 24说说设备像素、css像素、设备独立像素、dpr、ppi之间的区别?(2)


\1. 设备像素 设备像素指物理屏幕上的像素点数量,通常用“px”表示。屏幕上的每个px将有一个相对应的硬件像素来对应显示。设备像素决定了屏幕的绝对分辨率。


\2. CSS 像素 CSS 像素在设计中用来表示页面中图形和文本等元素尺寸的虚拟单位,通常用“px”表示。CSS 像素并不是“真实”的像素,它是相对于屏幕分辨率独立的。


\3. 设备独立像素 设备独立像素(DP,Density-Independent Pixel),也称为逻辑像素,是一种基于屏幕密度的抽象概念,通常用“dp”或“dip”表示,Android系统中也将其称为“sp”。设备独立像素是与屏幕像素密度有关的像素单位,屏幕像素密度越高,同样大小的物体所占设备独立像素就越高。


\4. DPR(设备像素比) DPR指的是设备像素和CSS像素的比例关系,即“设备像素 / CSS像素” 。它用来描述物理像素与逻辑像素之间的关系。常见的 DPR 值包括 1、2、3 等。


\5. PPI(每英寸像素数) PPI指的是屏幕上每英寸显示的像素点数量,通常用来表示屏幕密度以及屏幕显示清晰程度。PPI 值可以通过计算设备像素和屏幕尺寸之间的比例得到。



## 25说说TCP为什么需要三次握手和四次握手?(2)


\1. 三次握手 **初始化双方 seq。 确认双方信道可以实现最低限度的全双工**三次握手是指在建立 TCP 连接时,客户端与服务器之间需要进行三次通信以确认连接的建立。其流程如下:


· 第一次握手:客户端向服务器发送 SYN 报文,并随机生成一个初始序号(ISN)。


· 第二次握手:服务器收到SYN报文后,回复一个SYN+ACK报文作为确认,其也会随机生成一个ISN值。


· 第三次握手:客户端再次向服务器回复一个ACK报文,确认连接已建立。


\2. 四次握手 **TCP 要支持半关闭连接**四次握手是指在释放 TCP 连接时,客户端与服务器之间需要通过四次通信来确认连接已经关闭。其流程如下:


· 第一次握手:客户端向服务器发送FIN报文,请求关闭连接。


· 第二次握手:服务器收到FIN报文后,回复一个ACK报文作为确认,但此时仍可向客户端发送数据。


· 第三次握手:服务器完成数据的发送后,则向客户端发送一个FIN报文,表示服务器的数据发送完毕,请求关闭连接。


第四次握手:客户端收到服务器的FIN报文后,向服务器回复一个ACK报文作为确认,进入 TIME\_WAIT 状态,等待 2MSL 后断开连接。


## 26.react之间如何通信(3)


1.props:通过向子组件传递 props,可以实现父子组件之间的通信。这是 React 中最基本的通信方式。父组件可以将需要传递给子组 件的数据作为 props 传递给子组件,子组件可以通过 `this.props` 访问这些数据。


2.Context:Context 是 React 中的一种全局数据管理方式。它可以让子组件在不通过 props 传递的情况下,直接访问父组件或者祖先 组件中的数据。使用 Context 需要先创建一个 Context 对象,然后在祖先组件中通过 Provider 提供数据,在子孙组件中通 过 Consumer 访问数据。


3.Refs:Refs 允许我们访问在组件中创建的 DOM 或者其他组件实例。通过 Refs,组件可以在不通过 props 或者 context 的情况下, 直接修改子组件或者 DOM 元素的属性。


4.Event Bus:Event Bus 是一种跨组件通信方式,它可以让任何两个组件之间都可以进行通信。



## 27.什么是闭包,应用场景是什么(2)


闭包是指在函数内部创建的函数,并且该函数能够访问到其外部函数的作用域。


闭包有以下特点:


1. 内部函数可以访问外部函数中的变量和参数。
2. 外部函数的执行上下文被保留在内存中,即使外部函数执行完成后,内部函数仍然可以访问外部函数的作用域。
3. 多个内部函数可以共享同一个父级作用域,形成一个闭包函数族。


闭包的应用场景包括但不限于:


1. 保护变量:通过使用闭包,可以隐藏变量,只提供对外部函数公开的接口。这样,可以确保变量不会被外部直接修改,增加了数据的安全性。
2. 计数器和累加器:通过闭包,可以在函数外部保存一个内部状态,并在每次调用内部函数时修改该状态。这一特性可用于实现计数器、累加器等功能。
3. 延迟执行和回调函数:将函数作为返回值,可以实现延迟执行或者在特定条件满足时回调执行。



## 28.JS数组之间进行合并,写出三种合并方式?


concat() 方法:


`concat()` 方法可以合并两个或多个数组,并返回一个新数组。它会将所有输入数组中的元素添加到一个新的数组中。


spread 运算符(扩展运算符):


ES6 中引入的 spread 运算符可以很方便地合并数组。它可以将一个数组转换为独立的参数列表,从而可以很容易地合并多个数组。


push() 方法:


`push()` 方法可以将一个或多个元素添加到数组的末尾,并返回修改后的数组长度。因此,可以通过循环或迭代来将一个数组中的元素添加到另一个数组中。


## 29.为什么 useState 返回的是数组而不是对象?


在 React 中,`useState` 是一个用于在函数组件中声明状态的钩子函数。它的返回值是一个长度固定为 2 的数组,而不是一个对象,这是由设计选择所决定的。


使用数组来表示状态,是因为它具有以下优势:


1. 简单直观:将状态以数组的形式进行管理,可以更容易地理解和使用。
2. 顺序保持一致:使用 `useState` 声明多个状态时,它们的顺序和声明的顺序是完全一致的。你可以根据索引访问和更新每个状态,而不需要命名或记住状态的特定名称。
3. 无需每次指定键:当更新状态时,不需要像使用对象那样指定键。只要调用 `useState` 返回的第二个元素(通常命名为 `setXXX`)即可。


## 30.React 的生命周期?每个生命周期都做了什么?


在React中,组件的生命周期可以分为三个主要的阶段:挂载(Mounting)、更新(Updating)和卸载(Unmounting)。以下是每个阶段对应的方法:


1. 挂载阶段(Mounting Phase):


* constructor:组件实例被创建时调用,用于初始化状态和绑定事件处理函数。
* static getDerivedStateFromProps:在组件实例化和更新时都会调用,它接收新的属性和当前状态,并返回一个更新后的状态。
* render:根据组件的状态和属性,返回JSX元素进行渲染,负责生成组件的UI结构。
* componentDidMount:组件已经被渲染到DOM中后调用,此时可以进行网络请求、订阅事件等副作用操作。


1. 更新阶段(Updating Phase):


* static getDerivedStateFromProps:在组件更新时调用,接收新的属性和当前状态,并返回一个更新后的状态。
* shouldComponentUpdate:在组件即将更新之前调用,返回一个布尔值,用于判断是否需要进行组件的重新渲染。
* render:重新渲染组件的UI结构。
* componentDidUpdate:组件已经重新渲染并且更新到DOM上后调用,此时可以进行状态更新、网络请求等副作用操作。


1. 卸载阶段(Unmounting Phase):


* componentWillUnmount:组件将要从DOM中移除时调用,可以进行清理工作,如取消订阅、清除定时器等。



## 32.Js中深拷贝和浅拷贝的定义以及具体实现都有哪些方法?


**浅拷贝**是指创建一个新对象或数组,该对象或数组的值是原始对象或数组中的引用。换句话说,浅拷贝只复制对象或数组的第一层,而不会递归复制嵌套的对象或数组。


常见的实现浅拷贝的方法有:


1. 扩展运算符(Spread Operator)

 

javascriptCopy Codeconst originalArray = [1, 2, 3];
const newArray = […originalArray];

 

javascriptCopy Codeconst originalObject = { name: “John”, age: 25 };
const newObject = { …originalObject };

2. `Object.assign()`

 

javascriptCopy Codeconst originalObject = { name: “John”, age: 25 };
const newObject = Object.assign({}, originalObject);

 注意:`Object.assign()` 在浅拷贝对象时是深拷贝属性值。
3. `Array.slice()`

 

javascriptCopy Codeconst originalArray = [1, 2, 3];
const newArray = originalArray.slice();



**深拷贝**是指创建一个全新的对象或数组,并递归地复制原始对象或数组的所有嵌套对象或数组。


常见的实现深拷贝的方法有:


1. 使用 JSON 序列化和反序列化

 

javascriptCopy Codeconst originalObject = { name: “John”, age: 25 };
const newObject = JSON.parse(JSON.stringify(originalObject));

 这种方法可以处理大多数情况,但是对于包含函数、正则表达式等特殊对象的深拷贝可能会有问题。
2. 递归实现深拷贝

 

javascriptCopy Codefunction deepCopy(obj) {
if (typeof obj === “object” && obj !== null) {
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
return copy;
}
return obj;
}

const originalObject = { name: “John”, age: 25 };
const newObject = deepCopy(originalObject);




## 33.Umi中如何实现路由权限,实现按钮权限?


在 Umi 中实现路由权限和按钮权限可以通过以下步骤:


\1. 路由权限:


· 在路由配置文件中,为每个需要进行权限控制的路由添加 authority 属性,值为权限标识,例如角色名或权限码。


在全局的路由守卫(比如 layouts 中的页面组件)中,判断当前用户是否有权限访问该路由。可以使用 Access 组件配合 authority 属性来进行权限控制。


\2. 按钮权限:


· 在页面组件里,在需要进行权限控制的按钮上添加 authority 属性,值为权限标识,例如角色名或权限码。


自定义一个 Button 组件,根据当前权限判断是否显示按钮。


然后,在页面组件中使用 AccessibleButton 来替代 antd 的 Button 组件,传递相应的 authority 属性。


## 32.props和state相同点和不同点?render方法在哪些情况下会执行?


Props(属性)和 State(状态)是 React 组件中用于管理数据的两个概念。


相同点:


1. 它们都是用于存储和管理组件数据的。
2. 当它们的值发生变化时,都可以触发组件重新渲染。


不同点:


1. Source(数据来源):Props 是从父组件传递给子组件的,而 State 则是在组件内部定义和管理的。
2. Mutability(可变性):Props 是只读的,无法在子组件内部直接修改,只能通过父组件重新传递新的 Props。而 State 可以在组件内部进行修改和更新。
3. 更新方式:当 Props 发生变化时,React 会自动进行组件的重新渲染。而 State 的更新需要调用组件的 `setState` 方法来触发重新渲染。


关于 `render` 方法的执行情况:


1. 初始渲染:组件首次被渲染时,`render` 方法会被调用,并生成对应的虚拟 DOM 树。
2. 数据变化:当组件的 Props 或 State 发生变化时,React 会重新调用 `render` 方法来更新虚拟 DOM,并与之前的虚拟 DOM 进行对比。
3. 强制更新:通过调用组件的 `forceUpdate` 方法可以强制触发 `render` 方法的执行,即使 Props 和 State 没有发生变化。
4. 父组件更新:如果父组件进行重新渲染,子组件的 `render` 方法也会被调用。


需要注意的是,只有在 `render` 方法中返回的虚拟 DOM 与之前的虚拟 DOM 不同,React 才会重新渲染真实 DOM,并更新到页面上。


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


Object.defineProperty() 是 JavaScript 中用于定义或修改对象属性的方法。通过这个方法,可以精确地控制属性的特性,例如可枚举性、可写性、可配置性以及属性的值。


这个方法接受三个参数:要定义属性的对象、属性名和属性描述符对象。属性描述符对象可以包含以下属性:


1. value:属性的值。
2. writable:属性是否可写。
3. enumerable:属性是否可枚举。
4. configurable:属性是否可配置。


使用 Object.defineProperty() 方法可以实现对属性的定制化控制,例如可以定义一个不可枚举、不可写、但可配置的属性,从而实现对属性的严格保护或隐藏。


## 34.说说React中的虚拟dom?在虚拟dom计算的时候diff和key之间有什么关系?


虚拟 DOM(Virtual DOM)是 React 中的一种机制,通过在内存中构建一棵轻量级的虚拟 DOM 树来代替操作浏览器 DOM,从而提高组件的渲染性能和用户体验。


在 React 中,当组件的 Props 或 State 发生变化时,React 会根据最新的数据重新生成一棵虚拟 DOM 树,并与之前的虚拟 DOM 树进行对比。在对比的过程中,React 会找到两棵树中不同的节点,并将它们对应的真实 DOM 节点进行修改、删除或添加,最终将更新后的 DOM 渲染到页面上。


虚拟 DOM 的 diff 算法是 React 优化渲染性能的核心。在 diff 算法中,每个节点都有一个唯一的标识符,称为 key。当新旧虚拟 DOM 树进行对比时,React 会通过 key 来判断两个节点是否表示相同的内容。在判断过程中,React 会先以新节点为基准,在旧节点中查找对应的节点。如果找到了相同的节点,则进行更新;否则,将新节点插入到旧节点中或从旧节点中删除。


在使用 React 进行开发时,我们应该尽量避免使用索引作为 key,因为索引本身并没有表示唯一性,容易造成错误的判断结果和性能问题。相反,我们应该在数据中为每个元素提供一个唯一的标识符,例如数据库中的 ID 或者全局唯一的 UUID。


需要注意的是,虽然虚拟 DOM 可以有效地降低浏览器对真实 DOM 的操作次数,但也会带来一些额外的开销。例如,在生成和比较虚拟 DOM 树时,需要进行大量的计算和判断,可能会影响应用的整体性能。因此,在实际开发中,我们需要根据具体情况,权衡使用虚拟 DOM 的益处和代价,选择最适合自己应用的方案。


## 35.react新出来两个钩子函数是什么?和删掉的will系列有什么区别?(2)


react新出的哪两个钩子函数? 新出的getDerivedStateFromProps 与 getSnapshotBeforeUpdate 两个钩子。 少了componentWillMount,componentWillReceiveProps与componentWillUpdate三个都带有will的钩子。 1、getDerivedStateFromProps 这个钩子的作用其实就是从props获取衍生的state。getDerivedStateFromProps中返回一个对象用于更新当前组件的state,而不是直接取代。 2、getSnapshotBeforeUpdate 这个钩子的意思其实就是再组件更新前获取快照,此方法一般是结合componentDidUpdate使用,getSnapshotBeforeUpdate中返回的值将作为第三参数传递给componentDidUpdate。 为什么删掉will? 三个钩子函数是被废弃,但是不是直接不能用了,而是官方会给出警告并推荐我们在这三个钩子前添加UNSAFE\_前缀。 为什么被废弃呢,因为使用率并不太高,这三个钩子很容易被误解和滥用,这几个钩子不稳定。


## 36.React的props.children使用map函数来遍历会收到异常显示,为什么?应该 如何遍历?


使用 `map` 函数遍历 React 组件的 `props.children` 时,可能会遇到异常显示的问题。这是因为 `props.children` 在不同情况下的类 型不同,导致 `map` 函数操作的方式也不同。


解决方法:


1.首先检查 `props.children` 的类型。


2.如果它是一个数组,那么可以直接使用 `map` 函数进行遍历。


3.如果它只有一个子元素,需要先将它转换为数组,然后再进行遍历


## 37.ts中抽象类的理解?


在 TypeScript 中,抽象类是一种特殊的类,用于作为其他类的基类或父类,并且不能直接实例化。抽象类主要用于定义一组通用的属性和方法,然后让其他类继承并实现这些抽象类中定义的方法。


抽象类通过使用 `abstract` 关键字进行声明。抽象类可以包含抽象方法、普通方法和属性。


抽象类的主要特点包括:


* 不能直接实例化,只能被继承。
* 可以包含抽象方法、普通方法和属性。
* 抽象方法必须在派生类中实现。
* 子类可以覆盖或扩展抽象类中的方法和属性。


## 38.redux本来是同步的,为什么它能执行异步代码?实现原理是什么?中间件的实现原理是什么?


Redux 是一个使用单一数据源和不可变数据的状态管理库,它本身是同步的,但通过中间件可以实现异步操作。Redux 中最常用的异步中间件是 Redux Thunk、Redux Saga 和 Redux Observable。


具体来说,当我们需要在 Redux 中执行异步代码时,通常会使用如下流程:


1. 在 View 中触发一个异步 Action。
2. Action 发送到 Reducer,Reducer 更新 Store 中的状态。
3. 如果需要执行异步操作,Middleware 捕获到这个 Action,并执行异步代码。
4. 异步代码完成后, Middleware 发送新的 Action 到 Reducer,Reducer 再次更新 Store 中的状态。


实现原理是中间件利用了 Redux 提供的 action 和 reducer 的单向数据流机制,使得 action 可以被拦截并且异步处理后再次派发出去。中间件本质上是对 dispatch 函数的重写,并且它可以执行某些操作,例如异步处理、日志记录、错误报告等。


中间件的原理是 Redux 通过在 dispatch 中间注入中间件的执行代码,在 action 到达 reducer 之前对 action 进行了修改或者是对应的 side effect 操作。具体来说,每个中间件都是一个函数,它接收 store 的 dispatch 和 getState 函数作为参数,返回一个新的函数,这个函数被用来包装 action creator,在 dispatch 前后进行操作。这种方式支持链式调用多个中间件,以便进行不同操作,例如数据处理、异步调用、日志记录、错误报告等。


## 39.redux中同步action与异步action最大的区别是什么?


1.同步 Action: 同步 action 是指在触发后立即执行并完成的 action。具体表现为,在 Redux 中通过 dispatch 触发同步 action 后,它 会立即被发送到 reducer 进行状态更新


2.异步 Action: 异步 action 是指在触发后需要一定时间进行处理的 action,通常用于处理异步操作,比如网络请求、定时器等。异步 action 通常会触发多个相关的同步 action,以表示异步操作的不同阶段



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


Redux Saga 和 Redux Thunk 都是用于处理异步操作的 Redux 中间件,它们在实现和使用上有一些区别,适用于不同的场景。


1. 区别:


* 实现方式:Redux Thunk 是一个函数,允许我们在 Action Creator 中返回一个函数,这个函数可以进行异步操作并手动调用 dispatch。而 Redux Saga 则基于 ES6 的 Generator 函数,通过使用特定的语法来处理异步操作。
* 控制流程:Redux Thunk 使用简单的回调函数来处理异步操作,通常是通过链式调用多个 Action,从而实现异步流程控制。而 Redux Saga 则使用生成器来定义和控制异步操作的流程,通过监听和响应不同的 Action 来执行相应的操作。
* 异常处理:Redux Thunk 需要手动处理错误,通过 try-catch 捕获异常,并在回调函数中 dispatch 错误信息。而 Redux Saga 具备异常捕获和处理的能力,在 Generator 函数内部可以使用 try-catch 捕获异常,并且可以派发不同的 Action 处理异常情况。


1. 使用场景:


* Redux Thunk 适用于简单的异步场景,例如发起一个 AJAX 请求并在请求成功后更新状态。它的学习曲线比较低,容易上手,适合于对异步处理需求不复杂的项目。
* Redux Saga 适用于复杂的异步场景,例如需要处理多个连续的异步操作、任务取消、并发请求等。它提供了更强大和灵活的异步处理能力,并且通过 Generator 函数的形式使得异步流程易于阅读和维护。但是相对复杂性也较高,需要掌握 Generator 函数和 Saga 相关的代码结构。


总而言之,Redux Thunk 适用于简单的异步操作,学习曲线较低;而 Redux Saga 适用于复杂的异步场景,提供了更强大和灵活的异步处理能力。选择哪个中间件取决于项目的具体需求和开发团队的技术背景。


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


在 Redux 中,为了避免定义的 action type 常量重复,可以采用以下几种方式:


1. 使用字符串常量:定义 action type 时使用字符串常量,在不同的模块或文件中使用不同的命名空间来确保唯一性。
2. 使用枚举类型:使用 TypeScript 的枚举类型来定义 action type,枚举成员的名称是唯一的
3. 使用工具库:可以使用一些辅助工具库来帮助管理和生成唯一的 action type,例如 `redux-act`、`redux-actions` 等。


## 42.CDN的特点及意义?


CDN(Content Delivery Network,内容分发网络)是一种分布式的网络架构,其目标是通过将内容缓存在离用户较近的边缘节点上,提供更快速、稳定的内容分发服务。CDN的特点和意义如下:


1. 高效快速:CDN利用分布在全球各地的边缘节点,将内容缓存到离用户最近的节点上,当用户请求内容时,可以从最近的节点获取,减少了传输距离和网络拥堵,提供了更快的响应速度和较低的延迟。
2. 负载均衡:CDN通过智能路由和负载均衡技术,将用户请求分配到最优的节点,避免了单一服务器的过载,提高了整体的系统吞吐量和可用性。
3. 提升用户体验:由于CDN提供了更快的响应速度和较低的延迟,用户可以更快速地访问和加载网页、图片、视频等内容,提升了用户的体验感受,降低了用户的等待时间。
4. 减轻源站压力:通过将静态内容缓存到CDN节点上,可以减轻源站的负载压力,提高源站的可扩展性和稳定性,同时减少了源站与用户之间的直接流量,降低了网络成本。
5. 抵御网络攻击:CDN可以通过分布式的架构和安全防护机制,提供一定程度的网络攻击防护,如分布式拒绝服务攻击(DDoS)的缓解。
6. 全球覆盖:CDN网络分布在全球各地的节点,可以实现全球范围内的内容分发,无论用户身处何地,都可以享受到较快速的网络访问。


综上所述,CDN具有高效快速、负载均衡、提升用户体验、减轻源站压力、抵御网络攻击和全球覆盖等特点。在大规模的网络应用中,使用CDN能够显著提升网站性能、可靠性和安全性,为用户提供更好的体验,并降低运营成本。


## 43.为什么for循环比forEach性能高


在 JavaScript 中,常用的循环有 for 循环和 forEach 循环。虽然两者都可以遍历数组,但它们的实现方式不同,因此性能也有所不同。


for 循环是一种基于索引值(或下标)的循环方式,通过数组的下标索引来访问数组元素。而 forEach 循环则是一种迭代器,对数组中的每个元素都执行一次回调函数。


因此,for 循环相对于 forEach 循环具有以下优势:


1. for 循环不需要编写额外的函数,可以直接对数组进行操作,因此其执行过程相对更加高效。
2. 在 for 循环中,可以通过定义一个本地变量(如`var len = arr.length`)将数组长度缓存起来,避免多次访问 `arr.length` 属性导致的性能损失。
3. 在需要对数组进行修改时,for 循环比 forEach 循环更为方便且高效。因为在 for 循环中,可以通过获取数组的下标索引来修改数组元素,而在 forEach 循环中无法直接修改数组元素。


总体而言,在大多数情况下,for 循环比 forEach 循环更具有优势。但是,对于需要进行异步操作的情况,forEach 循环可能更为适用,因为它可以支持 `async/await` 操作,而 for 循环不支持。


需要注意的是,虽然 for 循环比 forEach 循环更快,但在实际应用中,在性能方面的差别通常不会太大。因此,选择使用哪种循环方式应该根据实际情况而定,以符合代码的可读性、可维护性和执行效率等方面的要求。


## 44.redux实现原理?


Redux 是一个 JavaScript 状态管理库,它可以用于管理应用程序中的状态。Redux 的实现原理可以简单概括为以下几个步骤:


创建 store:创建一个全局的存储对象作为状态管理的中心。Store 由 Redux 提供,它包含应用程序的当前状态,还包括实现更新状态的 reducer。


创建 reducer:reducer 是纯函数,它接收旧的 state 和 action,返回新的 state。Reducer 的主要作用是根据 action 的类型来更新 state,同时保证 state 的不可变性。


创建 action:action 是一个普通的 JavaScript 对象,它描述了要发生的操作。Action 包含一个 type 属性,表示要执行的行动类型,还可以包含其他数据。


分发 action:通过调用 store.dispatch() 方法将 action 分发到 reducer,这个过程会触发 state 的更新。


更新 state:reducer 根据接收的 action 类型更新 store 中的 state,同时返回一个新的 state。


订阅 state 的变化:可以通过 store.subscribe() 方法监听 state 的变化,在 state 更新时执行相应的操作。


## 45.React render方法的原理,在什么时候会触发?(2)


`render()` 方法的原理:


根据组件的状态和属性(props)来生成对应的虚拟 DOM 树。React 使用虚拟 DOM 来表示组件的界面结构,并通过比较更新前后 的虚拟 DOM 树找出差异,然后将差异部分进行高效地更新到真实的 DOM 上,从而实现页面的局部更新


`render()` 方法会在以下情况下触发:


1.组件首次挂载:当组件第一次被渲染到真实的 DOM 上时,`render()` 方法会被调用


2.组件的状态或属性发生变化:当组件的状态(通过 `setState()` 方法更新)或属性发生变化时,React 会自动重新调用 `render()` 方法,生成新的虚拟 DOM,并进行比较和更新


3.父组件重新渲染:如果父组件的 `render()` 方法被调用,那么其中包含的子组件的 `render()` 方法也会被触发


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


对于移动端适配,一种常用的方法是响应式布局(Responsive Layout)和媒体查询(Media Queries)。以下是一些常见的移动端适配策略:


1. 使用Viewport meta标签:在HTML文档的头部添加Viewport meta标签,通过设置`width=device-width`,可以告诉浏览器使用设备的宽度作为页面的宽度。另外,还可以设置`initial-scale`、`maximum-scale`和`minimum-scale`等属性来控制缩放行为。
2. 使用相对单位:在样式表中使用相对单位(如百分比、em、rem)来定义元素的尺寸和布局,相对单位可以根据屏幕尺寸进行适配。
3. 弹性布局:使用弹性盒模型(Flexbox)或网格布局(Grid)等弹性布局方式,使得元素能够根据可用空间自动调整大小和位置。
4. 媒体查询:利用CSS3中的媒体查询功能,根据不同的屏幕尺寸和设备特性,为不同的屏幕宽度范围应用不同的样式规则。可以根据需要调整字体大小、布局结构、隐藏或显示某些元素等。
5. 图片适配:通过设置图片的最大宽度为100%来保证图片在不同尺寸的屏幕上自适应。同时,使用高分辨率的图片(如Retina屏幕),以提供更清晰的图像。
6. 测试和调试:在开发过程中,使用模拟器或真实设备进行测试,并通过调试工具(如Chrome开发者工具)来检查和优化页面在不同屏幕大小下的样式和布局。


综合使用以上方法,可以使网页在不同尺寸的移动设备上呈现出良好的用户体验,适应不同的屏幕大小和设备特性。


当然,对于较为复杂的移动端适配需求,可以考虑使用CSS预处理器(如Sass、Less)或CSS框架(如Bootstrap、Foundation)等工具来简化和优化适配过程。


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


在移动端开发中,由于不同设备的像素密度差异,1像素问题成为了一个常见的难题。如果我们不对这个问题进行针对性的解决,那么会导致页面显示效果不美观,甚至影响用户体验。


以下是一些解决方案:


1. 使用css3的scale属性:将要渲染的元素放大一倍,然后通过scale缩小回去。例如,将一个1像素的边框放大到2像素,再通过scale(0.5)恢复原来大小。这种方法可以使边框看起来更加清晰,但是可能会影响元素的布局和性能。
2. 通过伪元素实现:使用伪元素before或after,并设置其content属性为空,然后通过border设置为1像素粗细的边框。例如:

 

cssCopy Code.box::before {
content: “”;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border: 1px solid #ddd;
-webkit-box-sizing: border-box;
box-sizing: border-box;
z-index: -1;
}

 这种方法可以避免影响元素布局,但是可能会增加HTML代码量和CSS复杂度。
3. 通过JavaScript动态设置viewport缩放比例: 使用JavaScript获取设备物理像素和设备独立像素的比例,然后动态设置viewport的缩放比例,从而实现1像素问题的解决。例如:

 

javascriptCopy Codevar scale = 1 / window.devicePixelRatio;
document.querySelector(‘meta[name=“viewport”]’).setAttribute(‘content’, ‘width=device-width,initial-scale=’ + scale + ‘,maximum-scale=’ + scale + ‘,minimum-scale=’ + scale + ‘,user-scalable=no’);

 这种方法可以根据设备分辨率进行动态适配,但是可能会对页面布局和性能产生影响。
4. 使用第三方库:有一些开源的第三方库可以帮助我们解决1像素问题,例如[border.css]( )、[postcss-1px]( )等。这些库可以通过CSS预处理器或者PostCSS等工具使用。


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


在弹性盒(Flexbox)布局中,存在一种缩放机制,可以通过调整弹性盒子的基准尺寸和剩余空间的分配方式来实现元素的缩放。这个机制称为「Flex的缩放」。


Flex缩放是通过`flex-grow`、`flex-shrink`和`flex-basis`这三个属性来控制的:


1. `flex-grow`属性:确定弹性盒子在剩余空间中分配的比例,它决定了元素在主轴方向上的扩展能力。默认值为0,即不进行扩展。如果多个弹性盒子都设置了`flex-grow`,它们将按照数值比例分配剩余空间。
2. `flex-shrink`属性:定义了元素在空间不足时的收缩能力,即在元素溢出容器时的缩放比例。默认值为1,表示会收缩。如果多个弹性盒子都设置了`flex-shrink`,它们将按照数值比例进行收缩。
3. `flex-basis`属性:指定了弹性盒子在分配多余空间之前的初始尺寸。可以用像素(px)、百分比(%)或者`auto`(根据内容大小自动计算)来设置。默认值为`auto`。


下面是一个示例代码,演示了Flex缩放的效果:



htmlCopy Code

cssCopy Code.container { display: flex; }

.box {
flex-grow: 1; /* 元素扩展比例为1 /
flex-shrink: 1; /
元素收缩比例为1 /
flex-basis: 100px; /
初始尺寸为100px */
background-color: gray;
margin: 10px;
}


在上述示例中,`.box`元素设置了`flex-grow: 1;`,表示在剩余空间中按比例进行扩展。当容器宽度增加时,`.box`元素会根据比例分配到的空间进行水平扩展。当容器宽度减小时,`.box`元素会根据`flex-shrink`属性进行水平收缩。


需要注意的是,Flex缩放只会发生在弹性盒子的主轴方向上。在交叉轴方向上,元素的尺寸由`align-self`属性来控制,默认情况下不进行缩放。


## 49。shouldComponentUpdate有什么作用?


shouldComponentUpdate 是 React 组件的生命周期函数之一,用于控制组件是否需要重新渲染。


当组件的 Props 或 State 发生变化时,React 会自动触发组件的重新渲染,但是在渲染之前,React 会调用 shouldComponentUpdate 方法来判断是否需要重新渲染。如果 shouldComponentUpdate 返回 true,则组件将继续进行重新渲染;如果返回 false,则组件将停止重新渲染,直接使用之前的结果。


shouldComponentUpdate 的作用主要有以下两个方面:


1. 性能优化:在某些情况下,组件可能会因为不必要的重新渲染而浪费大量性能。比如,在组件的某个 Prop 值没有发生变化时,我们可以通过重写 shouldComponentUpdate 方法来告诉 React 直接复用上次的渲染结果,从而避免不必要的计算和渲染,提升应用性能。
2. 控制组件渲染的粒度:有些组件可能包含多个子组件,当 Props 或 State 发生变化时,所有子组件都会被重新渲染,即使对于某些子组件来说,数据并没有发生变化。此时,我们可以通过在父组件中重写 shouldComponentUpdate 来手动控制子组件的重新渲染,从而优化渲染性能和用户体验。


需要注意的是,shouldComponentUpdate 每次在重新渲染之前都会被调用,因此返回 false 可以有效地防止组件重复渲染。


## 50.Vue中自定义指令的理解,应用场景有哪些?


在Vue中,自定义指令是一种扩展Vue功能的方式。它允许你在DOM元素上添加自定义行为。自定义指令可以用于处理特定的DOM操作、事件处理、动画效果等。



下面是一些自定义指令的常见应用场景:



1)操作DOM元素:自定义指令可以用于直接操作DOM元素,例如改变元素的样式、添加/移除类名、设置属性等。



2)表单验证:自定义指令可以用于表单的验证,例如验证输入的内容是否符合规则、验证手机号码、密码强度等。



3)事件处理:自定义指令可以用于处理特定的事件,例如点击元素外部区域关闭弹窗、长按元素触发某个操作等。



4)动画效果:自定义指令可以用于实现动画效果,例如淡入淡出动画、滑动特效等。



5)第三方库的集成:自定义指令可以用于集成第三方库,例如集成日期选择器、拖拽库等。



总的来说,自定义指令可以在需要对DOM元素添加特定行为或功能的场景下发挥作用。通过自定义指令,你可以更加灵活地操作和控制DOM,使你的应用功能更加丰富和可扩展。


## 51.说说Connect组件的原理是什么?


`Connect`组件是`react-redux`库中的一个高阶组件,它用于连接React组件与Redux的状态管理库之间的桥梁。`Connect`组件的原理如下:



1)`Connect`组件通过一个函数来生成一个新的代理组件。这个函数接收两个参数:`mapStateToProps`和`mapDispatchToProps`。



2)`mapStateToProps`函数定义了如何从Redux的状态树中取出组件所需的状态数据,并将其以`props`的形式传递给组件。



3)`mapDispatchToProps`函数定义了如何将Redux的指令(actions)以`props`的形式传递给组件,并提供了一种将指令与Redux的`dispatch`方法绑定的方式。



4)当Redux的状态发生改变时,`Connect`组件会重新计算和比较`mapStateToProps`返回的结果,如果有变化,就会重新渲染组件。



5)在代理组件中,`Connect`组件会订阅Redux的状态变化,使得在状态更新时能够触发重新渲染,并将更新后的状态数据通过`props`传递给包装的React组件。



通过以上的流程,`Connect`组件实现了将Redux的状态和指令与React组件进行绑定,使得React组件能够获取并响应Redux的状态变化,同时也能够触发Redux的指令来改变状态。



需要注意的是,`Connect`组件还有一些优化机制,比如对状态的浅比较以减少不必要的重新渲染、对指令和状态的批量处理等,这些机制都可以提升应用的性能和效率。


## 52.谈谈你对BFC的理解?


理解:


BFC(Block Formatting Context)是指块级格式化上下文,在 CSS 布局中起着重要的作用。BFC 是一个独立的渲染区域,其中的元素 按照一定的规则进行布局,它可以影响到元素的大小、位置等属性。


下面是 BFC 的一些特性:


1. BFC 内部的元素会在垂直方向上一个接一个地排列,元素之间的间隔由 margin 属性决定,不会发生重叠现象。
2. 在同一个 BFC 中的两个相邻元素的 margin 会发生折叠(margin collapsing)现象,即两个相邻元素的 margin 只会取其中较大的一个值。
3. BFC 区域不会与浮动元素重叠,浮动元素只会影响到 BFC 环境中没有成为 BFC 的元素。
4. BFC 可以防止元素被父级或者兄弟元素的 float 影响而产生错位现象。
5. BFC 中的元素可以包含浮动元素,并且不会发生高度塌陷。


## 53.最少说出三种前端清除浮动的方法?


1.使用空元素清除浮动:


优点:


简单易懂,容易实现


缺点:


无法添加额外得HTML元素


无法清除浮动元素之外的其他内容,可能会影响布局


2.使用 overflow 属性清除浮动:


优点:


不需要添加额外的 HTML 元素


缺点:


当浮动元素超出父元素范围时,可能会被裁剪


只适用于包含块具有确定高度的情况


3.使用 clearfix 类清除浮动(使用伪类)


优点:


不需要添加额外的 HTML 元素


兼容性较好


缺点:


有时需要设置 display: table,可能会对部分布局产生影响


## 54.什么是强缓存和协商缓存?


强缓存(Strong Caching):


强缓存是指在缓存过期前,浏览器直接从本地缓存中加载资源,而不需要向服务器发送请求。这是因为服务器在响应资源请求时,在响 应头中设置了合适的缓存策略(例如 Cache-Control 和 Expires),告诉浏览器可以在一段时间内直接使用缓存的资源。


协商缓存(Conditional Caching):


协商缓存是指在缓存过期后,浏览器发送一个请求到服务器,检查本地缓存的资源是否仍然有效。这是通过在请求头中包含一个条件标 签来实现的。如果服务器判断资源未发生变化,它会返回一个 304 Not Modified 响应,告诉浏览器可以继续使用本地缓存


## 55.说说React jsx转换成真实DOM的过程?


\1. 解析JSX:React会使用解析器(如Babel)将JSX代码解析成JavaScript对象。这个对象被称为虚拟DOM(Virtual DOM)或元素(Element)。


\2. 创建虚拟DOM树:React会根据解析得到的虚拟DOM对象,递归地创建一个虚拟DOM树。该树结构与真实DOM树相对应,但它是JavaScript对象的表示形式。


\3. Diff算法比较:当组件状态发生变化时,React会将新的虚拟DOM树与之前保存的旧的虚拟DOM树进行比较,使用Diff算法(如Real Diff算法)找出差异。


\4. 更新差异节点:React会根据找到的差异,进行相应的DOM操作来更新真实的DOM。这些DOM操作可以包括插入、移动或删除DOM节点。


\5. 触发生命周期方法:如果组件需要进行更新,React会触发相应的生命周期方法,如`shouldComponentUpdate`、`componentWillUpdate`和`componentDidUpdate`等,以便组件可以根据需要执行自定义的逻辑。


\6. 应用更新:经过组件更新后,React会将变化应用到页面上,通过DOM操作将修改后的虚拟DOM转化为真实的DOM。React会尽可能高效地计算出需要进行的最小化DOM操作,以提高性能。


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


`@reduxjs/toolkit` 是 Redux 官方团队提供的一个工具包,旨在简化和加速 Redux 应用程序的开发。它提供了一组实用工具和约定,使得编写 Redux 代码更加简洁、可读性更高,并且减少了样板代码的量。



区别:



1.相比传统的 Redux 开发方式,使用 `@reduxjs/toolkit` 可以大大简化 Redux 相关的代码,提升开发效率,减少出错机会



2.`@reduxjs/toolkit` 是一个针对 Redux 的开发工具包,提供了一系列简化 Redux 开发的工具和约定;而 `react-redux` 则是一个用 于在 React 应用中使用 Redux 的库,提供了与 React 结合的相关 API。



3.`@reduxjs/toolkit` 可以简化 Redux 的使用,而 `react-redux` 则让我们更方便地在 React 组件中使用 Redux




**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/06dc4fe2df3b51020cb54ca544634adc.jpeg)
![img](https://img-blog.csdnimg.cn/img_convert/ff4a553e453b640e1b8fc6e04b756a8e.png)
![img](https://img-blog.csdnimg.cn/img_convert/062e28e92ea16dd9d3949b1b01b41473.png)
![img](https://img-blog.csdnimg.cn/img_convert/d12db92a7c70f85bf633daddd989a015.png)
![img](https://img-blog.csdnimg.cn/img_convert/06dffbe65f27eac347bdea5ce5dcd526.png)
![img](https://img-blog.csdnimg.cn/img_convert/b584fd48266b70975d063b54b1d2c894.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!**

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)**
![img](https://img-blog.csdnimg.cn/img_convert/2ab03e63663749254bacb87daa6d6ebe.png)



### 最后

由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!



![](https://img-blog.csdnimg.cn/img_convert/28b785b3d636ade8961373261061d145.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/9924702e2a8d3b4978d22371f769fa65.webp?x-oss-process=image/format,png)



**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
![img](https://img-blog.csdnimg.cn/img_convert/61caeeef977f1b9624f61c2679f62809.png)

入阿里一直到现在。**

**深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中...(img-0HPB8zFT-1712940702598)]
[外链图片转存中...(img-JTHIP8Wi-1712940702599)]
[外链图片转存中...(img-LSgsNWLM-1712940702600)]
[外链图片转存中...(img-7qBwFs1c-1712940702600)]
[外链图片转存中...(img-3QpxdOPQ-1712940702601)]
[外链图片转存中...(img-e6FGCSWN-1712940702601)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!**

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)**
[外链图片转存中...(img-7jZC5h4K-1712940702602)]



### 最后

由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!



[外链图片转存中...(img-B00EdvIo-1712940702602)]

[外链图片转存中...(img-8il3oJBv-1712940702603)]



**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
[外链图片转存中...(img-P00EsKib-1712940702603)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值