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

该博客整理了前端面试常见问题及答案,涵盖HTML、CSS、JS、计算机网络等多方面知识。如HTML语义化目的、CSS选择器优先级、ES6新特性等,还涉及React、Vue等框架相关内容,为前端求职者提供了全面的复习资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前端面试笔记

前言

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

一、HTML篇

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

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

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

2.HTML5新特征⭐

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

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

特性CookiesessionStoragelocalStorage
存储容量4KB 左右5-10MB5-10MB
生命周期可设置过期时间(默认会话结束)关闭当前页面或浏览器后失效永久存储(需手动清除)
数据传递每次请求自动携带(HTTP头部)不自动发送不自动发送
访问范围同源下所有窗口仅限当前标签页同源下所有窗口
API易用性原始字符串操作(需自行解析)键值对键值对
安全机制支持 Secure/HttpOnly 标志无特殊安全机制无特殊安全机制
存储数据类型仅字符串字符串(需序列化对象)字符串(需序列化对象)

因为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 选择器的数量。(100)
  2. b: 具有类选择器、属性选择器和伪类选择器的数量。(10)
  3. c: 具有元素选择器和伪元素选择器的数量。(1)
  4. d: 通配符选择器 *、类型选择器和伪元素选择器的数量(在某些CSS实现中,可能仅表示元素选择器)。

优先级计算过程

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

计算优先级的步骤

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

举例1: 执行以下代码后,<p> 的最终文字颜色是?(答案在后面)

<div id="content">
  <p class="text">Hello</p>
</div>   
#content p { color: blue; }
.text { color: red; }
div p { color: green; }
A. blue 
B. red  
C. green 
D. 黑色(默认色)

举例2:现有样式 .list li.active { background: yellow; },哪个选项能覆盖它?(答案在后面)

A. ul.list > li { background: white; }
B. li#selected { background: white; }
C. .list .item.active { background: white; }
D. [data-state="active"].item { background: white; }

举例3:下列选项中能覆盖 #content .article h2 样式的是? (答案在后面)

A. h2. title
B. .article #subtitle
C. body #content h2
D. #content .article > h2.special

1、的答案答案与解析 答案:A
#content p → ID+元素 = 100+1 = 101
.text → 类 = 10
div p → 元素+元素 = 1+1 = 2
最高权重 101 的样式生效(blue)。

2、的答案答案与解析 答案:B、C
原样式权重:类+元素+类 = 10+1+10 = 21
A:元素+类+元素 = 1+10+1 = 12
B:元素+ID = 1+100 = 101( 更可靠)
C:类+类+类 = 10+10+10 = 30(权重大于21)
D:属性+类 = 10+10 = 20

3、的答案答案与解析 答案与解析 答案:D
原样式权重:ID(100) + 类(10) + 元素(1) = 111
选项权重
A:元素(1) + 类(10) = 11
B:类(10) + ID(100) = 110
C:元素(1) + ID(100) + 元素(1) = 102
D:ID(100) + 类(10) + 元素(1) + 类(10) = 121

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>
  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?(不是KFC别弄混了)⭐

全称:Block Formatting Context(块级格式化上下文)
BFC是一个独立的渲染区域,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。
特性:

  • 内部的盒子会在垂直方向上一个接一个地放置。
  • 垂直方向上的距离由margin决定,margin的重叠会导致垂直方向上的距离减小。
  • 创建BFC的元素不会与浮动元素重叠。
  • BFC是一个独立的容器,内部的元素不会影响到外部元素,反之亦然。
  • 计算BFC的高度时,浮动元素也会参与计算。

创建:

  • 使用float属性,将元素的float属性设置为left、right或none。
  • 使用position属性,将元素的position属性设置为absolute或fixed。
  • 使用display属性:将元素的display属性设置为inline-block、table-cell、table-caption或flex。
  • 使用overflow属性:将元素的overflow属性设置为hidden、auto或scroll。
  • 使用table元素:table元素及其子元素(如thead、tbody、tfoot、tr、td、th)默认会创建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 文件解析完成后再加载
