前端经典面试题总结
前端开发领域涵盖了大量的知识点,面试中的经典问题通常集中在 HTML、CSS、JavaScript 及 ES6 等基础技能上。以下是针对这些知识点的一些总结:
1. HTML
HTML 是 Web 页面结构的基础,它定义了网页的内容和结构。面试中的 HTML 问题常常涉及到常见标签的使用、表单控件的管理、语义化标签的理解以及 HTML5 的新特性。对 SEO、网页可访问性、和移动端优化等也经常有相关的考察。
2. CSS
CSS 是网页样式的核心,面试常常考察你对布局、样式继承、盒子模型以及 CSS3 新特性的理解。常见的考察点包括:如何实现响应式布局、常见的布局方法(如 Flexbox、Grid、浮动布局等)、如何优化 CSS 性能、如何解决跨浏览器兼容性问题等。随着前端技术的发展,CSS 工程化(如预处理器、模块化、自动化构建工具等)也成为了考察的重点。
3. JavaScript
JavaScript 是前端开发的核心语言,面试中通常会考察你对语言基础的掌握程度,例如数据类型、作用域、闭包、原型链、事件机制、异步编程等。对 DOM 操作、事件处理以及性能优化的了解也是必考内容。随着现代 JavaScript 应用的复杂性,面试也开始关注框架(如 React、Vue)和工程化的相关知识。
4. ES6+ 新特性
ES6(ECMAScript 2015)引入了许多新特性,如箭头函数、模板字符串、解构赋值、Promise、模块化等,这些特性大大提高了 JavaScript 的开发效率和可读性。随着 ES6+ 规范的普及,面试题目也会考察你对这些新特性的理解和实际应用能力。此外,ES6 的异步编程(如 async/await)以及模块化系统(如 import/export)在现代 Web 开发中变得尤为重要。
总结:
前端面试不仅考察你对基本知识的理解,还关注你能否灵活运用这些知识解决实际开发中的问题。你需要深入理解 Web 技术栈的每个层面,并具备实际的工程化能力,如前端性能优化、跨浏览器兼容性解决方案、组件化开发等。在面试准备过程中,除了掌握理论知识,更要注重实际项目中的应用和问题解决能力。
这份经典面试题合集可以帮助你全面梳理前端基础知识,也能在实际面试中提供有力的支持。
1. 有哪些常用的 HTML 标签?
常用的 HTML 标签包括:
结构标签:
<html>
:定义 HTML 文档的根。<head>
:包含文档的元信息,如标题、样式表等。<body>
:定义 HTML 文档的主体内容。
标题和段落:
<h1>
-<h6>
:表示标题,<h1>
级别最高。<p>
:段落。<br>
:换行。<hr>
:水平分割线。
文本格式化:
<b>
/<strong>
:加粗。<i>
/<em>
:斜体。<u>
:下划线。<mark>
:标记高亮。<small>
:小号文本。
链接和媒体:
<a>
:超链接。<img>
:插入图像。<video>
:视频。<audio>
:音频。<source>
:媒体资源源。<iframe>
:嵌入其他网页。
列表:
<ul>
:无序列表。<ol>
:有序列表。<li>
:列表项。
表格:
<table>
:表格。<tr>
:表格行。<td>
:单元格。<th>
:表头。<caption>
:表格标题。
表单:
<form>
:表单。<input>
:输入框。<textarea>
:多行文本框。<button>
:按钮。<label>
:标签。<select>
:下拉列表。<option>
:选项。
其他常用标签:
<div>
:容器,无语义。<span>
:行内容器,无语义。<header>
:头部。<footer>
:尾部。<main>
:主体内容。<section>
:分区。<article>
:文章。<nav>
:导航。
2. 什么是 HTML5,HTML5 有哪些新特性?
HTML5 定义:
HTML5 是 HTML 的第五个版本,为现代 Web 提供了更加丰富的功能和结构化语义。
新特性:
-
新增结构标签:
<header>
<footer>
<section>
<article>
<aside>
<main>
等语义化标签。
-
多媒体支持:
<audio>
和<video>
标签用于嵌入音频和视频。<source>
支持多种格式切换。
-
表单增强:
- 新的表单控件如
<date>
<email>
<color>
。 - 属性如
required
、placeholder
和pattern
。
- 新的表单控件如
-
Canvas:
<canvas>
用于绘制图形和动画。
-
SVG 支持:
- 可直接嵌入矢量图形。
-
地理位置 API:
- 获取用户位置。
-
离线存储:
localStorage
和sessionStorage
替代 Cookies。IndexedDB
提供更复杂的数据存储。
-
WebSockets:
- 实现实时通信。
-
拖放(Drag and Drop):
- 内置拖拽支持。
-
语义增强:
- 改善 SEO 和屏幕阅读器支持。
3. iframe 标签的作用是什么?有哪些优缺点?
作用:
<iframe>
用于嵌入一个 HTML 文档到当前页面中。
优点:
- 代码复用:可加载外部内容,如广告、视频或第三方功能。
- 隔离性:与主页面相对独立,避免样式冲突。
- 跨域嵌入:可以加载不同域的内容。
缺点:
- SEO 不友好:搜索引擎不容易索引。
- 性能问题:多个 iframe 增加页面加载时间。
- 复杂的通信:需要额外技术(如
postMessage
)处理主页面与 iframe 的通信。
4. 什么是 HTML 语义化?为什么要语义化?
定义:
HTML 语义化是指使用具有语义的标签来构建网页,使其内容结构清晰明了。
为什么语义化:
- 增强可读性:开发者更容易理解代码。
- 提高 SEO 效果:搜索引擎能更好地抓取和解析内容。
- 屏幕阅读器支持:提升无障碍体验。
- 便于维护和扩展:结构清晰,降低维护难度。
5. CSS 选择器有哪些?优先级分别是什么?
常见选择器:
-
基础选择器:
*
:全局选择器。element
:元素选择器。.class
:类选择器。#id
:ID 选择器。
-
组合选择器:
E F
:后代选择器。E > F
:子代选择器。E + F
:紧邻兄弟选择器。E ~ F
:普通兄弟选择器。
-
伪类选择器:
:hover
、:nth-child(n)
、:first-child
等。
-
伪元素选择器:
::before
、::after
。
优先级:
- 内联样式:
1000
。 - ID 选择器:
100
。 - 类选择器、伪类:
10
。 - 元素选择器、伪元素:
1
。 - 通配符选择器:
0
。
6. 有哪些常见的 CSS 布局?
常见布局:
-
普通流布局:
- 默认布局方式,按文档流排列。
-
浮动布局:
- 使用
float
实现。
- 使用
-
定位布局:
- 使用
position
(absolute
、relative
、fixed
、sticky
)。
- 使用
-
弹性盒(Flexbox):
- 单维布局,使用
display: flex
。
- 单维布局,使用
-
网格布局(Grid):
- 双维布局,使用
display: grid
。
- 双维布局,使用
-
多列布局:
- 使用
column-count
。
- 使用
-
响应式布局:
- 使用媒体查询
@media
和弹性单位。
- 使用媒体查询
7. CSS 中的 1像素问题是什么?有哪些解决方案?
1像素问题:
在高分辨率屏幕(如 Retina)中,1 像素会显得很粗或模糊。
解决方案:
- 使用
transform: scale(0.5)
缩放。 - 使用伪元素
::before
或::after
实现边框。 - 使用
background-image
代替边框。 - 通过媒体查询调整不同设备的边框值。
8. 什么是 CSS 盒子模型?
CSS 盒子模型是指 HTML 元素被渲染为矩形盒子,分为以下区域:
- 内容区:
content
,实际内容。 - 内边距:
padding
,内容与边框之间的距离。 - 边框:
border
,环绕内容和内边距的框。 - 外边距:
margin
,与其他元素的距离。
9. 哪些 CSS 属性可以继承?
可继承的属性:
- 文本相关:
color
、font
系列(font-family
、font-size
等)。
- 空间相关:
visibility
、cursor
。
非继承属性(需要 inherit
强制继承):
border
、margin
、padding
等。
10. 什么是响应式设计?响应式设计的基本原理是什么?如何进行实现?
定义:
响应式设计使网页在不同设备和屏幕尺寸上都能良好显示。
基本原理:
- 流式布局:使用百分比宽度代替固定像素。
- 弹性单位:
em
、rem
。 - 媒体查询:根据屏幕宽度应用不同样式。
实现方法:
- 使用视口元标签:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- 媒体查询:
@media (max-width: 768px) {body {font-size: 14px;} }
- 使用 CSS 框架如 Bootstrap。
- 弹性和网格布局 (
flexbox
和grid
)。
1. CSS3 新增了哪些特性?
选择器:
- 属性选择器增强:
[attr^=value]
、[attr$=value]
、[attr*=value]
。 - 伪类选择器:
:nth-child(n)
、:nth-last-of-type()
。 - 伪元素增强:
::before
、::after
。
背景和边框:
border-radius
:圆角边框。box-shadow
:盒子阴影。background-size
:背景图片尺寸调整。background-clip
:背景裁剪范围。
颜色与渐变:
- 支持
rgba
、hsla
颜色。 - 渐变背景:
linear-gradient
和radial-gradient
。
文本效果:
text-shadow
:文字阴影。word-wrap
:自动换行。
布局和定位:
flexbox
弹性盒模型。grid
网格布局。position: sticky
。
动画和过渡:
transition
:过渡效果。transform
:旋转、缩放、位移。animation
:关键帧动画。
媒体查询:
- 根据设备尺寸或分辨率应用样式。
2. 怎么用 CSS 实现一个宽高自适应的正方形?
方法一:利用 padding
.square {width: 50%;padding-top: 50%;position: relative;
}
方法二:使用 aspect-ratio
.square {width: 50%;aspect-ratio: 1 / 1;
}
3. CSS 中,有哪些方式可以隐藏页面元素?有什么区别?
-
display: none;
:- 完全隐藏,元素从文档流中移除,不占空间。
-
visibility: hidden;
:- 隐藏但保留空间。
-
opacity: 0;
:- 透明化,仍占据空间和响应事件。
-
clip-path
或overflow: hidden
:- 将元素裁剪出视野。
-
移出屏幕:
position: absolute; left: -9999px;
区别:
display: none
不再响应事件,其他方式可能仍响应。- 空间是否保留取决于具体方法。
4. 怎么使用 CSS3 来实现动画?你实现过哪些动画?
使用 CSS3 动画的步骤:
- 使用
@keyframes
定义关键帧。 - 使用
animation
属性绑定动画。
示例:
@keyframes move {0% { transform: translateX(0); }50% { transform: translateX(100px); }100% { transform: translateX(0); }
}.box {width: 100px;height: 100px;background: red;animation: move 2s infinite;
}
常见动画:
- 渐显渐隐:
opacity
动画。 - 旋转翻转:
transform: rotate
。 - 位移动画:
transform: translate
。 - 加载效果:旋转、缩放等。
5. CSS 有哪些常用单位?这些单位各有什么区别?
-
绝对单位:
px
:像素,固定尺寸。cm
、mm
、in
:物理长度单位。
-
相对单位:
%
:相对父元素。em
:相对父元素的字体大小。rem
:相对根元素的字体大小。vw
、vh
:视口宽高的百分比。vmin
、vmax
:视口最小/最大维度。
区别:
- 绝对单位适合固定布局。
- 相对单位适合响应式设计。
6. 有哪些 CSS 性能优化的操作或技巧?
-
减少重绘与重排:
- 合理使用
position
和transform
。 - 避免频繁操作 DOM。
- 合理使用
-
使用硬件加速:
- 利用
transform
和opacity
。
- 利用
-
CSS 压缩:
- 移除注释、空格等冗余。
-
合并文件:
- 减少 HTTP 请求数量。
-
选择器优化:
- 避免过长的选择器链。
-
异步加载:
- 使用
media
属性或异步加载 CSS。
- 使用
7. JavaScript 中如何中止网络请求?
- 使用
AbortController
:
const controller = new AbortController();
const signal = controller.signal;fetch('url', { signal }).then(response => response.json()).catch(err => console.log('Request aborted:', err));// 中止请求
controller.abort();
- 使用 XMLHttpRequest 的
abort
方法:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'url', true);
xhr.send();// 中止请求
xhr.abort();
8. 什么是 BOM 和 DOM?分别列举一些它们的函数
BOM(浏览器对象模型):
- 提供与浏览器窗口交互的接口。
- 常见函数:
window.alert()
:弹出警告框。window.open()
:打开新窗口。window.setTimeout()
/setInterval()
:定时器。navigator.userAgent
:获取用户代理信息。location.href
:获取或设置当前 URL。
DOM(文档对象模型):
- 提供与 HTML 文档交互的接口。
- 常见函数:
document.getElementById()
:通过 ID 获取元素。document.querySelector()
:通过选择器获取元素。element.addEventListener()
:添加事件监听。element.classList.add()
:操作类名。element.innerHTML
:修改内容。
9. 深拷贝和浅拷贝有什么区别?怎么实现深拷贝?
区别:
- 浅拷贝:仅拷贝对象的第一层属性,嵌套对象仍指向原始引用。
- 深拷贝:递归拷贝所有层级,嵌套对象独立于原对象。
深拷贝实现:
- 使用
JSON.parse
和JSON.stringify
:
const deepCopy = JSON.parse(JSON.stringify(obj));
注意:不能处理函数和 undefined
。
- 使用递归:
function deepClone(obj) {if (obj === null || typeof obj !== 'object') return obj;const clone = Array.isArray(obj) ? [] : {};for (const key in obj) {clone[key] = deepClone(obj[key]);}return clone;
}
- 使用
structuredClone
(现代浏览器支持):
const copy = structuredClone(obj);
10. 如何使用 JavaScript 来判断用户设备类型?比如判断是 PC 端还是移动端访问?
方法一:通过 navigator.userAgent
function detectDevice() {const ua = navigator.userAgent;if (/Mobile|Android|iP(hone|ad)|Windows Phone/i.test(ua)) {return 'Mobile';}return 'PC';
}
方法二:通过视口宽度
function detectDevice() {return window.innerWidth <= 768 ? 'Mobile' : 'PC';
}
方法三:结合现代 API
使用 matchMedia
检测:
const isMobile = window.matchMedia('(max-width: 768px)').matches;
console.log(isMobile ? 'Mobile' : 'PC');
### 1. **JS 中数组是如何在内存中存储的?**JavaScript 中的数组是对象的一种特殊类型。在内存中,数组元素存储方式可以因引擎实现而有所不同,但一般可以分为以下两种存储方式:#### **稀疏数组(Sparse Array)**:
- 如果数组中包含不连续的索引,JS 会将数组元素存储为对象,键为数组索引,值为数组元素。这会导致性能较差,因为查找元素时需要通过对象查找。#### **密集数组(Dense Array)**:
- 如果数组的索引是连续的,JS 引擎通常会优化数组的存储方式,直接在内存中分配连续的空间来存储数组元素,效率较高。现代浏览器的 JS 引擎会根据数组的使用方式来优化存储,例如 V8 引擎(Chrome 中的引擎)使用了一种名为 "elements kind" 的机制,来决定使用稀疏数组或密集数组的存储方式。---### 2. **JS 中 Map 和 WeakMap 有什么区别?**#### **Map**:
- **存储键值对**:`Map` 是一个键值对集合,可以使用任何类型的对象作为键(包括对象、函数等)。
- **键值对顺序**:`Map` 会记住插入顺序,遍历时按插入顺序返回键值对。
- **可遍历**:可以使用 `forEach()` 或 `for...of` 遍历。
- **内存管理**:不会自动垃圾回收,必须显式清除引用,否则可能造成内存泄漏。#### **WeakMap**:
- **存储键值对**:`WeakMap` 只能使用对象作为键,键是弱引用。
- **内存管理**:当 `WeakMap` 中的键对象不再被引用时,键值对会被自动垃圾回收,避免内存泄漏。
- **不可遍历**:`WeakMap` 不支持遍历或检查键值对。#### 主要区别:
1. `Map` 可以使用任何数据类型作为键,`WeakMap` 只能使用对象作为键。
2. `WeakMap` 的键是弱引用,会随时被垃圾回收,而 `Map` 的键会保持强引用,直到显式删除。
3. `WeakMap` 不可遍历,而 `Map` 可以遍历。---### 3. **用 CSS 和 JS 来实现动画分别有哪些优缺点?**#### **CSS 动画**:
**优点**:
- **性能优越**:CSS 动画可以由浏览器优化,特别是在 GPU 上进行硬件加速,通常比 JS 动画更高效。
- **代码简洁**:声明式的方式,代码清晰简洁。
- **易于维护**:可以直接在 CSS 中管理,和样式分离,便于维护。**缺点**:
- **不够灵活**:无法响应动态变化,较为固定。
- **控制性差**:如果需要复杂的交互和动画链,CSS 动画不够灵活。
- **兼容性问题**:某些旧版本的浏览器可能不支持所有 CSS 动画特性。#### **JavaScript 动画**:
**优点**:
- **高度灵活**:可以响应动态数据变化,支持复杂的动画链和逻辑控制。
- **完全控制**:可以精确控制动画的进度、状态和交互。**缺点**:
- **性能差**:需要频繁操作 DOM 或 CSS 属性,可能导致性能问题,尤其是在移动设备上。
- **代码复杂**:需要手动计算和管理动画状态,代码更复杂。
- **不如 CSS 优化**:无法充分利用浏览器的硬件加速。---### 4. **JS 中怎么阻止事件冒泡和事件默认行为?**- **阻止事件冒泡**:使用 `event.stopPropagation()` 方法。```javascriptelement.addEventListener('click', function(event) {event.stopPropagation(); // 阻止事件冒泡});
- 阻止事件默认行为:使用
event.preventDefault()
方法。element.addEventListener('click', function(event) {event.preventDefault(); // 阻止默认行为 });
两者可以同时使用来阻止事件的冒泡和默认行为。
5. 什么是防抖和节流?如何用 JavaScript 实现?
防抖(Debouncing):
- 定义:当某个事件被频繁触发时,防抖会推迟事件的执行,直到一定时间内不再触发该事件为止。
- 应用场景:输入框的实时搜索、窗口大小调整、滚动事件等。
实现:
function debounce(fn, delay) {let timer;return function(...args) {clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);};
}
节流(Throttling):
- 定义:在规定的时间内,只允许某个事件触发一次,不论这个事件被触发多少次。
- 应用场景:防止按钮重复点击、滚动监听等。
实现:
function throttle(fn, delay) {let lastTime = 0;return function(...args) {const now = Date.now();if (now - lastTime > delay) {fn.apply(this, args);lastTime = now;}};
}
6. 前端有哪些实现跨页面通信的方法?
-
LocalStorage / SessionStorage:
- 通过在不同页面间共享同一个存储,进行跨页面通信。
- 适合同一浏览器标签页中的不同页面。
-
Cookies:
- 在不同页面间传递小量数据,具有跨标签页、跨窗口的特性。
- 缺点:存储大小有限,且会带有额外的 HTTP 请求。
-
BroadcastChannel API:
- 可以在同一个浏览器中不同窗口或标签页之间发送消息。
-
PostMessage:
- 用于跨窗口、跨域的通信,适用于
iframe
和父窗口之间的通信。
- 用于跨窗口、跨域的通信,适用于
-
WebSockets:
- 适用于实时双向通信,特别是在 Web 应用和服务器之间。
-
Server-Sent Events (SSE):
- 从服务器推送数据到客户端,适用于服务器到客户端的单向通信。
7. 什么是虚拟 DOM?使用虚拟 DOM 一定更快吗?
虚拟 DOM:
- 定义:虚拟 DOM 是一种抽象的 DOM 表示,React、Vue 等框架通过虚拟 DOM 来优化页面的渲染性能。每当组件的状态变化时,框架会先在内存中创建一个虚拟 DOM 树,然后通过与真实 DOM 树的对比(diff 算法),计算出最小的修改,然后更新真实 DOM。
虚拟 DOM 是否更快?
- 虚拟 DOM 在大多数情况下比直接操作真实 DOM 更快,特别是在频繁的 UI 更新场景下。它通过减少不必要的 DOM 更新和批量处理来提升性能。
- 但是,虚拟 DOM 不是在所有情况下都更快,尤其是在小规模或简单的应用中,直接操作真实 DOM 可能更高效。虚拟 DOM 主要的优势在于优化性能和更易于维护。
8. JS 脚本延迟加载的方式有哪些?
-
setTimeout
延迟执行:setTimeout(() => {// 延迟执行的代码 }, 2000); // 延迟 2 秒
-
defer
属性(HTML 中):defer
会让脚本在文档解析完成后执行,而不会阻塞页面渲染。
<script src="script.js" defer></script>
-
async
属性(HTML 中):async
会在脚本下载完成后立即执行,而不等待文档解析。
<script src="script.js" async></script>
-
动态加载脚本(JavaScript 中):
const script = document.createElement('script'); script.src = 'script.js'; document.body.appendChild(script);
9. 什么是点击穿透,怎么解决?
点击穿透:
- 点击穿透指的是在一个透明的元素(如弹出层)上点击时,事件会穿透该元素并触发下面的元素。常见于有透明区域的弹出层或 Modal 窗口中。
解决方法:
- 使用
pointer-events: none
:- 对透明区域元素使用
pointer-events: none
来禁用该区域
- 对透明区域元素使用
的点击事件。
.transparent-element {pointer-events: none;
}
-
通过
z-index
和阻止事件冒泡:- 确保弹出层的
z-index
高于其他元素,并阻止点击事件冒泡。
document.querySelector('.modal').addEventListener('click', function(event) {event.stopPropagation(); });
- 确保弹出层的
10. 什么是 JS 对象的可枚举性 (enumerable)?
可枚举性:
- 可枚举指的是一个对象的属性是否能通过
for...in
或Object.keys()
等方法被枚举出来。
查看或设置属性是否可枚举:
- 使用
Object.propertyIsEnumerable()
检查一个属性是否可枚举。 - 可以通过
Object.defineProperty()
设置属性的enumerable
特性:Object.defineProperty(obj, 'name', {enumerable: false });
11. Js 如何顺序执行 10 个异步任务?
可以使用 async
和 await
结合 for
循环来顺序执行异步任务。
async function executeTasks() {for (let i = 1; i <= 10; i++) {await someAsyncFunction(i);console.log(`Task ${i} completed`);}
}
12. 介绍一下 JS 中 setTimeout
的运行机制?
setTimeout
是一种延迟执行的机制,它会在指定的时间延迟后执行一次给定的回调函数。
运行机制:
- 添加任务到消息队列:
setTimeout
会把回调函数添加到 JavaScript 的消息队列中。 - 事件循环:事件循环会检查调用栈,如果调用栈为空,会执行队列中的回调函数。
- 定时器精度:实际的延迟时间会受到 JavaScript 执行环境的影响,通常并不精确,可能会有一定的延迟(例如:最小延迟为 4 毫秒)。
setTimeout(() => {console.log('This runs after 2 seconds');
}, 2000);
1. 怎么用 JS 实现大型文件上传? 要考虑哪些问题?
实现大型文件上传时,前端需要考虑以下几个关键点:
基本思路:
- 分片上传:将大文件切割成小块(文件分片),然后逐个上传,每次上传一小块,减少每次请求的数据量,避免超时和内存溢出。
- 使用
FormData
:通过FormData
将文件分片数据与其他参数一同上传到服务器。 - 进度显示:可以使用
XMLHttpRequest
或fetch
来实现进度条。 - 断点续传:使用文件的 MD5 值或其他唯一标识符来标记每个文件分片,断点续传时可以从上次中断的地方开始上传。
注意事项:
- 上传速度和带宽限制:分片上传可以避免一次性上传过大的文件,提升效率。
- 内存管理:分片文件时需要确保内存占用适中,避免加载整个文件导致内存溢出。
- 多文件并发:考虑并发上传,合理设置并发数量,避免服务器压力过大。
- 文件完整性校验:通过文件校验和(如 MD5)来确保文件上传的完整性。
- 服务器支持:确保服务器能支持分片上传,能够处理多个上传请求并合并这些分片。
- 文件大小限制:根据文件大小设置合理的分片大小,一般 5MB 到 50MB 之间。
实现示例(分片上传):
function uploadLargeFile(file) {const chunkSize = 5 * 1024 * 1024; // 5MBconst totalChunks = Math.ceil(file.size / chunkSize);let currentChunk = 0;function uploadNextChunk() {const start = currentChunk * chunkSize;const end = Math.min(start + chunkSize, file.size);const blob = file.slice(start, end);const formData = new FormData();formData.append('file', blob, file.name);formData.append('chunk', currentChunk + 1);formData.append('totalChunks', totalChunks);// 发送分片fetch('/upload', {method: 'POST',body: formData,}).then(response => {if (response.ok) {currentChunk++;if (currentChunk < totalChunks) {uploadNextChunk();} else {console.log('File upload complete!');}} else {console.log('Error uploading chunk');}});}uploadNextChunk();
}
2. 什么是箭头函数?能使用 new
来创建箭头函数么?
箭头函数:
箭头函数是 ES6 引入的一种简洁的函数写法,语法上与传统的函数表达式不同。其特点是:
- 没有
this
:箭头函数没有自己的this
,它继承自外层作用域的this
。 - 没有
arguments
:箭头函数没有arguments
对象。 - 不可用作构造函数:不能使用
new
创建箭头函数的实例。
语法:
const sum = (a, b) => a + b;
不能使用 new
创建箭头函数:
箭头函数不能作为构造函数使用,因此不能使用 new
来创建实例:
const ArrowFunction = () => {};
const instance = new ArrowFunction(); // TypeError: ArrowFunction is not a constructor
3. 什么是前端路由?什么时候适合使用前端路由? 它有哪些优缺点?
前端路由:
前端路由指的是通过 JavaScript 来管理页面的导航和视图的切换,而无需重新加载整个页面。它通常与单页面应用(SPA)结合使用,依赖于 hash
或 history
API。
适合使用前端路由的场景:
- 单页面应用(SPA):需要在不同页面之间切换时,保持页面不刷新,提升用户体验。
- 动态加载内容:可以根据路由变化加载不同的数据和视图,避免每次切换页面都重新加载整个页面。
优点:
- 提高性能:通过避免页面刷新和重新加载,减少了与服务器的交互,页面切换更流畅。
- 用户体验好:前端路由可以实现无缝的页面切换,减少了加载时间。
- 支持复杂的页面结构:可以轻松处理动态 URL、路由嵌套、历史记录等复杂需求。
缺点:
- SEO 难度:传统的 SPA 前端路由对 SEO 不友好,搜索引擎无法索引动态加载的内容。可以使用服务端渲染(SSR)或预渲染来解决。
- 历史记录问题:在浏览器的历史记录管理上需要特别小心,避免出现无法回退的问题。
- 初始加载时间长:如果没有合理的懒加载策略,首次加载的资源可能会很大。
4. 为什么 JS 要被设计为单线程?
JavaScript 被设计为单线程的原因是为了简化程序的执行模型。通过单线程模型,JavaScript 避免了多线程编程中的许多复杂问题,如:
- 共享资源问题:多个线程访问同一资源时,容易产生竞态条件。
- 线程同步问题:在多线程环境下,需要额外的机制来保证线程间的数据一致性。
单线程的设计使得 JavaScript 在执行时保持简洁,开发者无需担心线程同步问题。然而,JavaScript 通过异步编程模型(如回调、Promise
、async/await
等)来处理耗时任务,实现非阻塞式执行。
5. JS 代码中的 use strict
是什么?有什么作用?
use strict
:
use strict
是 JavaScript 的严格模式,它是 ECMAScript 5 引入的。启用严格模式后,JavaScript 的行为会更严格一些,以避免一些潜在的错误和不规范的写法。
作用:
-
禁止使用未声明的变量:避免隐式的全局变量。
'use strict'; x = 10; // ReferenceError: x is not defined
-
消除
this
的意外绑定:在函数中,this
不再指向全局对象。'use strict'; function myFunction() {console.log(this); // undefined (而非全局对象) }
-
禁止删除不可删除的属性:
'use strict'; delete Object.prototype; // SyntaxError
-
禁止使用某些保留字:如
let
、class
、const
、eval
等。
启用方式:
可以在全局或函数作用域启用:
'use strict'; // 全局启用
function foo() {'use strict'; // 函数内部启用
}
6. 如何使用 JS 判断某个字符串长度(要求支持 Emoji 表情)?
解决方案:
普通的字符串 length
会返回字符数,但不能正确处理 Unicode 字符(如 Emoji 表情)。因为一些字符(如 Emoji)可能由多个字符组成。
使用 Array.from()
:
const str = 'Hello 👋';
const length = Array.from(str).length; // 8
console.log(length); // 8
Array.from()
会正确处理 Unicode 字符,将每个 Unicode 字符当作一个独立的元素,适合计算字符串的长度。
7. JS 在什么情况下会存在数字精度丢失的问题,如何解决?
数字精度丢失的原因:
JavaScript 使用 IEEE 754 双精度浮点数格式来表示数字,它无法精确表示某些浮动值,特别是在处理小数时会出现精度丢失问题。
例如:
console.log(0.1 + 0.2); // 0.30000000000000004
解决方案:
-
使用整数计算:避免直接进行浮动数值的加减乘除运算。将所有小数乘以 10 的某次方转为整数计算,最后再除以 10 的次方。
const result = (0.1 * 10 + 0.2 * 10) / 10; // 0.3
-
使用
toFixed()
或toPrecision()
:在结果输出时,控制小数点后的位数。const result = (0.1 + 0.2
).toFixed(2); // ‘0.30’
3. **BigInt**:对于需要高精度整数计算,可以使用 `BigInt` 类型,避免整数精度丢失。---### 8. **说说你对 JS 模块化方案的理解,比如 CommonJS、AMD、CMD、ES Module 分别是什么?**#### **CommonJS**:
- **定义**:一种模块化规范,广泛用于 Node.js 中。每个模块都有自己的作用域,模块通过 `require` 引入,使用 `module.exports` 导出。
- **特点**:同步加载,适用于服务器端。#### **AMD (Asynchronous Module Definition)**:
- **定义**:一种浏览器端的模块化规范,支持异步加载模块。
- **特点**:模块通过 `define` 定义,支持依赖管理,适用于浏览器端。#### **CMD (Common Module Definition)**:
- **定义**:类似于 AMD 的一种模块化方案,主要由 SeaJS 提出。与 AMD 的不同之处在于,它是懒加载模块,尽可能延迟加载模块,直到模块真正需要时。
- **特点**:延迟加载,支持依赖管理。#### **ES Module (ESM)**:
- **定义**:JavaScript 的原生模块化方案,从 ES6 开始引入。通过 `import` 和 `export` 实现模块化。
- **特点**:支持静态分析,模块加载是异步的,支持浏览器和 Node.js
### 1. **如果使用 `Math.random()` 来计算中奖概率,会有什么问题吗?**`Math.random()` 生成一个伪随机数,范围在 [0, 1) 之间。虽然它可以用于计算中奖概率,但存在一些潜在问题:- **概率不精确**:`Math.random()` 返回的是浮动数,精度有限,无法完美模拟某些精确的概率,尤其是在需要高精度的情况下。
- **不公平性**:若需要保证每个中奖者的概率是完全公平的,单纯依赖 `Math.random()` 可能会造成偏差,特别是在大规模的数据中,由于伪随机数的特性,可能存在重复的模式。#### 示例:
假设我们想要通过 `Math.random()` 来决定某人是否中奖,假设中奖的概率是 10%,可以通过如下方式计算:
```javascript
if (Math.random() < 0.1) {
console.log('中奖');
} else {
console.log('未中奖');
}
问题:这个方法在小规模的测试中可能看起来没问题,但如果需要处理大量数据时,它的随机性可能会导致中奖概率偏离预期,特别是当 Math.random()
生成的随机数分布不均匀时。
2. 怎么使用 JS 实现元素拖拽功能?
实现拖拽功能的基本思路是监听鼠标按下、移动和放开事件,通过计算鼠标的位置来动态修改元素的位置。
实现步骤:
- 监听鼠标按下 (
mousedown
) 事件,记录当前鼠标位置。 - 监听鼠标移动 (
mousemove
) 事件,计算鼠标的相对位置并更新元素的位置。 - 监听鼠标松开 (
mouseup
) 事件,停止拖动。
示例代码:
const draggable = document.getElementById('dragElement');draggable.onmousedown = function(event) {// 获取鼠标初始位置let offsetX = event.clientX - draggable.getBoundingClientRect().left;let offsetY = event.clientY - draggable.getBoundingClientRect().top;// 监听鼠标移动事件function onMouseMove(event) {// 更新元素位置draggable.style.position = 'absolute';draggable.style.left = event.clientX - offsetX + 'px';draggable.style.top = event.clientY - offsetY + 'px';}// 监听鼠标松开事件function onMouseUp() {document.removeEventListener('mousemove', onMouseMove);document.removeEventListener('mouseup', onMouseUp);}// 添加事件监听document.addEventListener('mousemove', onMouseMove);document.addEventListener('mouseup', onMouseUp);
};
3. JS 会出现内存泄漏问题么? 在哪些情况下可能会出现内存泄漏?
是的,JavaScript 可能会出现内存泄漏,通常是由于无法访问的对象仍然被保留在内存中。常见的内存泄漏情况包括:
-
全局变量:如果未正确清理不再使用的变量,尤其是意外的全局变量,可能会导致内存泄漏。
function example() {window.leak = {}; // 误用全局变量 }
-
闭包:闭包引用了外部函数的变量,如果闭包在外部作用域无法访问时,它可能仍然占用内存。
function createClosure() {const largeObject = new Array(1000000); // 大对象return function() {console.log(largeObject); // 即使外部函数执行完毕,largeObject 仍然存在}; } const closure = createClosure();
-
事件监听器未移除:绑定到 DOM 元素的事件监听器如果未移除,可能导致内存泄漏,特别是在动态添加和移除元素时。
const element = document.getElementById('element'); element.addEventListener('click', function() {console.log('Clicked'); }); // 忘记移除事件监听器,导致内存泄漏
-
定时器未清除:定时器如果没有清除,也会导致内存泄漏。
const timer = setInterval(function() {console.log('Running'); }, 1000); // 如果未清除定时器,内存会被占用
4. 什么是 JavaScript 的事件流? 有哪些事件流模型?
事件流是指浏览器在处理事件时的传播顺序。JavaScript 事件流分为两个主要阶段:
- 捕获阶段(Capturing Phase):事件从最外层的祖先元素开始向目标元素传递。
- 目标阶段(Target Phase):事件到达目标元素并触发该元素的事件处理程序。
- 冒泡阶段(Bubbling Phase):事件从目标元素向外层元素冒泡,直到最外层的祖先元素。
事件流模型:
- 事件捕获(Capturing):事件从文档的根节点开始捕获,逐层向目标元素传递。
- 事件冒泡(Bubbling):事件从目标元素开始冒泡,逐层向上触发父元素的事件处理程序。
可以通过 addEventListener
的第三个参数来指定是否使用事件捕获:
element.addEventListener('click', function() {console.log('clicked');
}, true); // true 表示捕获阶段,false 表示冒泡阶段
5. ES6 有哪些新特性?
ES6(ECMAScript 2015)引入了许多新特性,其中最重要的包括:
- let 和 const:块级作用域的变量声明,
let
用于声明变量,const
用于声明常量。 - 箭头函数:简化函数语法,且不绑定自己的
this
。 - 模板字符串:支持多行字符串和变量插值。
- 类(Class):面向对象的语法糖,基于原型继承。
- Promise:处理异步操作的对象,解决了回调地狱问题。
- 解构赋值:从数组或对象中提取值并进行赋值。
- 默认参数:为函数参数提供默认值。
- 扩展运算符(Spread):用于展开数组或对象。
- 模块化(import/export):原生支持模块化。
- 生成器函数(Generator):可以暂停和恢复的函数,支持异步编程。
6. ES5 中的类和 ES6 中的 class 有什么区别?
ES5 中的类:
ES5 使用函数构造器和原型链模拟类:
function Person(name) {this.name = name;
}
Person.prototype.greet = function() {console.log('Hello, ' + this.name);
};
ES6 中的 class:
ES6 引入了 class
语法,更接近传统面向对象编程语言:
class Person {constructor(name) {this.name = name;}greet() {console.log('Hello, ' + this.name);}
}
区别:
- 语法简洁:ES6 的
class
语法更简洁,且更易于理解。 - 继承:ES6 支持
extends
和super
来进行类的继承。 - 构造函数:ES6 中的
constructor
是必须的。
7. 什么是 ES6 中的 Promise? 它的使用场景有哪些?
Promise:
Promise
是 ES6 引入的用于处理异步操作的对象。它代表了一个异步操作的最终完成(或失败)及其结果值的表示。
基本用法:
let promise = new Promise((resolve, reject) => {let success = true; // 假设异步操作是否成功if (success) {resolve("成功");} else {reject("失败");}
});promise.then(result => console.log(result)) // 成功时执行.catch(error => console.log(error)); // 失败时执行
使用场景:
- 网络请求:可以用来封装
fetch
或XMLHttpRequest
,简化异步调用。 - 文件操作:读取文件、数据库操作等。
- 并行异步操作:
Promise.all()
用于处理多个并行异步任务。
8. ES6 中的 Reflect 对象有什么用?
Reflect
是一个内置的对象,它提供了一些方法,用于操作 JavaScript 对象的属性。这些方法与 Object
对象的方法类似,但 Reflect
提供了一
些更强大的功能:
- 拦截器和代理:在代理对象时,
Reflect
能够更好地拦截操作。 - 方法的统一性:
Reflect
中的方法返回值更一致,能返回操作是否成功,适用于操作属性、方法调用、反射等。
例如:
let obj = { name: 'Alice' };
console.log(Reflect.has(obj, 'name')); // true
9. 什么是浏览器的同源策略? 为什么要有同源策略?
同源策略是浏览器的一项安全功能,用于限制从一个源加载的文档或脚本如何与来自不同源的资源进行交互。源由协议、主机和端口号组成。
同源策略的目的是:
- 保护用户隐私:避免恶意网站窃取用户的数据。
- 防止恶意操作:防止某些网站执行非法的操作,操控其他网站的数据。
10. 怎么解决跨域问题?
跨域问题可以通过以下几种方式解决:
- CORS(跨域资源共享):服务器通过设置
Access-Control-Allow-Origin
等头部来允许跨域访问。 - JSONP:通过
<script>
标签的跨域特性,传递回调函数来获取数据。 - 代理服务器:通过在本地服务器或代理服务器进行请求转发来解决跨域问题。
11. 浏览器的本地存储方式有哪些,有什么区别,分别有哪些应用场景?
-
Cookies:
- 大小限制:每个域名约 4KB。
- 特点:每次请求会自动发送到服务器。
- 应用场景:用于保存用户登录状态、会话信息。
-
localStorage:
- 大小限制:每个域名通常 5MB。
- 特点:数据不会自动发送给服务器。
- 应用场景:保存用户偏好设置、缓存数据。
-
sessionStorage:
- 大小限制:每个域名通常 5MB。
- 特点:与
localStorage
类似,但会话结束后数据丢失。 - 应用场景:用于存储会话级别的信息。
-
IndexedDB:
- 大小限制:更大,依赖于浏览器的具体实现。
- 特点:结构化数据存储,可以存储对象。
- 应用场景:用于存储大量数据,如离线缓存。
12. 什么是回流和重绘? 什么场景下会触发? 怎么减少回流和重绘?
回流 (Reflow):
回流是指浏览器重新计算元素的布局,通常发生在元素的几何属性(如宽高、位置)发生变化时。
触发回流的操作:
- 改变元素的尺寸(如
width
,height
)。 - 改变布局(如
display
、position
)。 - 改变字体大小。
重绘 (Repaint):
重绘是指浏览器重新渲染元素的样式,但不会重新计算布局,通常是样式变化但不影响布局。
触发重绘的操作:
- 改变颜色、背景色、边框等样式。
减少回流和重绘的策略:
- 批量修改 DOM:避免多次单独修改 DOM,批量进行修改。
- 使用 CSS 类代替直接修改样式:将多个样式变更放到一个类中,然后切换类。
- 避免频繁修改布局属性:尽量减少修改宽高、位置等会触发回流的样式属性。
- 使用
transform
和opacity
代替top
、left
来动画元素,这样可以避免触发回流。