2025前端面试题及答案(详细)
HTML5 的新特性有哪些?
简约版本:
“HTML5 新特性主要体现在六个方面:
第一,语义化标签,比如 header、footer、nav 等,让页面结构更清晰;
第二,表单增强,新增了 date、email 等类型和 placeholder、required 等属性;
第三,多媒体支持,提供了 audio 和 video 标签,不再依赖 Flash;
第四,图形绘制,有 canvas 和 svg;
第五,本地存储,提供了 localStorage 和 sessionStorage;
第六,一些新的 API,比如 WebSocket 实时通信、Web Worker 多线程、地理定位、拖拽 API 和 History API 等。
总体来说,HTML5 更加语义化、增强了交互体验,也提高了性能和可用性。”
详细版本:
主要分为 语义化标签、表单增强、多媒体支持、图形绘制、本地存储、API 六大类:
语义化标签
新增结构标签:
header
、footer
、nav
、section
、article
、aside
、main
作用:提升页面语义,利于 SEO 和可访问性
表单增强
新的表单控件:
date
、email
、url
、number
、range
、color
、search
新的属性:
required
、placeholder
、pattern
、autofocus
多媒体支持
原生音视频标签:
<audio>
、<video>
支持多种格式,不依赖 Flash
图形绘制
canvas
:支持 2D 图形绘制svg
:矢量图形支持
本地存储
localStorage
:长期存储,容量比 cookie 大sessionStorage
:会话级存储
新的 API
地理定位:
navigator.geolocation
拖放 API:
drag & drop
Web Worker:多线程处理
WebSocket:双向实时通信
History API:控制浏览器历史记录
ARIA / 无障碍开发面试题 & 答案
Q1. 什么是 ARIA?
ARIA(Accessible Rich Internet Applications)是一组 HTML 属性,主要以 aria-
开头。它的作用是帮助辅助技术(比如屏幕阅读器)理解页面结构和交互状态。
在现代前端中,我们经常用自定义组件(比如用 div
模拟按钮),这些元素本身没有语义,屏幕阅读器读不懂。ARIA 就是用来补充语义信息,让页面对残障用户更加友好。
👉 可以补充一句:ARIA 不会改变视觉效果,只影响辅助技术的理解。
Q2. 常见的 ARIA 属性有哪些?举例
1.角色(role):定义元素的语义角色,帮助辅助技术识别它是什么。
banner
(页面头部)navigation
(导航区域)main
(主要内容)complementary
(补充内容)contentinfo
(页脚信息)search
(搜索区域)button
(按钮)checkbox
(复选框)radio
(单选按钮)tab
(选项卡)tabpanel
(选项卡面板)tooltip
(提示信息)dialog
(对话框)article
(文章)heading
(标题)list
(列表)listitem
(列表项)table
(表格)等等
2. 状态/属性(aria-*):描述元素当前状态,比如展开/选中/禁用等。
aria-label
:给元素添加描述信息。aria-hidden
:让读屏器忽略某个元素。aria-expanded
:表示菜单是否展开。aria-checked
:复选框是否选中。aria-live
:提示动态区域,读屏器会自动播报内容更新。
<div role="button" aria-pressed="false" tabindex="0">点我
</div>
//role="button" → 让它被识别为按钮
//tabindex="0" → 允许键盘 Tab 聚焦
//aria-pressed="false" → 状态(未按下)
Q3. 开发无障碍应用时,ARIA 和语义化 HTML 有什么关系?
最佳实践是 优先使用语义化 HTML 标签(比如 <button>
、<nav>
、<header>
),因为浏览器和读屏器天然支持。
ARIA 是 补充手段,当我们用自定义组件(比如 div
写的按钮)时,再用 role
和 aria-*
属性来增强可访问性。
一句话总结:能用原生标签就用原生,实在不行才用 ARIA。
Q4. 如何让自定义组件可访问?
- 给组件加
role
(补充语义)。 - 用
aria-*
属性描述状态(比如展开、禁用、选中)。 - 确保 键盘可操作,加
tabindex="0"
并监听 Enter/Space 键事件。 - 动态内容要用
aria-live
或role="alert"
提示用户。
👉 这样屏幕阅读器和键盘用户都能正常使用。
Q5. 在实际工作中,你做过哪些可访问性优化?
参考回答:(随便挑 2–3 点说)
用语义化标签代替纯
div
/span
,比如<button>
。给图片加
alt
,给图标加aria-hidden="true"
。自定义组件时,补充
role
和aria-*
属性。处理键盘可访问性(Tab 聚焦、Enter/Space 触发)。
动态提示(比如表单报错)用
aria-live
让读屏器播报。
Q6. ARIA 有什么使用原则?
参考回答:
能用 HTML 原生语义就用原生,不要过度依赖 ARIA。
只在必要时添加,避免属性过多导致读屏器混乱。
测试可访问性:实际用屏幕阅读器(NVDA、VoiceOver)和键盘来测试。
CSS 相关问题
1. CSS 选择器的优先级是怎么计算的?
👉 内联样式 > ID 选择器 > 类 / 属性 / 伪类选择器 > 标签选择器 > 通配符 > 继承 > 浏览器默认样式。
数值记忆法:行内(1000) > ID(100) > 类/伪类(10) > 标签/伪元素(1)。
2. 标准盒模型和 IE 盒模型有什么区别?
👉 标准盒模型:width
只包含 content,不含 padding 和 border。
👉 IE 盒模型:width
包含 content + padding + border。
元素总宽度 = content + padding + border + margin
元素总高度 = content + padding + border + margin
box-sizing: content-box; /* 标准盒模型(默认) */
box-sizing: border-box; /* 怪异盒模型 */
3.如何创建响应式布局?
👉 媒体查询(Media Queries):根据不同屏幕宽度设置不同的样式。
/* 默认样式:移动端优先 */
.container { font-size: 14px; }/* 平板端 */
@media (min-width: 768px) {.container { font-size: 16px; }
}/* PC 端 */
@media (min-width: 1200px) {.container { font-size: 18px; }
}
👉 流式布局(Fluid Layout):使用百分比 %
、视口单位 vw/vh
、弹性单位 em/rem
等,而不是固定 px
。
👉 弹性盒子(Flexbox):用 flex
让子元素在父容器中自动适应。
👉网格布局(CSS Grid):Grid 天生适合响应式布局。 自动根据屏幕宽度决定列数。
.grid {display: grid;grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));gap: 20px;
}
👉响应式图片 / 媒体:使用 max-width: 100%
让图片不超出容器。
👉移动端适配(viewport 设置):确保在不同设备上缩放正常。
<meta name="viewport" content="width=device-width, initial-scale=1.0">
移动端优先 是主流做法:先写小屏,再通过媒体查询适配大屏。
4. position 的值有哪几种?区别是什么?
static:默认定位,不受 top/left 影响。
relative:相对自身定位,占据原位置。
absolute:相对最近的非 static 祖先定位。
fixed:相对浏览器窗口定位,不随滚动条移动。
sticky:结合 relative 和 fixed,根据滚动位置切换。
5. z-index 为什么有时不起作用?
没有定位属性
z-index
只在元素设置了定位(position: relative | absolute | fixed | sticky
)时才生效。如果是
position: static
(默认),z-index
不会起作用。
层叠上下文(Stacking Context)影响
元素所在的层叠上下文会限制它的
z-index
效果。每个层叠上下文内部的元素
z-index
只在自己“上下文”里比较,无法跨层叠上下文提升。触发层叠上下文的情况(常见):
设置了
position
且z-index
值不是auto
;设置了
opacity < 1
;设置了
transform
、filter
、perspective
、clip-path
等属性;设置了
isolation: isolate
。
父子关系影响
子元素的
z-index
受限于父元素所在的层叠上下文,即使子元素z-index
再大,也不会覆盖在父元素所在层叠上下文之外的兄弟元素之上。
浏览器默认层叠顺序
如果
z-index
相同或没设置,浏览器会按照以下顺序叠放:背景和边框(最底层)
负
z-index
元素块级元素(按 HTML 顺序)
浮动元素
行内元素(按 HTML 顺序)
正
z-index
元素(数值越大越靠上)
6.如何实现水平垂直居中?
- 使用 Flex 布局(推荐 ✅)
.parent {display: flex;justify-content: center; /* 水平居中 */align-items: center; /* 垂直居中 */
}
.child {width: 100px;height: 100px;
}
- 使用 Grid 布局
.parent {display: grid;place-items: center; /* 水平 + 垂直居中 */
}
- 绝对定位 + transform
.parent {position: relative;
}
.child {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);
}
- 绝对定位 + margin auto
.parent {position: relative;
}
.child {position: absolute;top: 0; bottom: 0;left: 0; right: 0;margin: auto;width: 100px;height: 100px;
}
- 传统方法:表格布局 / line-height
//第一。表格法(适合文字/小组件):.parent {display: table-cell;text-align: center; /* 水平 */vertical-align: middle; /* 垂直 */
}// 第二。line-height 法(适合单行文本):.parent {height: 100px;line-height: 100px; /* 设置等于高度 */text-align: center;
}
7. 什么是 BFC?有什么作用?
👉定义
BFC 全称 Block Formatting Context(块级格式化上下文)。
它是页面中的一个独立渲染区域,内部元素的布局不会影响到区域外部。
👉如何触发 BFC(常见几种方式)
设置
float
值(left
/right
,但不是none
)。设置
position
为absolute
/fixed
。设置
display
为inline-block
、table-cell
、flex
、grid
等。设置
overflow
为hidden
、auto
、scroll
。
👉 BFC 的特性 / 作用
清除浮动:父元素触发 BFC 后,可以包含子元素的浮动,不会出现高度塌陷。
避免外边距重叠(margin collapse):同一个 BFC 内的块级元素上下
margin
会重叠,不同 BFC 之间则不会。隔离作用:BFC 区域和外部独立,内部元素不会影响外部布局。
自适应两栏布局:利用 BFC 可以实现一边浮动一边自适应。
👉 常见应用场景
✅ 清除浮动问题
✅ 阻止
margin
合并✅ 实现多栏布局(左侧固定宽度,右侧自适应)
JavaScript 作用域 & 闭包
1. 什么是作用域?有哪些类型?
作用域(Scope)是指程序中变量和函数的可访问范围。
JavaScript 的作用域类型主要有:
全局作用域:在代码最外层声明的变量和函数,整个程序都可以访问。
函数作用域:每个函数调用时都会创建自己的作用域,函数内部定义的变量只能在函数内部访问。
块级作用域:
let
和const
声明的变量只在代码块{}
内有效,例如 if、for 中。
👉 JavaScript 采用 词法作用域(在函数定义时就决定了作用域,而不是运行时)。
2. 什么是作用域链?
当访问一个变量时,JavaScript 引擎会:
先在当前作用域查找
如果找不到,就沿着外层作用域逐级向上查找
直到全局作用域,如果还没找到,就抛出
ReferenceError
这种 逐级查找的链条 就叫 作用域链。
👉 本质:函数持有外部作用域的引用。
3. 什么是执行上下文(Execution Context)?
执行上下文是 JavaScript 代码运行时的环境。
每次执行一段代码时,都会创建一个执行上下文,包括:
变量环境(var 声明的变量、函数声明)
词法环境(let、const 声明的变量)
this 绑定(当前执行环境的 this 指向)
执行上下文分为三类:
全局上下文(页面首次运行时创建,只有一个)
函数上下文(每次调用函数时都会创建)
eval 上下文(eval 代码执行时创建,不推荐使用)
4. 什么是变量提升?
JavaScript 在代码执行前,会先进行“预编译”阶段:
var
声明的变量会提升到作用域顶端,默认值是undefined
。函数声明(function declaration)会整体提升,可以在声明前调用。
let
和const
也会提升,但存在 暂时性死区(TDZ),在声明前使用会报错。
console.log(a); // undefined
var a = 10;console.log(b); // ReferenceError
let b = 20;
5.this 在不同场景下的指向是什么?
普通函数调用 → 在非严格模式下指向全局对象(
window
),严格模式下是undefined
。对象方法调用 → 指向调用该方法的对象。
构造函数调用(new) → 指向新创建的实例对象。
显式绑定 → 使用
call
、apply
、bind
可以手动指定 this。箭头函数 → 没有自己的 this,继承外层作用域的 this。
6. 什么是闭包?
闭包是指一个函数与其周围的词法环境(变量和状态)的绑定。通过闭包,函数可以访问其外部作用域中的变量,即使在外部函数执行结束后,这些变量依然可以被内部函数访问和使用。
闭包的本质是函数与其定义时的作用域环境的结合。当一个函数返回另一个函数时,返回的内部函数会保留对外部函数作用域的引用,从而形成闭包。
条件(记三个字:函数 + 引用)
必须有 函数嵌套
内部函数 用到了外部变量
外部函数执行完,但变量 仍被引用
作用:
数据持久化:能够让某些变量长期保存在内存中,实现状态的记忆。
封装与私有化:利用闭包,可以模拟私有变量,避免外部随意修改。
函数式编程:闭包是实现回调函数、高阶函数、函数工厂等的重要基础。
缺点:
(1)容易在循环中出 bug(var
导致所有闭包共享同一个变量)
(1)循环里的闭包问题
for (var i = 0; i < 3; i++) {setTimeout(() => console.log(i), 1000);
}
// 输出:3 3 3原因:var i 是同一个函数作用域,闭包引用的是同一个 i解决方法:for (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 1000);
}
// 输出:0 1 2或者使用 IIFE(立即执行函数):for (var i = 0; i < 3; i++) {((i) => {setTimeout(() => console.log(i), 1000);})(i);
}
(2)内存泄漏
闭包持有外部函数的变量引用,如果使用不当,可能导致内存不能及时释放。
7. 为什么闭包会导致内存泄漏?
因为闭包会让外部函数的变量一直被引用,垃圾回收机制(GC)无法释放这部分内存。
解决方法:
在不需要闭包时,将其引用置为
null
避免过度使用闭包
8. 闭包的实际应用场景有哪些?
(1)封装私有变量
function Counter() {let count = 0;return {inc: () => ++count,dec: () => --count};
}
(2)事件处理器(回调函数中访问外部变量)
(3)函数柯里化(currying)
function add(a) {return function(b) {return a + b;};
}
9. 什么是事件循环(Event Loop)?宏任务和微任务有什么区别?
JavaScript 是单线程的,采用 事件循环机制 来处理异步任务。
执行顺序:
先执行 同步代码(主线程)。
同步执行完后,检查 微任务队列(microtask queue),依次执行完。
微任务清空后,再执行一个 宏任务(macrotask)。
重复以上过程,形成循环。
常见宏任务(macrotask): setTimeout
、setInterval
、setImmediate
、I/O、UI 渲染。
常见微任务(microtask): Promise.then/catch/finally
、MutationObserver
、queueMicrotask
、process.nextTick(Node)
。
👉 口诀:先同步 → 再微任务 → 再宏任务。
10. 什么是原型和原型链?
原型(prototype):
每个函数都有一个prototype
属性,用于存放实例共享的方法和属性。原型链(Prototype Chain):
对象在查找属性时,会先在自己本身查找,如果没有,就沿着__proto__
向上查找父级原型,直到Object.prototype
,再到null
停止。
function Person() {}
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
👉 原型链的本质是 对象属性查找机制。
11. async / await 的底层原理是什么?
async/await
是 ES2017 引入的语法糖,用来写异步代码。async
函数默认返回一个Promise
。await
用来等待一个Promise
的结果,函数会“暂停”执行,等结果出来再继续。👉 作用:让异步代码写起来更像同步代码,可读性更好。
底层原理:
async/await
是Promise
的语法糖,本质是基于 Promise + Generator 实现的。await
会暂停函数的执行,把后续代码放进微任务队列,等Promise
完成后再继续执行。
👉 关键点:await
会把后续代码放到 微任务队列。
12. Promise 是什么?解决了什么问题?
Promise
是 ES6 引入的一种 异步编程的解决方案,用于表示一个可能在未来某个时间点才会完成的操作结果。本质是一个 状态机,有三种状态:
pending
(进行中)、fulfilled
(已成功)、rejected
(已失败),且状态 不可逆。Promise
提供了统一的.then/.catch/.finally
接口,使得我们可以通过链式调用来组织异步代码。
解决的问题:
(1)回调地狱(Callback Hell)
传统异步依赖多层嵌套回调,代码难以维护;
Promise
通过链式调用让异步逻辑更加扁平化、清晰。
(2)错误处理分散
回调模式下错误需要逐层处理;
Promise
支持 错误冒泡,集中用.catch
捕获异常。
(3)异步流程控制
Promise.all / race / allSettled / any
提供了强大的并发控制能力,方便管理多个异步任务。