阻塞渲染不会阻塞页面渲染可能因加载顺序导致渲染延迟
模块化需手动管理多个文件便于代码拆分
灵活性可动态操作,支持媒体查询静态引入,无法动态修改

19. 介绍一下 position的属性⭐

position 是 CSS 中用于设置元素定位方式的属性。它决定了元素在文档流中的位置,以及如何与其他元素进行交互。position 属性有以下几个值:

  • static(默认值):默认值,元素按正常文档流定位。
  • relative:元素相对于正常位置进行定位。
  • absolute:元素相对于最近的已定位(即 relative、absolute、fixed 或 sticky)的祖先元素进行定位。 元素脱离正常文档流,不占据空间。
  • fixed:元素相对于浏览器窗口定位,脱离文档流。
  • sticky:元素在相对定位和固定定位之间切换,基于用户滚动位置。

三、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. Set 和 Map
  12. Reflect
  13. Symbol
  14. 链判断操作符(.?),空值操作符(??)
    了解关于es6的更多知识可以看阮一峰——ES6 入门教程
    关于操作符的了解可以参考这篇文章JS核心操作符

2.ES7、ES8新特性 ⭐

es7

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

es8

3.闭包的理解⭐⭐

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

闭包使用场景:防抖 、节流 、封装私有变量与方法、高阶函数、自定义Hook、数据格式化缓存

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)

更多关于=====操作符的运算可以参考这篇文章JS核心操作符

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>

扩展:如何通过事件捕获获取同一父元素下所有同名子元素的操作事件???
可以通过 事件捕获 + 事件委托 的组合方式实现,核心思路是利用事件捕获的传播特性,在父元素上统一监听,再精准匹配目标子元素

  • 事件触发时会先从顶层元素(如window)向目标元素由外到内传播(捕获阶段),通过addEventListener的第三个参数useCapture: true(如果设置为false 则相反为冒泡)可监听此阶段。
  • 不给每个同名子元素单独绑定事件,而是在它们的共同父元素上绑定事件。因为事件捕获会经过父元素,所以父元素的事件处理函数会被触发
  • 在父元素的事件处理函数中,通过event.target获取实际触发事件的子元素,再通过类名、标签名等判断是否为目标同名元素(例如通过classList.contains(‘目标类名’)筛选)。
  • 若匹配成功,则针对该子元素执行具体逻辑。

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

结果放在23了

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

关于操作符的运算可以参考这篇文章JS核心操作符

23. 22题的结果⭐

  1. [1, NaN, NaN]
    原因:[1,2,3].map(parseInt) 相当于执行的是
 ==> [1,2,3].map((num,i) => parseInt(num,i))
 ==> [parseInt(1,0),parseInt(2,1),parseInt(3,2)]

parseInt接受两个参数(string, radix),如果radix是undefined、0或未指定,则会判断:
1、如果输入的sting以 0x 或 0X 开头,那么radix被假定为16进制
2、如果输入string以 “0”(0) 开头, radix使用十进制
3、如果输入 string 以任何其他值开头,radix是十进制
所以parseInt(1,0) === parseInt(1,10) === 1
二进制里面没有1,2所以后面两个无法表示

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

以上方法有时不严谨,可以用下面这个

Reflect.ownKeys() (es6新增) 包含所有属性类型

const obj = Object.defineProperty({}, 'hidden', {
  value: 123,
  enumerable: false // 不可枚举属性
});

Object.keys(obj).length; //  0 
Reflect.ownKeys(obj);    // ['hidden']  

const b = {[Symbol('a')]:1}
JSON.stringify(b);     //  '{}'
Reflect.ownKeys(b);    //  [Symbol(a)]

详细了解可以参考阮一峰——Reflect

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)); 

32. Map 与Set 的区别⭐

区别MapSet
核心用途键值对存储唯一值集合
键的灵活性支持任意类型键无键,值即键
典型操作增删查改键值对增删查值、去重
性能优势键关联数据快速查找存在性检测高效

