前端面试题(附答案)完善中……

前端面试笔记

前言

这里是关于前端面试的一些题,我整理了一些经常被问到的问题,出现频率比较高的问题,以及个人经历过的问题。如有不足之处,麻烦大家指出,持续更新中…(ps:一到三颗⭐代表重要性,⭐选择性了解,⭐⭐掌握,⭐⭐⭐前端需要知道的知识)
在这里插入图片描述
Enchanted

一、HTML篇

1.语义话的目的是什么?⭐

答:用正确的标签做正确的事。

提高代码的可读性,页面内容结构化,有利于开发和维护,同时提高的用户体验,有利于SEO。

2.HTML5新特征⭐⭐⭐

  1. Canvas绘图以及SVG绘图。
  2. 拖放(Drag and drop)API
  3. 语义化标签(header、nav、footer、article、section)
  4. 音频、视频(audio、video)API
  5. 地理定位(Geolocation)
  6. 本地离线存储(localStorage),长期存储数据,关闭浏览器后不丢失。
  7. 会话储存(sessionStorage),数据在关闭浏览器后自动删除。
  8. 表单控件(calendar、date、time、email、url、search)
  9. 多任务 webworker
  10. 全双工通信协议 websocket
  11. 历史管理 history
  12. 跨窗口通信 PostMessage
  13. Form Data 对象

3.cookie与sessionStorage和localStorage的区别⭐⭐⭐

  1. 保存方式
    cookie存放在客户的浏览器上。
    session都在客户端中保存,不参与服务器通讯。

  2. 生命周期
    cookie可设置失效时间
    localStorage除非手动清除否则永久保存
    sessionStorage关闭当前页面或浏览器后失效

  3. 存储的大小
    cookie 4kb左右
    session 5M

  4. 易用性
    cookie需自己封装
    session可以接受原生接口

因为cookie每次请求都会携带在http请求中,所以它的主要用来识别用户登录,localStorage可以用来跨页面传参,sessionStorage可以用来保留一些临时数据。

关于storage使用的方式可以查看storage传值

二、CSS篇

1.css有哪些基本的选择器,执行先后顺序?⭐⭐

  1. 基础选择器

元素选择器:选择所有指定的HTML元素。

p { color: blue; }  

类选择器:选择具有特定类的元素。

.myClass { color: red; }

ID选择器:选择具有特定ID的单个元素。

#myId { color: green; }
  1. 组合选择器

后代选择器:选择某个元素内部的后代元素。

div p { color: yellow; }

子选择器:选择某个元素直接子级的元素。

ul > li { color: green; }

相邻兄弟选择器: 选择紧接在另一个元素后的元素。

h1 + p { color: red; }

一般兄弟选择器: 选择同一父元素下在指定元素之后的所有元素。

h1 ~ p { color: yellow; }
  1. 属性选择器

具有特定属性的元素:选择具有指定属性的元素。