33. Map和普通对象的区别⭐

  • 键的类型
    普通对象 键只能是字符串或Symbol。如果使用其他类型(如数字、对象)作为键,会被自动转换为字符串。
    Map 键可以是任意类型(字符串、数字、对象、函数、Symbol 等),且保持原类型不变。
  • 键的遍历
    普通对象 没有原生的迭代器,遍历需要通过 for…in、Object.keys()、Object.values() 等方法。
    Map 是可迭代对象,支持 for…of 直接遍历,且遍历顺序严格按照键的插入顺序。 提供 keys()、values()、entries() 方法获取迭代器,方便遍历键、值或键值对。
  • 大小获取
    普通对象 没有直接获取属性数量的方法,需通过 Object.keys(obj).length 间接计算。
    Map 提供 size 属性,直接返回键值对的数量(如 map.size),高效且方便。
  • 原型链影响
    普通对象 会继承 Object.prototype 上的属性(如 toString、hasOwnProperty),可能与自定义属性冲突。
    Map 没有原型链干扰,默认不包含任何继承的键,无需担心属性名冲突。
  • 增删改查操作
    普通对象 通过 .[] 操作(如 obj.key = value、delete obj.key),语法简洁但功能有限。
    Map 提供专门的方法:set(key, value)(新增 / 修改)、get(key)(查询)、delete(key)(删除)、has(key)(判断是否存在),操作更规范,语义更清晰。

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

1.HTTP与HTTPS⭐⭐⭐

HTTP 和 HTTPS 都是网络上传输数据使用的协议。
HTTP:客户端与服务器之间数据传输的格式规范,表示“超文本传输协议”。
HTTPS:在HTTP与TCP之间添加的安全协议层。

  • 默认端口号:HTTP:80,HTTPS:443
  • 传输方式:http是明文传输,https则是具有安全性的lSSL加密传输l协议。
  • 连接方式:http的是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
  • 性能方面:https由于需要设计加密以及多次握手,性能方面不如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(服务器返回的资源唯一标识,如hash值) ,浏览器发送 If-None-Match(Etag的值是对应的hash值,会加大服务器开销),若一致 → 304

Etag优于Last-Modified
Last-Modified的单位时间是秒,如果一个文件在1s内被多次改变,那么它们的Last-Modified就没有被体现出来修改,而Etag保持了精度
→ 在性能上 Last-Modified优于EtagLast-Modified只记录时间,而Etag需要计算出hash值

  • 结果:
    资源未变化 → 返回 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. URL解析
    首先判断输入的URL是否合法,并根据输入的内容进行对应的URL解析
  2. DNS查询
    浏览器通过 DNS 将域名转换为目标服务器 IP 地址
  3. TCP连接
    通过 TCP 三次握手与服务器建立连接
  4. 发送HTTP请求
    浏览器发送 HTTP 请求(包含请求头、方法、URL 路径等)
  5. 服务器处理请求并返回HTTP报文
    服务器返回 HTTP 响应(状态码、响应头、响应体)
  6. 浏览器解析并渲染页面
    HTML/CSS 解析 → DOM/CSSOM 树构建 → 渲染树生成 → 页面布局 → 绘制。
  7. 资源加载
    加载 JS、图片等资源

11.浏览器渲染原理?⭐

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

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

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

12.深拷贝,浅拷贝⭐⭐

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

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

总而言之,核心区别在于是否复制对象的深层结构。
实现方法
浅拷贝

  1. 函数库lodash的 _.clone 方法
  2. Object.assign()
  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)

递归实现思路: 手动递归遍历对象 / 数组,对每个属性判断类型

  • 基本类型(如数字、字符串)直接复制;
  • 引用类型(对象、数组)则创建新实例,递归复制其属性;
  • 特殊类型(如 Date、RegExp)单独处理。

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

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请求、减少DNS解析、压缩代码、按需加载、预加载、减少请求次数、HTTP 缓存、Service Worker、本地存储缓存

  • 渲染性能优化(减少页面渲染延迟):减少重绘重排、优化渲染树、CSS放顶部JS放底部、拟列表、减少DOM节点、优化高频事件、GPU加速

  • 网络与服务端优化:CDN 加速、压缩传输、DNS 预解析、服务器缓存

  • 用户体验优化:骨架屏、loading、预渲染首屏、渐进式加载

首屏加载慢?优先优化资源体积、缓存、懒加载;
交互卡顿?重点排查长任务、回流重绘、内存泄漏;
大数据页面卡?考虑 Web Worker、虚拟列表(只渲染可视区域内容)。

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

Webpack:从入口模块出发,递归解析所有依赖模块并构建依赖关系图,然后将这些模块按一定规则打包成一个或多个浏览器可识别的输出文件。整个过程包括解析、编译转换、代码拆分、优化等步骤,最终生成优化后的静态资源。

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

16. vite构建项目 开发环境和生成环境有什么区别?⭐

开发环境:基于浏览器原生 esbuild 模块运行,无需预打包,通过开发服务器实时响应请求并处理依赖,实现毫秒级启动和即时热更新(HMR),优先保证开发效率。
生产环境:使用 Rollup 进行预打包,会对代码进行压缩、分割、 tree-shaking 等优化,生成高度优化的静态资源,优先保证输出产物的性能和体积。
esbuild ⽤于开发服务器阶段,通过实时编译和提供模块来实现快速的冷启动和热模块替换。⽽ Rollup ⽤于⽣产构建阶段,将源代码打包为最终可发布的⽂件,以⽤于部署到⽣产环境。

  • esbuild: esbuild 是⼀个快速、可扩展的 JavaScript 打包器,它被⽤作 Vite 的默认构建⼯具。
    esbuild 的主要任务是将源代码转换为浏览器可以理解的代码,同时还⽀持压缩、代码分割、按需
    加载等功能。esbuild 利⽤其⾼性能的构建能⼒,实现了快速的开发服务器和热模块替换。
  • Rollup: Rollup 是⼀个 JavaScript 模块打包⼯具,也是 Vite 的另⼀个基础依赖。在 Vite 中,
    Rollup 主要⽤于⽣产构建阶段。它通过静态分析模块依赖关系,将多个模块打包为⼀个或多个最终
    的输出⽂件。Rollup ⽀持多种输出格式,如 ES 模块、CommonJS、UMD 等,可以根据项⽬的需要进⾏配置。

17.vite和webpack的区别⭐⭐

  • 开发模式的差异
    在开发环境中, Webpack 是先打包再启动开发服务器,⽽ Vite 则是直接启动,然后再按需编译
    依赖⽂件
    。(可以启动项⽬后检查源码 Sources 那⾥看到)
    这意味着,当使⽤ Webpack 时,所有的模块都需要在开发前进⾏打包,这会增加启动时间和构建时
    间。
    ⽽ Vite 则采⽤了不同的策略,它会在请求模块时再进⾏实时编译,这种按需动态编译的模式极⼤地缩短了编译时间,特别是在⼤型项⽬中,⽂件数量众多, Vite 的优势更为明显。

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

  • 底层语⾔的差异
    Webpack 是基于 Node.js 构建的,⽽ Vite 则是基于 esbuild 进⾏预构建依赖。esbuild 是采
    Go 语⾔编写的,Go 语⾔是 纳秒 级别的,⽽ Node.js 是 毫秒 级别的。因此,Vite 在打包速度上
    相⽐Webpack 有 10-100 倍的提升。

  • 热更新的处理
    Vite 当某个模块内容改变时,只需要让浏览器重新请求该模块即可,不需要刷新整个⻚⾯,这⼤⼤减少了热更新的时间。
    Webpack 当⼀个模块或其依赖的模块内容改变时,热更新是基于⽂件级别的,需要重新构建并刷新整个⻚⾯。

关于项目中如何使用webpack与vite可以参考这篇文章前端项目工程化配置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事件。

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