[type="text"] { border: 1px solid black; }
input[type="email"] { border: 1px solid #ccc; }

包含特定值的属性:选择包含特定值的属性。

[href^="https"] { color: orange; } /* 以"https"开头的href属性 */
  1. 伪类选择器
  • 状态伪类:用于选取元素的不同状态(hover、active、focus、visited、link、checked、disabled、enabled、required、optional)

:hover:选择鼠标悬停时的元素。

a:hover { color: red; }
  • 结构伪类:用于选取文档中特定的元素结构位置(first-child、last-child、nth-child(n)、nth-of-type(n)、first-of-type、last-of-type、only-child、only-of-type)。

:first-child: 选取属于其父元素的第一个子元素

ul li:first-child {
   color: red;
}

:nth-child(n):选择第n个子元素。

li:nth-child(2) { color: blue; }
  • 动态伪类:基于元素的动态状态来应用样式。

:not(selector): 选取不匹配指定选择器的元素。

div:not(.special) {
  background-color: #f0f0f0;
}

:root: 选取文档的根元素,通常是 。

:root {
  --main-color: #000000;
}

:empty: 选取没有子元素的元素(包括文本节点)。

div:empty {
  display: none;
}
  1. 伪元素选择器

::before 和 ::after:在元素内容前或后插入内容。

p::before { content: "UZI"; color: red; }

!important > 内联样式(<div style="color: red;") >ID选择器 > 类选择器 > 标签选择器和伪元素选择器 > 通配符选择器(*)

CSS选择器的优先级(或称为权重)规则用于决定当多个规则适用于同一元素时,哪个规则将最终应用。
优先级计算规则
CSS优先级是根据选择器的类型来计算的,优先级值由四个部分组成:a、b、c、d。这些值分别代表以下内容:

  1. a: 具有 ID 选择器的数量。
  2. b: 具有类选择器、属性选择器和伪类选择器的数量。
  3. c: 具有元素选择器和伪元素选择器的数量。
  4. d: 通配符选择器 *、类型选择器和伪元素选择器的数量(在某些CSS实现中,可能仅表示元素选择器)。

优先级计算过程

  1. ID选择器:每个ID选择器增加一个点数。例如,#header的优先级是(1,0,0,0)。
  2. 类选择器、属性选择器和伪类选择器:每个增加一个点数。例如,.button的优先级是(0,1,0,0)。
  3. 元素选择器和伪元素选择器:每个增加一个点数。例如,p的优先级是(0,0,1,0)。
  4. 内联样式:如果样式是直接在HTML元素上用style属性定义的(例如:
    ),其优先级最高,为(1,0,0,0)。

计算优先级的步骤

  1. 计算每个选择器的优先级值
    #header → (1,0,0,0)
    .button → (0,1,0,0)
    p → (0,0,1,0)
  2. 比较选择器的优先级
    如果选择器具有相同的优先级,后定义的规则会覆盖先定义的规则(即后出现的规则优先)。

2.垂直居中DIV⭐⭐⭐

请看这里前端CSS布局问题

3.两栏布局左边固定右边自适应⭐

请看这里前端CSS布局问题

3.三栏布局左右固定中自适应⭐

请看这里前端CSS布局问题

4.常用的块与行属性内标签有哪些?有什么特征⭐⭐

块标签:div、h1~h6、ul、li、table、p、br、form。
特征:独占一行,换行显示,可以设置宽高,可以嵌套块和行
设置margin时
相邻的块级元素之间的垂直外边距会合并,即如果两个块级元素相邻,它们的margin会合并较大的一个值,而不是简单相加(塌陷)。
行标签:span、a、img、textarea、select、option、input。
特征:只有在行内显示,内容撑开宽、高,不可以设置宽、高(img、input、textarea等除外)。
设置margin时
上下外边距不会生效
左右外边距有效

5.清除浮动⭐⭐⭐

  1. clearfix 伪元素法(推荐)
	.clearfix::after {
		content: "";
		display: block;
		clear: both;
	}
	<div class="parent clearfix">
	  <div class="float-left"></div>
	  <div class="float-right"></div>
	</div>

优点: ✅ 无额外空标签 ✅ 兼容性好(支持 IE8+) ✅ 现代主流解决方案

  1. 空 div 法
	<div class="parent">
	  <div class="float-left"></div>
	  <div class="float-right"></div>
	  <div style="clear: both;"></div>
	</div>

优点: ✅ 实现简单
❌ 缺点:增加无意义空标签

  1. 父元素设置 overflow 属性
	.parent {
	  overflow: hidden; /* 或 auto */
	}

优点: ✅ 代码简洁
❌ 缺点:可能隐藏溢出内容或触发滚动条

  1. 父元素浮动
	.parent {
	  float: left; /* 或 right */
	  width: 100%;
	} 

优点: ✅ 简单直接
❌ 缺点:可能引发新的浮动问题

  1. display: table
.parent {
  display: table;
  clear: both;
}

优点: ✅ 无副作用
❌ 缺点:改变盒模型性质

6.CSS3新特征⭐⭐⭐

  1. 圆角(border-radius)
  2. 阴影(box-shadow)
  3. 文字特效(text-shadow)
  4. 线性渐变(gradient)
  5. 变换(transform)
  6. 更多的CSS选择器
  7. 更多背景设置(background)
  8. 色彩模式(rgba)
  9. 伪元素(::selection)
  10. 媒体查询(@media)
  11. 多栏布局(column)
  12. 图片边框(border-image)

7.介绍一下盒模型⭐⭐

  1. 盒模型由内容(content)内边距(padding)边框(border)、**外边距(margin)**组成。
  2. 盒模型分为IE盒模型和W3C标准盒模型。
  3. 标准盒模型(content-box,默认)
    总宽度 = width + padding + border + margin
    总高度 = height + padding + border + margin
  4. (IE)盒模型(border-box)
    总宽度 = width(已含 padding 和 border) + margin
    总高度 = height(已含 padding 和 border) + margin
	.box {
	  width: 200px;
	  padding: 20px;
	  border: 2px solid red;
	  margin: 10px;
	}

标准盒模型宽度:200 (内容) + 20 * 2 (padding) + 2 * 2 (border) + 10 * 2 (margin) = 264px
IE盒模型宽度:200 (内容+padding+border) + 10 * 2 (margin) = 220px

8.CSS中有哪些长度单位?⭐

  1. 绝对长度单位:px
  2. 根据父元素的大小来计算: %
  3. 相对父元素字体大小单位: em
  4. 相对于根元素字体大小的单位: rem
  5. 相对于视口*宽度的百分比(100vw即视窗宽度的100%): vw
  6. 相对于视口*高度的百分比(100vh即视窗高度的100%): vh
  7. 网格布局剩余空间分配比例: fr

9.display:none和visibility:hidden的区别⭐

display:nonevisibility:hidden
完全移除元素空间保留元素占位空间
彻底脱离文档流保留在文档流中
所有子元素不可见(强制)可通过 visibility: visible 显示子元素
不支持过渡动画支持过渡动画
完全移除元素空间保留元素占位空间
触发重排触发重绘

性能优化:
频繁切换显隐 使用visibility:hidden
初始化隐藏大区块内容 使用display:none
需要保留布局的动画元素 使用visibility:hidden

10. 用CSS 实现长宽为浏览器窗口一半的正方形⭐

  1. 已知父元素宽高用%
                width: 50%;
                padding-top: 50%;
                background-color: red;
  • 用vw
                width: 50vw;
                height: 50vh;
                background-color: red;

11. 用CSS 实现高度为0.5像素的线条⭐

这个可以用 伪类 + transform 来实现

		.line {
		  position: relative;
		}
		.line::after {
		  content: "";
		  position: absolute;
		  left: 0;
		  right: 0;
		  bottom: 0;
		  height: 1px;
		  background: #ddd;
		  transform: scaleY(0.5);
		  transform-origin: 0 0;
		}
		
		/* 安卓设备优化 */
		@media (-webkit-device-pixel-ratio: 1.5) {
		  .line::after {
		    transform: scaleY(0.7);
		  }
		}

12. 用CSS 实现三角形⭐

向上

        width:0;
        height:0;   
        border-left:30px solid transparent;   
        border-right:30px solid transparent;   
        border-bottom:30px solid red;

13. 伪类和伪元素的区别⭐⭐

伪类
在这里插入图片描述
伪元素
在这里插入图片描述
区别

  • 伪类只能使用“”,伪元素既可以使用“:”,也可以使用“::”
  • 伪元素其实相当于伪造了一个元素,伪类没有伪造元素,例如first-child只是给子元素添加样式而已。(本质区别就是是否抽象创造了新元素

13. 重绘和重排是什么?如何避免?⭐⭐

重排:当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。
重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,仅重新绘制受影响的元素,不涉及布局计算。

重排需要重新计算布局树,重绘不需要,重排必定发生重绘,但是涉及到重绘不一定要重排 。涉及到重排对性能的消耗更多一些。

触发重排的方法: 页面初始渲染、添加/删除可见的DOM元素、改变元素位置、改变元素尺寸、改变元素内容、改变元素字体大小、改变浏览器窗口尺寸、设置 style 属性的值等。
避免重排的方式:样式集中改变、使用 absolute 或 fixed 脱离文档流。

触发重绘的方法:修改颜色、背景色、边框颜色、调整透明度、改变文本阴影、隐藏/显示元素(visibility: hidden)

14. gird⭐

详情可以看详细解析gird布局教程

15. flex⭐⭐⭐

详情可以看详细解析flex布局教程

15. 什么是BFC?⭐

全称:Block Formatting Context(块级格式化上下文)
含义:独立的渲染区域,规定了在该区域中,常规流块盒的布局。
创建 BFC:

  • 根元素
  • 浮动元素(元素的 float 不是 none)
  • 绝对定位元素(元素的 position 为 absolute 或 fixed)
  • 行内块元素(元素的 display 为 inline-block)
  • overflow 不为 visible 的块级盒子

BFC 内部的元素布局不受外部影响,即使两个相邻的元素浮动,它们也不会重叠。但是,在同一个 BFC 中的两个元素之间可能会发生盒子重叠问题。
BFC 还具有很多其他特性,例如可以包含浮动元素,防止父元素高度塌陷。常用的解决外边距塌陷的方法也是通过创建 BFC 来实现。

16. 元素显示与隐藏的方式⭐

  1. 使用CSS控制显示与隐藏
 主要使用的属性有 display: none; 和 visibility: hidden;
  1. 使用JS控制显示与隐藏

通过JS动态地控制元素的显示和隐藏,通常结合DOM操作来实现。

// 隐藏元素
document.getElementById('elementId').style.display = 'none'; 
// 显示元素
document.getElementById('elementId').style.display = 'block'; // 或者 'inline', 'inline-block',根据需要选择

通过添加或移除CSS类名来控制元素的显示和隐藏,这种方法结合了CSS和JS的优势,尤其适合于复杂的动态交互。

// 隐藏元素
document.getElementById('elementId').classList.add('hidden');
// 显示元素
document.getElementById('elementId').classList.remove('hidden');
/* CSS中定义隐藏的样式 */
.hidden {
    display: none;
}
  1. CSS动画控制显示与隐藏
.element {
    opacity: 0; 
} 
.element .show {
    opacity: 1;
}

17. 图片懒加载和预加载⭐

图片懒加载仅当图片即将进入用户视野(即将显示在屏幕上)时才加载图片资源,这种方法有助于减少页面初始加载时的资源请求和提升页面加载速度

工作原理
页面初始加载时,图片的 src 属性可以设置为一个占位符(如一个小尺寸的透明图片)或者空字符串。
当用户滚动页面时,监测图片是否进入了视口(浏览器可见区域)。
当图片即将进入视口时,动态设置图片的 src 属性为实际的图片路径,从而触发图片的加载。

优点
减少了页面初始加载时的资源请求,提升了页面的加载速度和用户体验。
节省了带宽,特别是对于长页面或者包含大量图片的页面效果更为显著。
可以有效地管理和控制页面上的图片资源加载,优化网络性能。

注意事项
对于SEO(搜索引擎优化),确保搜索引擎能够正确索引页面上的图片内容。
考虑兼容性,尽量使用现代浏览器支持的 IntersectionObserver API 等来实现懒加载效果。

实现demo

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lazy Loading Demo</title>
<style> 
  .placeholder {
    background-color: #f0f0f0;
    width: 100%;
    height: 300px; /* 或者根据图片尺寸设定高度 */
  }
</style>
</head>
<body>
  <div class="container">
    <div class="image-wrapper">
      <div class="placeholder"></div>
      <img class="lazy" data-src="image.jpg" alt="Lazy-loaded image">
    </div>
    <!-- 更多图片 -->
  </div>

<script>
  document.addEventListener("DOMContentLoaded", function() {
    let lazyImages = document.querySelectorAll('.lazy');

    function lazyLoad() {
      lazyImages.forEach(function(image) {
        if (image.getBoundingClientRect().top < window.innerHeight && !image.src) {
          image.src = image.dataset.src;
          image.onload = function() {
            image.classList.add('loaded');
          };
        }
      });
    }

    // 初始加载一次
    lazyLoad();

    // 滚动时加载
    window.addEventListener('scroll', lazyLoad);
  });
</script>
</body>
</html>

图片预加载在需要访问特定图片时能够立即展示,而不需要等待加载时间

工作原理
在页面加载或者在需要的时候,通过 JavaScript 动态创建 元素或者通过 CSS 背景图片的方式,提前加载图片资源。
使用预加载技术,浏览器会优先下载这些图片资源,而不需要用户显式请求加载。

优点
提升用户体验,当用户需要查看图片时,可以立即展示,避免了等待加载的时间。
对于视觉内容较重要的页面或者应用,可以显著减少用户感知的加载延迟。

注意事项
预加载大量的图片资源可能会增加页面的初始加载时间和带宽消耗,需要权衡资源的预加载优先级。
对于移动设备和低带宽环境,预加载需要特别小心,以免影响页面加载性能。

实现demo

const imagesToPreload = [
  'image1.jpg',
  'image2.jpg',
  'image3.jpg'
];

function preloadImages(images) {
  images.forEach(function(imageUrl) {
    const img = new Image();
    img.src = imageUrl;
  });
}

preloadImages(imagesToPreload);

18. link 和 @import的区别⭐

相同点:在css中link@import都可以用来引入外部样式表

区别link@import
语法HTML 标签,写在 head 中CSS 规则,写在 CSS 文件或 style 标签内
兼容性所有浏览器(包括 IE5+)IE5+ 支持,但部分旧版本可能存在问题
性能并行加载,与页面 HTML 同时解析串行加载,需等待当前 CSS 文件解析完成后再加载
阻塞渲染不会阻塞页面渲染可能因加载顺序导致渲染延迟
模块化需手动管理多个文件便于代码拆分
灵活性可动态操作,支持媒体查询静态引入,无法动态修改

三、JS篇

1.ES6新特性?⭐⭐⭐

  1. 新增块级作用域let定义变量和const定义常量 详情可以参考var、let、const的区别
  2. 变量的解构赋值
  3. 模板字符串 (‘${}’)
  4. 默认参数(key=value)
  5. 箭头函数(=>)
  6. 扩展运算符(…)
  7. 模块(import/export)
  8. 类(class/extends)
  9. Promise
  10. Proxy
  11. Symbol
    了解关于es6的更多知识可以看阮一峰——ES6 入门教程

2.ES7、ES8新特性 ⭐

es7

  • 新增了includes
    (includes)数组中是否存在
  • 取幂运算符 **
    base ** exponent
    其中,base表示底数,exponent表示指数。例如,3的4次方可以表示为3 ** 4 = 81。

es8

3.闭包的理解⭐⭐

理解:主要是为了设计私有的方法和变量。
优点:可以避免全局变量造成污染。
缺点:闭包会常驻内存,增加内存使用量,使用不当会造成内存泄漏。
特征:(1)函数嵌套函数。(2)在函数内部可以引用外部的参数和变量。(3)参数和变量不会以垃圾回收机制回收。

4.call()、apply()、bind()的区别⭐

详情请看call()、apply()、bind()重新定义this的区别

5.原型,原型链⭐⭐⭐

原型

基本思想就是通过原型链继承多个引用类型的属性和方法。简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。 ——摘自《javascript高级程序设计(第四版)》

  • 每个 构造函数 都有一个 prototype 属性,指向它的原型对象
  • 每个 实例对象 都有一个 proto 属性,指向构造函数的原型对象
  • 原型对象默认包含 constructor 属性,指向构造函数本身
  • 可以添加公共方法和属性,所有实例共享这些内容
	function Person(name) {
	  this.name = name;
	}
	
	// 构造函数的 prototype 属性
	Person.prototype.sayHi = function() { 
	  console.log(this.name);
	};
	
	const person = new Person('Alice');
	
	// 实例的 __proto__ 指向构造函数的原型
	console.log(person.__proto__ === Person.prototype); // true 
	console.log(Person.prototype.constructor === Person); // true 

原型链
当访问对象属性时,JavaScript 会执行以下查找:

  • 在对象自身属性中查找
  • 如果未找到,则通过 proto 向上层原型查找
  • 持续查找直到 Object.prototype(原型链顶端)
  • 未找到则返回 null
	person.toString(); // "[object Object]" 
	// 查找路径:
	// person → Person.prototype → Object.prototype → null

javascript——原型与原型链

6.JS基本数据类型⭐⭐⭐

  1. 基本数据类型
  • Number:数值,包括整型和浮点型。
  • String:字符型。
  • Undefined:未定义,声明变量时未赋值。
  • Null:定义为空或者不存在。
  • Boolean:布尔值,true or false。
  • Symbol:独一无二的值。
  • BigInt(s11新增) :任意精度的整数。
  1. 引用数据类型
  • Object: 包含了ArrayFunctionDateError等…

基本数据类型是直接存储在中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是存储基 本类型值和执行代码的空间。

引用数据类型是存储在内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆 中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。

两种数据类型的区别:

  1. 堆比栈空间大,栈比堆运行速度快。
  2. 堆内存是无序存储,可以根据引用直接获取。
  3. 基础数据类型比较稳定,而且相对来说占用的内存小。
  4. 引用数据类型大小是动态的,而且是无限的。

注: Object.prototype.toString.call() 适用于所有类型的判断检测

关于Object的方法可以看这个JS中Object方法大全

7.export和export default的区别⭐

  1. 均可导出常量、函数、文件、模块等。
  2. 在一个文件或模块中,export、import可以有多个。export default仅有一个。
  3. 通过export方式导出,在导入时要加{ },export default则不需要。

8.箭头函数和普通函数的区别⭐⭐⭐

  1. 箭头函数中this在定义时就决定,没有自己的this,一般this指向外层第一个普通函数的this,不能通过call、apply、bind来改变其this
  2. 箭头函数不能使用new(不能作为构造函数)
  3. 箭头函数没有原型
  4. 箭头函数没有arguments对象
  5. 语法更加简洁、清晰,=>()
const obj = {
    a: function() {
        console.log(this)
    },
    b:() => {
        console.log(this)
    }
}
obj.a()   //obj
obj.b()   //window

9.GET和POST的区别⭐

表面区别

  • 后退/刷新:GET无害,POST数据会被重新提交。
  • 书签:GET产生的URL地址可以被收藏为书签,而POST不可以。
  • 数据:GET一般是用来获取数据,POST提交数据。
  • 数据类型:GET只允许ASCII字符,POST无限制。
  • 数据大小:GET大小有限制(一般来说1024字节),POST理论上来说没有大小限制。
  • 安全性:GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
  • 可见性:GET参数通过URL传递对所有人可见,POST数据不可见。
  • 历史保留:GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

10.forEach和map的区别⭐⭐

forEach没有返回值,不能链式调用。
map返回新的数组,可以链式调用其他方法。

map创建新数组(不会修改原来数组),forEach不修改原数组。

map:将原数组中的每个元素通过回调函数转换成一个新的元素,然后组成一个新的数组。
forEach将每个元素传递给指定的回调函数,并对每个元素执行特定的操作。

11.JS基本数据类型的比较(==)⭐⭐

黄色表示ture

12.遍历方式for in、for of、forEach的区别⭐

for…in 遍历对象的可枚举属性,包括对象原型链上的属性。它通过遍历对象的键来实现迭代,一般用于遍历对象属性。如果遍历数组则返回的是索引。
注意,使用 for…in 遍历时,还需要使用 hasOwnProperty() 方法来判断属性是否来自对象本身,并避免遍历原型链上的属性。

        let object = { a: 1, b: 2, c: 3 }
        let array = [1, 2, 3, 4, 5]
        for (const key in object) {
            console.log(key);     //a, b, c
        } for (const key in array) {
            console.log(key);     //0, 1, 2, 3, 4
        }

for…of 遍历支持迭代协议的数据结构(数组、字符串、Set、Map 等),而不包括/对象
它通过遍历可迭代的对象的值来实现迭代,一般用于遍历数组、集合等迭代器对象。

    let array = [1, 2, 3, 4, 5]
    for (const key of array) {
        console.log(key); //1, 2, 3, 4, 5
    }
    //如果遍历对象则会报错
    let obj = { a: 1, b: 2, c: 3 }
    for (const item of array) {
        console.log(item); //obj is not iterable
    }

forEach 需要传入一个回调函数,用于对每个元素进行操作。for…in 和 for…of不用。
forEach 不支持 break 和 return 语句跳出循环,如果需要跳出循环可以使用 some() 或 every() 方法。

13.简述一下你理解的面向对象⭐

面向对象是基于万物皆对象这个哲学观点. 把一个对象抽象成类,具体上就是把一个对象的静态特征和动态特征抽象成属性和方法,也就是把一类事物的算法和数据结构封装在一个类之中,程序就是多个对象和互相之间的通信组成的。

面向对象具有封装性,继承性,多态性

封装:隐藏实现细节,使得代码模块化;
继承:扩展已存在的代码模块(类),它们的目的都是为了——代码重用。
多态:相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。多态分为两种,一种是行为多态与对象的多态

14. == 和 ===的区别⭐⭐

==!=:在比较前会进行强制类型转换,再确定操作符是否相等。
规则:

  • 如果是Boolean,将其转换为数值再比较是否相等,false转换为0,true转换为1
  • 如果是String,将其转换为数值
  • 如果是{},则调用对象的**valueOf()**取得其原始值,再比较
  • null == undefined
  • null和undefined不能转换为其他类型值再比较
  • 如果有一个操作数为NaN,则==返回false,!=返回true,NaN不等于NaN
  • 如果都是对象,则比较是不是同一个对象,如果都指向同一个对象,则返回true,否则false。

==只比较不比较类型
=== 会判断类型

 '1' === 1(false)     undefined === null(false)

15. 数组有哪些方法⭐⭐

详细可以看数组一些常用的方法

16. 普通的数组去重⭐

在不涉及去重对象、NaN等情况下。

  1. IndexOf()
  2. 双重for循环
  3. […new Set()]
  4. filter()
  5. sort()

注 :如果有多维数组如 [1,[2],[3,[2,3,4,5]] ] 先扁平化再去重,
Array.flat(Infinity)实现扁平化。

17. Promise⭐⭐⭐

含义:异步编程的一种解决方案,它通过链式调用 then 、 catch 、finally方法来处理异步调用的结果。。
三种状态pending(进行中)、resolved (已成功)和reject(已失败) (Promise对象的状态改变,只有两种可能:从pending变为resolve和从pending变为reject。

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

resolve:将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved)。
reject:将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected)。

Promise 可以通过链式调用的方式,让多个异步请求形成一个顺序执行的队列。

then: Promise 实例添加状态改变时的回调函数。
可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。
catch : 用于指定发生错误时的回调函数。
finally: 不管 Promise 对象最后状态如何,都会执行的操作。

其他方法
Promise.all():将多个 Promise 实例,包装成一个新的 Promise 实例(所有实例都改变状态,值就改变)。
Promise.race():将多个 Promise 实例,包装成一个新的 Promise 实例(有一个实例率先改变状态,值就改变)。

缺点: 无法取消Promise,一旦新建它就会立即执行,无法中途取消。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

更多详情请看Promise 对象

18.promise 和 async/await 的区别 ⭐

Promise 和 async/await 都是用于处理异步任务的方式

相同点:
Promiseasync/await 的目的都是处理异步任务。
Promiseasync/await 都可以避免回调地狱。

不同点:
处理异步调用的结果方法:
Promise 通过链式调用 then 方法和 catch 方法来处理异步调用的结果。
而 async/await 通过 await 关键字和 try…catch 语句来处理异步调用的结果。
异步处理方式
Promise 是一种基于回调函数的异步处理方式。
async/await 是一种基于生成器函数的异步处理方式。
创建Promise方法
Promise 可以直接使用静态方法 Promise.resolve() 和 Promise.reject() 来创建 Promise 对象。
async/await 则需要借助于 Promise 对象创建。
是否可以阻塞
Promise 是非阻塞的。
async/await 是可以阻塞执行的(注意:这里说的阻塞是指异步等待结束后再继续执行后续代码,但不会阻塞线程)。

19.JS中new操作符有什么用?⭐

  1. 创建空对象
  2. 绑定原型链
  3. 绑定 this 指向
  4. 执行构造函数
  5. 返回新对象
	function myNew(Constructor, ...args) {
	  // 1. 创建新对象并绑定原型
	  const obj = Object.create(Constructor.prototype);
	  
	  // 2. 执行构造函数并绑定 this
	  const result = Constructor.apply(obj, args);
	  
	  // 3. 处理返回值
	  return result instanceof Object ? result : obj;
	}
	
	// 使用示例
	function Person(name) {
	  this.name = name;
	}
	const p = myNew(Person, 'Jack');
	console.log(p.name); // "Jack" 

20.JS获取HTML DOM元素的方法⭐

  • 通过ID获取(getElementById)
  • 通过name属性(getElementsByName)
  • 通过标签名(getElementsByTagName)
  • 通过类名(getElementsByClassName)
  • 获取html的方法(document.documentElement)
  • 获取body的方法(document.body)
  • 通过选择器获取一个元素(querySelector)
  • 通过选择器获取一组元素(querySelectorAll)
    用法以及防坑可看JS获取HTML DOM元素的方法

21.事件捕获和事件冒泡⭐⭐

事件冒泡:如果一个元素的事件被触发,那么他的所有父级元素的同名事件也会被依次触发
元素->父元素->body->html->document->window
事件捕获:从最顶级的父元素一级一级往下找子元素触发同名事件,直到触发事件的元素为止

  • 事件捕获是由外向内;而事件冒泡则是由内向外。
  • event.stopPropagation() 可以阻止事件流的进一步传播(冒泡和捕获都能被阻止)。
  • 事件一共有三个阶段:事件的执行顺序 1、捕获阶段 ,2、目标阶段 ,3、冒泡阶段 。
  • 采用事件冒泡(事件委托)的方式,能够节省内存消耗,对于动态改变子元素的时候,也非常有利,避免了很多麻烦的步骤,比如重新绑定事件。(把子元素的事件委托给父元素来处理)
    事件委托举例:
    <ul id="container">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
    </ul>
    <script>
        const container = document.getElementById('container')
        container.addEventListener('click', (e) => { 
            if (e.target.tagName === 'LI') {
               //dosomething…
            }
        });
    </script>

22.以下运行结果是什么?⭐

结果放在23了

  1. ["2","3","4"].map(parseInt) 
  2. [] + {}
  3. {} + []
  4. 1 + {}
  5. [1,2,3] + [4,5,6]
  6. [true,false] + [true,false]
  7. Boolean({}) + []

23. 22题的结果⭐

  1. [2, NaN, NaN]
    //原因:[“2”,“3”,“4”].map(parseInt) 相当于执行的是[parseInt(‘2’,0),parseInt(‘3’,1),parseInt(‘4’,2)]
  2. “[object Object]”
    加法会进行隐式类型转换,规则是调用其 valueOf() 或 toString() 以取得一个非对象的值(primitive value)。如果两个值中的任何一个是字符串,则进行字符串串接,否则进行数字加法。
    [] 和 {} 的 valueOf() 都返回对象自身,所以都会调用 toString(),最后的结果是字符串串接。[].toString() 返回空字符串,({}).toString() 返回“[object Object]”。
    最后的结果就是“[object Object]”
  3. 0
    {} + []相当于+[]语句,也就是相当于强制求出数字值的Number([])运算,相当于Number(“”)运算,最后得出的是0数字
    :所以如果第一个(前面)是{}时,后面加上其他的像数组、数字或字符串,这时候加号运算会直接变为一元正号运算,也就是强制转为数字的运算。
  4. ‘1[object Object]’
    {} 视为一个对象,而不是直接将其强制类型转换。
  5. ‘1,2,34,5,6’
    JS 将数组 [1, 2, 3] 和 [4, 5, 6] 分别转换为它们的字符串表示形式:“1,2,3” 和 “4,5,6”,
    然后,它使用 + 运算符将这两个字符串连接起来,得到最终结果 “1,2,34,5,6”。
  6. ‘true,falsetrue,false’
    同上,依然因为JS 中 + 运算符在数组,会将数组转换成字符串表示,然后连接两个字符串。
  7. “true”
    Boolean({}) 返回 true,[] 是一个空数组,在上下文中被强制转换为字符串时,会变成一个空字符串 “”。
    JavaScript 中,字符串和布尔值相加会将布尔值隐式转换为字符串。因此,true 被转换为字符串 “true”。

24.数组操作方法会改变原数组⭐⭐

会改变:push(),pop(),shift(),unshift() ,splice(),sort(),reverse()。
不变:concat(),split(),slice()。

25.JS有几种方法判断变量的类型?⭐⭐⭐

  1. typeof
    判断基本数据类型,
typeof(null) 'object'   typeof(undefined) 'undefined'  typeof([]) 'object'   typeof({}) 'object'
typeof(0)  'number'     typeof('0') 'string'       typeof(true)  'boolean'   typeof(Symbol()) 'symbol'

对于引用数据类型除了function返回’function‘,其余全部返回’object’。
可以返回类型 number、string、boolean、undefined、object、function、symbol。
2. instanceof
区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类型中的undefined, null, symbol检测不出来。
3. constructor
检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。
4. Object.prototype.toString.call()
适用于所有类型的判断检测,检测方法是Object.prototype.toString.call(数据) 返回的是该数据类型的字符串。(举例:字符串返回的是[object String])

instanceof的实现原理:验证当前类的原型prototype是否会出现在实例的原型链__proto__上,只要在它的原型链上,则结果都为true。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,找到返回true,未找到返回false。
Object.prototype.toString.call原理:Object.prototype.toString 表示一个返回对象类型的字符串,call()方法可以改变this的指向,那么把Object.prototype.toString()方法指向不同的数据类型上面,返回不同的结果

26.如何判断一个对象是否存在?⭐

直接!XXX 这样会报错,因为没有定义
在这里插入图片描述
建议使用typeof运算符,判断XXX是否有定义,如果有定义,则返回"object"。如果不是一个对象,则返回"undefined"。

27.如何判断一个空对象 ?⭐

  • Object.keys()
    Object.keys() 会返回一个包含对象所有可枚举属性名称的数组。如果数组为空,则说明对象为空。
	let obj = {};  // Object.keys(obj).length === 0
	let obj1 = { id:'1' } // Object.keys(obj).length === 1
  • JSON.stringify()
    JSON.stringify() 会将对象转换为JSON字符串。如果字符串为 {},则说明对象为空。
	let obj = {}; // "{}"
	let obj1 = { id:'1' } // '{"id":"1"}' 
  • for…in
    遍历对象的所有可枚举属性。如果循环体没有执行,则说明对象为空。
	let obj = {};  // 没有执行
	let obj1 = { id:'1' }  // id
	for (let key in obj) {
	  console.log(key)
	  break;
	}

28.undefined和null 区别⭐

undefined:一个变量已经声明但未被赋值,或者一个对象属性不存在
null: 一个变量或对象属性被明确地赋值为 null,表示该变量或属性的值为空。

  • undefined 表示缺少值,而 null 表示有一个值,但这个值是空的。
  • typeof检测 null为object, undefined为undefined
  • Number.null转数组为0,undefined转数值为NaN
  • JSON会将undefined对应的key删除,null则不会

以下场景会出现undefined

  • 声明了一个变量,但没有赋值
  • 访问对象上不存在的属性
  • 函数定义了形参,但没有传递实参
  • 使用 void 对表达式求值

29.break 和 return 的区别⭐

作用对象不同
break 用于跳出当前的循环或者 switch 语句。
return 用于结束当前函数并返回一个值。
结束方式不同
break 只是提前结束当前的循环或者 switch 语句,然后继续执行后续代码
return 则会直接结束函数的执行,并将一个指定的值返回给调用处。
使用场景不同
break 通常用于跳出不满足条件或者特定情况的循环结构,避免出现死循环。
return 主要用于结束函数执行,返回一个执行结果或者错误信息等。

30. 排序方式⭐

  • 冒泡排序:比较所有相邻元素,如果第一个比第二个大,则交换它们。(复杂度 O(n^2))
  • 选择排序:找到数组中的最小值,选中它并将其放置在第一位。(复杂度 O(n^2))
  • 插入排序:从第二个数开始往前比,比它大就往后排。(复杂度 O(n^2))
  • 归并排序:把数组劈成两半,再递归地对数组进行“分”操作,直到分成一个个单独的数。(复杂度 O(nlog(n)))
  • 快速排序:从数组中任意选择一个基准,所有比基准小的元素放到基准前面,比基准大的元素放到基准的后面。(复杂度 O(nlog(n)))
  • 计数排序:使用一个用来存储每个元素在原始数组中出现次数的临时数组。在所有元素都计数完成后,临时数组已排好并可迭代以构建排序后的数组。(复杂度 O(n+k))
  • 桶排序:将元素分为不同的桶(较小的数组),再使用简单的排序来对每个桶进行排序,然后将每个桶合并为数组。(复杂度 O(n))
  • 基数排序:根据数字的有效位或基数将整数分布到桶中。基数是基于数组中的记数制。 (复杂度 O(n))在这里插入图片描述

31. ajax 与 axios的区别⭐

相同点:ajax 和 axios 都是用于实现异步 HTTP 请求的技术

  • 实现方式
    ajax : 通过JavaScript 的 XMLHttpRequest(XHR)对象实现浏览器与服务器的异步通信, 需手动封装 XHR 对象,代码较为冗长。
    axios :基于 Promise 的 HTTP 客户端,提供链式调用和更友好的 API,更简洁。
  • 功能扩展
    ajax : 需手动实现高级功能,比如请求/响应拦截等。
    axios :提供完整 API,内置拦截器、取消请求等。
  • 是否支持node?
    ajax : 仅浏览器。
    axios :浏览器 + Node.js。

实现方式

		// 原生 AJAX (XMLHttpRequest)
		const xhr = new XMLHttpRequest();
		xhr.open('GET', '/api/data');
		xhr.onload = function() { 
		  console.log(JSON.parse(xhr.response));
		};
		xhr.send();
		
		// Axios
		axios.get('/api/data')
		  .then(res => console.log(res.data))
		  .catch(error => handleError(error)); 

三、计算机网络与其他知识篇

1.HTTP与HTTPS⭐⭐⭐

HTTP 和 HTTPS 都是网络上传输数据使用的协议。
HTTP:客户端与服务器之间数据传输的格式规范,表示“超文本传输协议”。
HTTPS:在HTTP与TCP之间添加的安全协议层。
默认端口号:HTTP:80,HTTPS:443
传输方式:http是明文传输,https则是具有安全性的ssl加密传输l协议。
连接方式:http的是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
URL:HTTP :http://, HTTPS: https://。

2.HTTPS为什么比HTTP更安全⭐

  1. 加密数据传输
    HTTPS 使用 SSL/TLS 协议对数据进行加密传输。这意味着通过 HTTPS 发送的数据在传输过程中会被加密,第三方无法直接查看或篡改数据内容。而HTTP传输的数据是明文的,容易被窃听和篡改。
  2. 身份验证与数据完整性
    HTTPS 不仅加密了数据,还提供了身份验证和数据完整性验证。通过SSL/TLS证书,客户端可以验证服务器的身份。同时,传输的数据在传输过程中会被检查,以确保它没有被篡改。
  3. 信任与SEO因素
    HTTPS 使用数字证书来验证网站的身份,这增强了用户对网站真实性的信任。此外,现代浏览器对于使用HTTPS的网站给予积极的安全标识,这有助于提升用户体验和信任度。另外,搜索引擎(如Google)也倾向于将使用HTTPS的网站排名更高,这是因为它们认为HTTPS是一个重要的网站安全指标。
  4. 防止窃听和篡改
    HTTPS 的加密机制有效防止了数据在传输过程中被窃听和篡改。尤其是对于用户登录、支付交易等涉及敏感信息的场景,使用HTTPS能够极大地降低信息泄露和被篡改的风险。

总体来说,HTTPS通过加密传输、身份验证和数据完整性保护等多重安全措施,大大提升了网站和用户数据的安全性,使得用户能够更加安全和放心地在互联网上进行数据传输和交流。

3.浏览器缓存机制以及原理⭐⭐

作用: 减少网络请求,提升页面加载速度,降低服务器负载。
核心思想: 通过本地存储资源的副本,有效期内重复使用,减少冗余数据传输。

浏览器缓存分为 强缓存协商缓存

  1. 强缓存
  • 特点: 不向服务器发请求,直接读本地缓存
  • 控制字段:
    Cache-Control :max-age=时间(资源有效期(秒)), no-cache(跳过强缓存), no-store(禁用所有缓存)
    Expires:指定资源的过期时间(绝对时间)
  • 结果: 返回 200
  1. 协商缓存
    特点: 强缓存失效后,请求服务器验证是否更新。
  • 验证方式:
    时间对比: 服务器返回 Last-Modified(服务器返回最后修改时间),浏览器发送 If-Modified-Since(浏览器请求时携带该时间,与当前资源对比),若资源未修改 → 304
    内容对比: 服务器返回 ETag(服务器返回的资源唯一标识,如哈希值) ,浏览器发送 If-None-Match(浏览器携带此哈希值供服务器校验),若一致 → 304
  • 结果:
    资源未变化 → 返回 304 Not Modified(浏览器复用本地缓存)。
    资源已变化 → 返回 200 OK 和新资源。

用户操作影响:

  • 地址栏回车 → 优先强缓存。
  • 普通刷新(F5) → 跳过强缓存,触发协商缓存。
  • 强制刷新(Ctrl+F5) → 忽略所有缓存,重新下载。

Tips:
no-cache 和 no-store 区别? → no-cache 会验缓存,no-store 彻底不缓存

4.HTTP1.0、HTTP1.1、HTTP2.0 的区别⭐

HTTP 不同版本之间主要区别在于性能、功能和安全性方面:

HTTP 1.0
单连接: 每个请求需要建立一个新的连接,这样每发送一个请求都需要消耗时间进行连接建立。
无头信息压缩: 头信息不进行压缩,会增加数据传输量。
安全性较弱: 缺乏安全机制,容易受到攻击。

HTTP 1.1
持久连接: 保持连接,可以减少一次连接建立的时间成本,提高效率。
头部信息压缩: 通过使用压缩算法减少头信息的大小,降低数据传输量。
更完善的状态码: 提供了更多状态码,更加精准地描述请求结果。

HTTP 2.0
多路复用 (Multiplexing): 多个请求可以共享一个连接,并以独立的流进行传输,提高并发性和性能。
头信息压缩: 使用 HPACK 算法进行头信息压缩,更有效地减少数据传输量。
二进制格式: 传输数据采用二进制格式,比文本格式更简洁高效。
服务器推送: 服务器可以主动推送资源给客户端,提高页面的加载速度。

5.TCP与UDP的区别⭐

  1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
  2. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
  3. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  4. TCP首部开销20字节;UDP的首部开销小,只有8个字节。
  5. TCP提供可靠的服务。UDP适用于一次只传少量数据、对可靠要求不高的环境。

6.三次握手四次挥手⭐

原因:TCP是面向连接的,三次握手就是用来建立连接的,四次握手就是用来断开连接的。

关于三次握手先看草图(看图理解)
请添加图片描述

最开始客户端和服务端都处于关闭状态,客户端主动打开连接。
第一次握手:客户端向服务端发送SYN报文,并处于SYN_SENT状态。
第二次握手:服务端收到SYN后应答,并返回SYN+ACK报文,表示已收到,并处于SYN_RECEIVE状态。
第三次握手:客户端收到服务端报文后,再像服务端发送ACK包表示确认。此时双方进入ESTABLISHED状态。(这次报文可以携带数据

为什么不是两次握手
原因:是因为如果只有两次,在服务端收到SYN后,向客户端返回一个ACK确认就进入ESTABLISHED状态,万一这个请求中间遇到网络情况丢失而没有传给客户端,客户端一直是等待状态,后面服务端发送的信息客户端也接受不到了,所以要客户端再次确认。

关于四次挥手先看草图(看图理解)
请添加图片描述
最开始客户端和服务端都处于ESTABLISHED状态,客户端主动关闭连接。
第一次挥手:客户端准备关闭连接,向服务端发送一个FIN报文,进入FIN_WAIT1状态。
第二次挥手:服务端收到后,向客户端发送ACK确认报文,进入CLOSE_WAIT状态,
第三次挥手:客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。等待服务端处理完数据后,向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
第四次挥手:客户端收到FIN+ACK包后,再向服务端发送ACK包,
等待两个周期后再关闭连接。服务器收到了 ACK 应答报文后,关闭连接。

客户端在要等2周期再关闭
为的是确认服务器端是否收到客户端发出的 ACK 确认报文,当客户端发出最后的 ACK 确认报文时,并不能确定服务器端能够收到该段报文。服务端在没收收到ACK报文之前,会不停的重复发送FIN包而不关闭,所以得等待两个周期。

7.HTTP常见的状态码⭐

HTTP常见的状态码

8.HTTP 传输过程⭐

含义:从建立连接到断开连接一共七个步骤,就是三次握手四次挥手

  1. TCP 建立连接
  2. 浏览器发送请求命令
  3. 浏览器发送请求头
  4. 服务器应答
  5. 服务器回应信息
  6. 服务器发送数据
  7. 断开TCP连接

9.如何解决跨域⭐⭐⭐

什么是跨域?
跨域是因为浏览器的同源策略(Same Origin Policy)限制导致的。
浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域
常见的:
1、JSONP跨域
JSONP 可以跨域传递数据,基本原理是通过前端动态创建一个 <script> 标签,其中的 src 属性指向一个跨域 API 的 URL,该 URL 带有一个参数 callback,跨域 API 返回一段特定格式的 JavaScript 代码,其中 callback 函数的参数就是前端传回去的数据,前端获得结果后可以在本地执行回调函数。

2、跨域资源共享(CORS)
服务器端设置 HTTP 响应头部,使得浏览器可以跨域访问,服务器返回的响应头中包含 Access-Control-Allow-Origin 字段,指定可访问该资源的域名白名单,浏览器在接收响应时自动识别该字段判断该响应是否可跨域访问。

3、代理跨域 API 请求
使用自己的后端服务器作为代理服务器,把 API 请求发给服务器,服务器将请求转发到目标域名 API 并从服务器返回数据给前端,此时,前端 AJAX 请求的就是同域的 API,不存在跨域请求的问题了。

4、WebSocket协议跨域
基于 WebSocket 协议进行实时双向数据传输,但是需要服务端和客户端同时支持该技术。WebSocket 允许跨域使用。
5、proxy
前端配置一个代理服务器(proxy)代替浏览器去发送请求:因为服务器与服务器之间是可以通信的不受同源策略的影响。
所有的请求都发送到同一个本地后端 API,后端服务器再将请求转发到真正的目标域名服务器上。

10.网页从输入url到页面加载发生了什么⭐⭐

  1. DNS解析
    浏览器通过 DNS 将域名转换为 IP 地址
  2. TCP连接
    通过 TCP 三次握手与服务器建立连接
  3. 发送HTTP请求
    浏览器发送 HTTP 请求(包含请求头、方法、URL 路径等)
  4. 服务器处理请求并返回HTTP报文
    服务器返回 HTTP 响应(状态码、响应头、响应体)
  5. 浏览器解析并渲染页面
    HTML/CSS 解析 → DOM/CSSOM 树构建 → 渲染树生成 → 页面布局 → 绘制。
  6. 资源加载
    加载 JS、图片等资源

11.浏览器渲染原理?⭐⭐

含义: 从接收 HTML、CSS 和 JavaScript,到最终显示页面内容。

  1. 解析
    浏览器将 HTML 文档解析为 DOM(文档对象模型)树,
    将CSS文件解析为 CSSOM(CSS 对象模型)树。
  2. 构建渲染树
    浏览器将 DOM 和 CSSOM 结合,构建渲染树(Render Tree) 。
  3. 布局
    浏览器根据渲染树中的元素的样式信息计算每个元素的位置和尺寸,
  4. 绘制
    浏览器将渲染树中的每个元素绘制到屏幕上。
  5. 合成
    如果页面包含多个图层(例如,使用 CSS3 动画或变换),浏览器会将这些图层合成到一起,以形成最终的页面图像。

注:
浏览器会执行 JavaScript 脚本,这可能会修改 DOM 或 CSSOM,导致重新布局或重新绘制。
在JS操作后,可能会触发重排(Reflow)或重绘(Repaint),重新计算布局或重新绘制部分内容。

12.深拷贝,浅拷贝⭐⭐⭐

浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

总而言之,浅拷贝改动拷贝的数组原数组也会变(慎用!项目中很多地方共用的数组都会变)。深拷贝修改新数组不会改到原数组。
实现方法
浅拷贝

  1. Object.assign()
  2. 函数库lodash的 _.clone 方法
  3. es6的扩展运算符 (只能浅拷贝数组或对象中的引用类型的元素)
  4. Array.prototype.concat()
  5. Array.prototype.slice()
            let arr=[{name:"uzi"}]
 -             let arr1= Object.assign({}, arr);   arr1[0].name="xiaoming"
 -             let arr2= _.clone(arr);              arr2[0].name="mlxg"
 -             let arr4 = arr.concat()              arr4[0].name="zitai"
 -             let arr5 = arr.slice();              arr5[0].name="clearLove"
               console.log(arr[0].name==arr[1].name==arr[2].name==……);
               //true  arr[0].name="clearLove"

深拷贝

  • JSON.parse(JSON.stringify())
  • 函数库lodash的 _.cloneDeep 方法
  • **jQuery.extend()**方法
  • 手写递归方法(转)
  • 扩展运算符…(只是一层的数组或者对象)
var $ = require('jquery'); 
            let arr=[{name:"theShy",age:"21"}]
 -             let arr1= JSON.parse(JSON.stringify(arr));   arr1[0].name="rookie"
 -             let arr2= _.cloneDeep(arr);                  arr2[0].name="ning"
 -             let arr3= $.extend(true, {}, arr);           arr3[0].name="baolan"
                console.log(arr[0].name==arr[1].name==arr[2].name==……);
               //fales arr1[0].name="rookie" arr2[0].name="ning"

JSON.parse(JSON.stringify())使用限制:

  • 不支持函数
  • 不支持undefined(支持null)
  • 不支持Symbol
  • 不支持循环引用,比如 a = {name: ‘a’}; a.self = a; a2 = JSON.parse(JSON.stringify(a)),会出现 TypeError 错误
  • 不支持Date,会变成 ISO8601 格式的字符串
  • 不支持正则表达式
  • 对象属性的顺序可能会被改变。可以使用 ES6 中的 Map 或者使用 Object.keys 方法获取对象属性的顺序
    (对象包含 undefined、函数、Symbol 类型时,转化为 JSON 字符串时会被忽略或者转化为 null)

下面是一个简单的手写递归方法

function deepCopy(obj) {
  if (typeof obj !== "object" || obj === null) {
    return obj;
  }

  let newObj = Array.isArray(obj) ? [] : {};

  for (let key in obj) {
    newObj[key] = deepCopy(obj[key]);
  }

  return newObj;
}

对于扩展运算符的深拷贝和浅拷贝不同情况可以看数组一些常用的方法

13.防抖与节流⭐⭐⭐

防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间(多次执行变为最后一次执行
应用场景:
提交按钮用户注册时候的手机号验证邮箱验证

节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率(多次执行变成每隔一段时间执行
应用场景:
window对象的resize、scroll事件
拖拽时候的mousemove
射击游戏中的mousedown、keydown事件
文字输入、自动完成的keyup事件

vue中使用详情可以看vue中使用防抖和节流

14.性能优化⭐⭐

前端性能优化手段从以下几个方面入手:加载优化、执行优化、渲染优化、样式优化、脚本优化

  • 加载优化:减少HTTP请求、缓存资源、压缩代码、无阻塞、首屏加载、按需加载、预加载、压缩图像、减少Cookie、避免重定向、异步加载第三方资源

  • 执行优化:CSS写在头部,JS写在尾部并异步、避免img、iframe等的src为空、尽量避免重置图像大小、图像尽量避免使用DataURL

  • 渲染优化:设置viewport、减少DOM节点、优化动画、优化高频事件、GPU加速

  • 样式优化:避免在HTML中书写style、避免CSS表达式、移除CSS空规则、正确使用display:display、不滥用float等

  • 脚本优化:减少重绘和回流、缓存DOM选择与计算、缓存.length的值、尽量使用事件代理、尽量使用id选择器、touch事件优化

15.webpack是怎么打包的,babel又是什么⭐

Webpack:把所有依赖打包成一个 bundle.js文件,通过代码分割成单元片段并按需加载。Webpack是以公共JS的形式来书写脚本的,但对AMD/CMD的支持也很全面,方便旧项目进行代码迁移。
把项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。

babel将es6、es7、es8等语法转换成浏览器可识别的es5或es3语法。

16. webpack构建流程⭐

  1. 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
  2. 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
  3. 确定入口:根据配置中的 entry 找出所有的入口文件;
  4. 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
  5. 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

17.vite和webpack的区别⭐⭐

  • 构建速度
    Vite 通过 ES Module 原生支持和开箱即用的特性,可以快速实现客户端和服务端的热更新,开发时的启动速度快,在初次构建和增量构建时性能均优于 Webpack。
    Webpack 需要通过分析所有的模块之后进行构建,而 Vite 利用浏览器支持的 ESM 原生支持的特性,通过直接加载文件来进行编译和构建,速度较 Webpack 更快。

  • 打包方式
    Vite 支持基于 ES Module 的原生打包,可以直接使用浏览器原生支持的 ES Module 特性进行代码打包和分发。
    Webpack 对开发者使用的方式相对更为多样化,例如支持 CommonJS、AMD 等 JavaScript 模块系统,更加灵活。

  • 插件生态
    相比于 ViteWebpack 社区相对更成熟和庞大,拥有更多的第三方插件和工具支持,可以满足更为复杂的打包需求。

  • 应用场景
    Vite 仅支持现代浏览器,因此仅适用于现代浏览器、轻量级应用场景和小型项目。
    Webpack 支持更为广泛,适用于大型项目和复杂应用场景。

总结:Vite 在性能和速度方面上优于 Webpack,特别是在开发环境下。
如果较大的项目,建议使用 Webpack 进行管理和构建。
当项目较小,且需要快速启动和热更新时,可以使用 Vite 来开发项目,更加高效。

18.require和import区别⭐

  • 调用时间
    require 运行时 调用,理论上可以运用在代码任何地,甚至不需要赋值给某个变量之后再使用。
    lmport是 编译时 调用,必须放在文件开头,而且使用格式也是确定的。
  • 遵循规范
    require 是 AMD规范引入方式
    import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法
  • 本质
    require是赋值过程,其实require 的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量。
    import是解构过程

通过require 引入基础数据类型时,属于复制该变量。
通过require 引入复杂数据类型时,数据浅拷贝该对象。
出现模块之间的循环引用时,会输出已经执行的模块,而未执行的模块不输出(比较复杂)。CommonJS模块默认export的是一个对象,即使导出的是基础数据类型。

ES6 模块语法是 JavaScript 模块的标准写法,坚持使用这种写法,取代 Node.js 的 CommonJS 语法。
使用import取代require()。

// CommonJS 的写法
const moduleA = require('moduleA');
const func1 = moduleA.func1;
const func2 = moduleA.func2;
// ES6 的写法
import { func1, func2 } from 'moduleA';

使用export取代module.exports。

// commonJS 的写法
var React = require('react');
var Breadcrumbs = React.createClass({
  render() {
    return <nav />;
  }
});
module.exports = Breadcrumbs;

// ES6 的写法
import React from 'react';
class Breadcrumbs extends React.Component {
  render() {
    return <nav />;
  }
};
export default Breadcrumbs;

19.事件循环(Event Loop)⭐⭐⭐

原因:JavaScript是单线程,所有任务需要排队,前一个任务结束,才会执行后一个任务。

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务:不进入主线程、而进入"任务队列"的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

同步和异步任务分别进入不同的执行环境, 先执行同步任务,把异步任务放入循环队列当中挂起,等待同步任务执行完,再执行队列中的异步任务。异步任务先执行微观任务,再执行宏观任务。一直这样循环,反复执行。

微任务:Promise.then、catch、finally、async/await。
宏任务:整体代码 Script、UI 渲染、setTimeout、setInterval、Dom事件、ajax事件。

如果使用同步的方式,有可能造成主线程阻塞,从而导致消息队列中很多其他任务无法执行。这样一来,一方面会导致主线程白白浪费时间,另一方面导致页面无法及时更新,给用户造成卡死。
所以浏览器采用异步的方式来避免,从最大限度的保证了单线程的流畅运行。

20.什么是单页面应用(SPA)⭐

一个系统只加载一次资源,之后的操作交互、数据交互是通过路由、ajax来进行,页面并没有刷新。
在一个页面上集成多种功能,甚至整个系统就只有一个页面,所有的业务功能都是它的子模块,通过特定的方式挂接到主界面上。
优点

  • 前后端分离
  • 良好的交互体验——用户不用刷新页面,页面显示流畅
  • 减轻服务器压力——服务器只出数据
  • 共用一套后端代码——多个客户端可共用一套后端代码
  • 加载速度快,内容的改变不需要重新加载整个页面,对服务器压力小

缺点

  • SEO难度高——数据渲染在前端进行
  • 页面初次加载比较慢,页面复杂提高很多

多页面:一个应用多个页面,页面跳转时整个页面都刷新,每次都请求一个新的页面
优点:SEO效果好
缺点:页面切换慢,每次切换页面需要选择性的重新加载公共资源

详情可以参考构建单页Web应用

21.axios与fetch的区别?⭐

axios 和浏览器自带的 fetch 都是用于发起 HTTP 请求的工具

  1. 语法与使用方式
    axios: 语法相对简单,易于使用。它可以直接返回 JSON 数据,无需手动解析。
    fetch: 默认返回的是一个 Promise 对象,需要手动解析为 JSON。
  2. 请求和响应拦截
    axios: 支持请求和响应拦截器,方便进行全局拦截处理(例如添加Authorization头)。
    fetch: 不支持拦截器,需要手动实现。
  3. 兼容性
    axios: 兼容性好,支持 IE 11 及更早版本。
    fetch: 不支持 IE 11,部分旧版浏览器可能不支持。
  4. 默认的 Content-Type
    axios: 当发送 JSON 数据时,默认将 Content-Type 设置为 application/json 。
    fetch: 默认不设置 Content-Type,需要自己手动设置。
  5. 错误处理
    axios: 只在 HTTP 响应状态码不为 2xx 时才会触发 catch。
    fetch: 只会在网络错误时触发 catch,仍需对 response.ok 进行判断。
  6. 数据处理
    axios: 会自动将响应数据转换为 JSON。
    fetch: 需要手动调用 .json() 方法。

22.浏览器内存泄漏?⭐⭐

概念:程序中己动态分配的堆内存由于某种原因未释放或无法被浏览器所回收,造成系统内存占用越来越大,最终导致程序运行缓慢甚至系统崩溃等严重后果。
原因:

  • 滥用全局变量,使这个变量一直留在内存中无法被回收
  • 不合理的使用闭包,从而导致某些变量一直被留在内存当中。
  • 没有清除定时器。
  • 未被销毁的事件监听。
  • 某些DOM操作(使用Dom对象绑定事件时,Dom对象消失后,还保留着没有及删除)。

23. iframe的优点、缺点⭐

优点:

  • iframe能够原封不动的把嵌入的网页展现出来。
    如果有多个网页引用iframe,那么你只需要修改iframe的内容,就可以实现调用的每一个页面内容的更改,方便快捷。
  • 网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用iframe来嵌套,可以增加代码的可重用。
  • 如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由iframe来解决。

缺点:

  • iframe会阻塞主页面的onload事件;
  • iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。会产生很多页面,不容易管理。
  • iframe框架结构有时会让人感到迷惑,如果框架个数多的话,可能会出现上下、左右滚动条,会分散访问者的注意力,用户体验度差。
  • 代码复杂,无法被一些搜索引擎索引到,iframe会不利于搜索引擎优化(SEO)。
  • 很多的移动设备无法完全显示框架,设备兼容性差。
  • iframe框架页面会增加服务器的http请求,对于大型网站是不可取的。

24. vue与react的区别⭐

相同点:虚拟DOM,组件化开发,响应式数据驱动,丰富的生态系统。

区别VueReact
核心思想渐进式、强调易用性与低门槛函数式编程、强调灵活性与控制力
模板语法HTML + 指令JSX(JavaScript + XML)
数据绑定双向绑定(v-model)单向数据流
响应式原理自动依赖追踪(Proxy/defineProperty)手动触发更新(setState/useState)
数据可变性直接修改数据触发更新推荐不可变数据(需创建新对象)
状态管理Pinia(组合式 API)Redux + Hooks
作者尤雨溪Meta(原 Facebook)

四、TS篇

1.TS相比JS的有哪些优点⭐

支持静态类型定义提高了代码的可维护性和可读性,减少了代码错误。
提高了开发成本降低维护成本。
ts编译时警告,js运行时警告。

2.TS的类型 ⭐⭐⭐

  • JS的数据类型
  • 新增类型
  1. any:表示任意类型。
  2. unknown:表示未知类型,但在使用前必须强行先进行类型检查。
  3. never:表示任何值都不是,不能直接限制变量,通常用于表示永远不会抛出异常或永远不会有返回值的函数的类型。
  4. void:用于表示没有任何返回值的函数的类型,与undefined相比,如果是函数不应该去调用或使用其返回值。
  5. tuple(元组类型):一种特殊的数组类型,可以存储固定数量的元素,并且每一个元素的类型是已知的且可以不同
  6. enum(枚举):可以定义一堆命名常量,它能增强代码可读性,也让代码更好维护
  • 两个用于自定义类型的方式
  1. type:可以为任何类型创建别名,让代码更简洁,更方便进行类型复用和扩展。
  2. interface:一种定义结构的方式,主要为:类、对象、函数等规定的一种契约。只能定义格式,不能包含任何实现

3. type和interface的区别 ⭐⭐

相同点:都可以用于定义对象结构

区别:
interface:

  • 更专注于定义对象的结构
  • 可以使用extends继承,支持合并
  • 多个同名的interface会合并

type:

  • 可以定义类型别名联合类型(|)、交叉类型(&)
  • 多个同名的type会报错
  • 不支持继承和自动合并(可以使用 & 继承,但不推荐)

一般用interface

4. interface和抽象类的区别 ⭐⭐

相同点:都用于定义一个类的格式
区别:
interface: 只能描述结构不能有任何实现代码,一个类可以定义多个接口
抽象类:既可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类

五、VUE篇

关于vue的面试题移步这篇文章
前端面试题 — — vue篇

六、REACT篇

1.React的生命周期 ⭐⭐⭐

class组件
挂载:组件被创建并插入到 DOM 中

  • constructor :初始化状态和绑定方法。
  • render :渲染 UI。
  • componentDidMount:组件挂载后执行一次,常用于初始化操作(如数据请求、订阅、DOM 操作)。
    更新:当组件的状态或属性 发生变化
  • shouldComponentUpdate:确定是否可以跳过重新渲染。
  • getSnapshotBeforeUpdate :DOM 更新前捕获信息(如滚动位置)
  • componentDidUpdate:依赖项变化时触发,用于响应状态/属性更新。
    卸载
  • componentWillUnmount:清理函数(返回的函数)在组件卸载前执行,用于取消订阅、清理定时器等。

函数组件
useEffect:挂载–>更新–>卸载
useMemo 和 useCallback: shouldComponentUpdate

详情可看官网Component

2. React19新特性?⭐⭐

React 19 是 React 生态的一次重大更新,引入了多项创新特性,显著提升了开发效率、性能优化和开发体验。

  • 新Hook
    通过 useActionStateuseTransition 等新钩子,简化异步操作(如数据提交、错误处理、乐观更新)的管理。开发者不再需要手动管理 isPending 或错误状态,React 会自动处理这些逻辑。
    新增 useOptimistic 钩子,允许在异步请求未完成时立即显示预期结果,提升用户体验。

  • 服务端能力增强
    服务器组件(Server Components) 可在构建时或请求时预渲染,减少客户端负载。
    允许客户端组件通过 action 属性调用服务端异步函数,实现跨端协作。

  • 开发体验提升
    直接在组件内声明 <title><link><meta> 等标签,React 会自动将其提升至 <head> 中,简化 SEO 管理。
    样式表优先级:通过 precedence 属性控制样式加载顺序,避免覆盖问题。
    支持预加载资源:支持 preload、preinit 等资源预加载策略。
    更好的错误报告:改进了 React 19 中的错误处理,以消除重复,并提供处理捕获和未捕获错误的选项。

  • API 与钩子改进
    use: 用 来读取一个 promise,React 会 Suspend 直到 promise 解析。use不支持在 render 中创建的 Promise。
    直接使用 <Context> 替代 <Context.Provider>
    改进Ref :函数组件可直接通过 ref prop 接收引用,无需 forwardRef。

  • 其他关键更新
    更好的错误报告: 以消除重复,并提供处理捕获和未捕获错误的选项。
    新增 prerender 和 prerenderToNodeStream,改进服务端静态 HTML 生成,支持流式响应
    新增React Compiler自动优化渲染逻辑,减少手动记忆化。

参考React v19

3.React中可以在render访问refs吗?为什么?⭐

答:不能,因为在render阶段refs还未生成。DOM 的读取在 pre-commit 阶段,DOM的使用在 commit 阶段。
在这里插入图片描述

4.React中什么是受控组件和非控组件?⭐⭐

含义:受控组件(Controlled Components) 和 非受控组件(Uncontrolled Components) 是处理表单元素的两种不同模式,核心区别在于表单数据由谁管理

受控组件: 表单元素(如 、、)的值完全由 React 状态控制,并通过 onChange 事件同步更新。
特点
→ 数据流:用户输入 → 触发 onChange → 更新 State → 组件重新渲染 → 表单值更新。
→ 控制权:React 完全控制表单的值和更新逻辑。
→ 适用场景:需要实时反馈(如输入验证、动态禁用按钮)或与复杂状态逻辑交互时

非受控组件 :表单元素的值由 DOM 自身管理,React 通过 ref 直接访问 DOM 元素来获取值(通常在提交时获取)。
→ 数据流:用户输入 → 直接修改 DOM → 通过 ref 手动获取值。
→ 控制权:DOM 维护表单值,React 仅在需要时读取。
→ 适用场景:简单表单、需要集成非 React 代码、文件上传()。

总结:
受控组件:数据流清晰,适合复杂交互,但可能影响性能。
非受控组件:轻量高效,适合简单场景或文件上传。

5.谈一谈React的状态提升?⭐⭐

官网是这么解释的:

多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。

简单来说就是:将多个组件需要共享的状态提升到它们最近的父组件上,在父组件上改变这个状态然后通过props分发给子组件。对子组件操作,子组件不改变自己的状态。
可看官网的温度计数器例子

6.react有哪些router? ⭐

  1. BrowserRouter
    描述: 基于 HTML5 的 history API 实现,适用于现代浏览器,URL 格式为 http://www.baidu.com/1,无 # 符号。
    → 支持 location.key 和 location.state,可传递复杂状态
    → 需要服务器端配合,否则直接访问路径可能返回 404(参考vue中hash与history的区别)
    → 提供 basename 属性,支持子目录部署

  2. HashRouter
    描述: 通过 URL 的哈希值(#)管理路由,适用于不支持HTML5 API 的旧浏览器,URL 格式为 http://www.baidu.com/#/1,有 # 符号。
    → 兼容性更好,无需服务器端配置,适合静态部署或老版本浏览器 。
    → 不支持 location.key 和 location.state,路由状态管理受限。

  3. MemoryRouter
    描述: 将路由历史记录保存在内存中,地址栏不会变化,常用于非浏览器环境(如 React Native、测试场景)。
    → 无 URL 可见性,适合需要隐藏路由路径的场景。
    → 支持通过编程式导航(如 navigate())控制路由跳转。

  4. StaticRouter
    描述: 专为 React Native 设计,适配移动端应用的路由管理。
    → 基于移动端导航特性实现,与原生导航行为一致。
    → 通常与 React Native 的导航库(如 react-navigation)结合使用。

  5. NativeRouter
    描述: 用于服务端渲染(SSR),生成静态 HTML,路由路径由服务器固定指定。
    → 无动态交互,仅用于预渲染场景。
    → 需配合服务器框架(如 Express)设置请求路径。

参考react-router

7.类组件与函数组件有什么区别? ⭐⭐

区别类组件函数组件
语法基于 class,需要 render方法直接返回 JSX
状态管理this.state + setStateuseState + useReducer
生命周期生命周期方法(如 componentDidMount)useEffect + 依赖项控制
this 绑定需要处理绑定无 this 问题
逻辑复用高阶组件(HOC)、Render Props自定义 Hooks
错误边界支持(componentDidCatch)暂不支持
性能优化PureComponent、shouldComponentUpdateReact.memo、useMemo、useCallback

推荐建议:尽量优先使用函数组件 + Hooks来进行项目管理与开发。

8.useEffect 与 useLayoutEffect 的区别?⭐

useEffect: 异步执行,不阻止浏览器渲染,在浏览器完成渲染和布局(绘制)之后异步执行。
useLayoutEffect: 同步执行,在 DOM 更新后、浏览器绘制前触发,适合需要直接操作 DOM 的场景,阻止浏览器重新绘制(useLayoutEffect可能会损害性能。尽可能使用 useEffect。),

参考:useLayoutEffect

9.useMemo 与 useCallback 的区别?⭐

共同点:都是用于性能优化的 Hook
useMemo : 缓存计算结果,缓存一个值(计算结果),避免每次渲染时重复计算(跟vue中computed相似)。

	const filteredList = useMemo(() => {
	  return list.filter(item => item.price > 100);
	}, [list]); // 仅当 `list` 变化时重新计算

参考官网:useMemo

useCallback : 缓存函数引用,缓存一个函数的引用,避免每次渲染时创建新函数。

	const handleClick = useCallback(() => {
	  console.log("Item clicked:", itemId);
	}, [itemId]); // 仅当 `itemId` 变化时创建新函数

参考官网:useCallback

10.组件之间如何传值?⭐⭐⭐

  1. Props:父 → 子 , 子 → 父
  2. 状态提升 : 兄弟组件通过父组件共享状态
  3. Context :跨层级组件通信(父孙)
  4. Redux:全局状态管理
  5. bus:全局传值
  6. Refs :父组件通过 Ref 调用子组件方法
  7. Portals 通信: 结合 Context 实现跨 DOM 层级通信
  8. 自定义 Hooks:共享逻辑和状态
  9. 路由参数传值
  10. 本地存储传值(localStorage、sessionstorage)

11.谈一谈redux⭐

使用场景:当组件层级深、状态需要跨组件共享或需要管理复杂状态逻辑时。
三个核心概念

  • state: 管理的数据
  • action: 描述如何改变数据
  • reducer: 根据action的描述来改变state

12.react渲染原理?⭐

答:React 的渲染原理核心是通过 虚拟 DOMDiff 算法 高效更新界面。当状态变化时,React 会生成新的虚拟 DOM 树,通过 Diff 对比新旧树的差异,计算出最小的 DOM 操作,并借助 Fiber 架构 将渲染拆分为可中断的异步任务,结合双缓存机制优先级调度(如并发模式),避免阻塞主线程,最终批量更新真实 DOM,实现高性能、流畅的渲染过程。

后言

其他常问的非技术问题可以看这

面试经常会问的问题

总结

每天多学习一点,更进步一点
祝:大家早日找到理想的工作~
码字不易,持续更新中…

哪里有不足之处,麻烦指出,谢谢~~
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jet_closer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值