async function fun1 () {
    console.log('1')
}
const fun2 = async () => {
    console.log('2')
    await fun1()
    console.log('3')
}
console.log('4');
setTimeout(() => console.log('宏任务'), 0);
Promise.resolve(console.log('5')).then(() => console.log('6'));
fun2()
console.log('7');
//运行结果 4 5 2 1 7 6 3 宏任务

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,实现高性能、流畅的渲染过程。

后言

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

面试经常会问的问题

总结

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

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

在2025年的前端开发面试中,面试官通常会围绕基础知识、进阶概念以及实际应用能力来设置题目。以下是一些可能的题目及解析,涵盖了JavaScriptHTMLCSS、框架和工具等方面。 ### JavaScript 相关问题 #### 1. **解释 Promise.race 的作用并提供一个示例** `Promise.race` 方法返回一个新的 Promise,它会在传入的 Promise 数组中的任意一个 Promise 被解决(resolve)或拒绝(reject)时立即以相同的结果进行解决或拒绝。这个方法适用于需要处理多个异步操作,并且只需要第一个完成的操作结果的情况。 ```javascript const promise1 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'one')); const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'two')); Promise.race([promise1, promise2]).then(value => { console.log(value); // 输出: one }); ``` #### 2. **什么是闭包?请提供一个使用场景** 闭包是指有权访问另一个函数作用域内变量的函数。闭包的一个常见用途是创建私有变量或数据封装。例如: ```javascript function createCounter() { let count = 0; return function() { count++; return count; }; } const counter = createCounter(); console.log(counter()); // 输出: 1 console.log(counter()); // 输出: 2 ``` ### HTML 相关问题 #### 3. **列举一些常见的 HTML 标签及其用途** - `<div>`:用于定义文档中的块级元素。 - `<p>`:表示段落。 - `<h1>` 到 `<h6>`:表示标题,其中 `<h1>` 是最高级别的标题。 - `<ul>` 和 `<ol>`:分别表示无序列表和有序列表。 - `<li>`:表示列表项。 - `<section>`:用于定义文档中的节(如章节、页眉、页脚或文档的其他部分)。 - `<article>`:表示文档、页面或应用程序中独立的内容[^2]。 ### CSS 相关问题 #### 4. **解释 Flexbox 布局的基本原理** Flexbox 是一种一维布局模型,旨在更轻松地设计响应式布局。通过设置容器的 `display` 属性为 `flex`,可以启用 Flexbox 布局。Flexbox 允许通过简单的属性控制子元素的排列方向、对齐方式和间距等。例如: ```css .container { display: flex; justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */ } ``` ### 框架与工具相关问题 #### 5. **React 中的 useEffect 钩子有什么作用?** `useEffect` 是 React 提供的一个钩子函数,用于在函数组件中执行副作用操作,例如数据获取、订阅或手动修改 DOM。它可以替代类组件中的生命周期方法(如 componentDidMount、componentDidUpdate 和 componentWillUnmount)。例如: ```javascript import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 仅当 count 变化时更新 return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ``` #### 6. **Vue.js 中的 computed 属性与 methods 有何不同?** `computed` 属性是基于它们的依赖进行缓存的,只有当依赖的数据发生变化时才会重新计算。而 `methods` 中的方法则每次调用都会重新执行。因此,在不需要频繁重新计算的情况下,使用 `computed` 更加高效。 ```vue <template> <div> <p>Reversed Message: {{ reversedMessage }}</p> </div> </template> <script> export default { data() { return { message: 'Hello Vue' }; }, computed: { reversedMessage() { return this.message.split('').reverse().join(''); } } }; </script> ``` ### 总结 这些题目覆盖了前端开发的基础知识和进阶概念,帮助你在面试中展示全面的技术能力。掌握这些内容不仅有助于应对面试,还能提升日常开发中的实践水平。
评论 49
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jet_closer

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

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

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

打赏作者

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

抵扣说明:

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

余额充值