个人空间:
https://blog.csdn.net/m0_73589512
https://blog.csdn.net/m0_73589512?spm=1011.2415.3001.5343接续上文:
目录
[深入理解浏览器中的 JavaScript:BOM、DOM、网络与性能优化](#深入理解浏览器中的 JavaScript:BOM、DOM、网络与性能优化)
[1.1 location 对象:URL 操作核心](#1.1 location 对象:URL 操作核心)
[核心属性(URL 解析)](#核心属性(URL 解析))
[1.2 history 对象:历史记录与 SPA 路由](#1.2 history 对象:历史记录与 SPA 路由)
[关键考点:popstate 事件](#关键考点:popstate 事件)
[1.3 navigator 对象:浏览器与系统信息](#1.3 navigator 对象:浏览器与系统信息)
[1.4 screen 与元素尺寸:视口与布局计算](#1.4 screen 与元素尺寸:视口与布局计算)
[关键考点:clientHeight vs offsetHeight](#关键考点:clientHeight vs offsetHeight)
[2.1 事件模型:捕获与冒泡](#2.1 事件模型:捕获与冒泡)
[2.2 事件委托:性能优化核心](#2.2 事件委托:性能优化核心)
[传统方式 vs 事件委托](#传统方式 vs 事件委托)
[三、网络请求:XMLHttpRequest 与缓存策略](#三、网络请求:XMLHttpRequest 与缓存策略)
[3.1 XMLHttpRequest 完整用法](#3.1 XMLHttpRequest 完整用法)
[3.2 核心考点:HTTP 方法与状态码](#3.2 核心考点:HTTP 方法与状态码)
[常用 HTTP 方法(RESTful 规范)](#常用 HTTP 方法(RESTful 规范))
[3.3 缓存策略:强缓存与协商缓存](#3.3 缓存策略:强缓存与协商缓存)
[缓存优先策略示例(Service Worker)](#缓存优先策略示例(Service Worker))
[4.1 高频面试题总结](#4.1 高频面试题总结)
[4.2 性能优化实战](#4.2 性能优化实战)
[1. 减少重排重绘](#1. 减少重排重绘)
[2. 防抖与节流](#2. 防抖与节流)
[3. 虚拟列表(大数据渲染优化)](#3. 虚拟列表(大数据渲染优化))
[浏览器 JS 核心面试题汇总(高频考点 + 标准答案)](#浏览器 JS 核心面试题汇总(高频考点 + 标准答案))
[1. 什么是同源策略?同源的判断依据是什么?](#1. 什么是同源策略?同源的判断依据是什么?)
[2. location.assign ()、location.replace ()、location.reload () 的区别?](#2. location.assign ()、location.replace ()、location.reload () 的区别?)
[3. history.pushState () 和 history.replaceState () 的作用?如何监听浏览器前进 / 后退?](#3. history.pushState () 和 history.replaceState () 的作用?如何监听浏览器前进 / 后退?)
[4. 如何通过 navigator 判断浏览器类型和设备类型?](#4. 如何通过 navigator 判断浏览器类型和设备类型?)
[5. clientHeight、offsetHeight、scrollHeight 的区别?](#5. clientHeight、offsetHeight、scrollHeight 的区别?)
[二、DOM 事件模型高频题](#二、DOM 事件模型高频题)
[1. JS 事件流的三个阶段是什么?如何控制事件传播?](#1. JS 事件流的三个阶段是什么?如何控制事件传播?)
[2. 什么是事件委托?它的优势是什么?适用场景?](#2. 什么是事件委托?它的优势是什么?适用场景?)
[3. event.preventDefault () 和 event.stopPropagation () 的区别?](#3. event.preventDefault () 和 event.stopPropagation () 的区别?)
[4. 相同元素绑定多个同类事件,执行顺序是什么?如何阻止?](#4. 相同元素绑定多个同类事件,执行顺序是什么?如何阻止?)
[1. XMLHttpRequest 的核心步骤是什么?readyState 的取值含义?](#1. XMLHttpRequest 的核心步骤是什么?readyState 的取值含义?)
[2. HTTP 状态码 304 的含义是什么?与浏览器缓存的关系?](#2. HTTP 状态码 304 的含义是什么?与浏览器缓存的关系?)
[3. RESTful API 的核心规范是什么?常用 HTTP 方法的含义?](#3. RESTful API 的核心规范是什么?常用 HTTP 方法的含义?)
[4. 什么是跨域?OPTIONS 请求的作用是什么?](#4. 什么是跨域?OPTIONS 请求的作用是什么?)
[1. 如何减少 DOM 重排和重绘?](#1. 如何减少 DOM 重排和重绘?)
[2. 防抖和节流的区别是什么?实现原理?适用场景?](#2. 防抖和节流的区别是什么?实现原理?适用场景?)
[3. 浏览器的渲染流程是什么?如何优化渲染性能?](#3. 浏览器的渲染流程是什么?如何优化渲染性能?)
[1. HTTP 协议和 XMLHttpRequest/Fetch/axios 的关系?](#1. HTTP 协议和 XMLHttpRequest/Fetch/axios 的关系?)
[2. history 模式和 hash 模式的 SPA 路由有什么区别?](#2. history 模式和 hash 模式的 SPA 路由有什么区别?)
深入理解浏览器中的 JavaScript:BOM、DOM、网络与性能优化
JavaScript 在浏览器中的运行离不开三大核心体系:BOM(浏览器对象模型)、DOM(文档对象模型)和网络请求 。它们共同支撑起前端交互、页面操作和数据通信的基础,也是面试高频考点。本文将系统拆解这些核心知识点,结合实战案例和面试方向,帮你建立完整的浏览器 JS 知识体系。
一、BOM:浏览器对象模型
BOM(Browser Object Model)是操作浏览器功能的 API 集合,核心对象包括location、history、navigator、screen,直接对接浏览器的地址栏、历史记录、系统信息等功能。
1.1 location 对象:URL 操作核心
location封装了当前页面的 URL 信息,提供了 URL 解析和导航功能,是路由管理、地址跳转的关键。
核心属性(URL 解析)
以 https://www.hkc.com:8080/search?class=browser#comments 为例:
console.log(location.href); // 完整URL:"https://www.hkc.com:8080/search?class=browser#comments"
console.log(location.protocol); // 协议:"https:"
console.log(location.host); // 主机(含端口):"www.hkc.com:8080"
console.log(location.hostname); // 主机名:"www.hkc.com"
console.log(location.port); // 端口:"8080"
console.log(location.pathname); // 路径:"/search"
console.log(location.search); // 查询字符串:"?class=browser"
console.log(location.hash); // 锚点:"#comments"
console.log(location.origin); // 源(协议+域名+端口):"https://www.hkc.com:8080"
核心方法(页面导航)
location.assign('https://new.url'); // 导航到新URL,保留历史记录(可后退)
location.replace('https://new.url'); // 替换当前URL,不保留历史记录(不可后退)
location.reload(); // 重新加载页面(默认使用缓存)
location.reload(true); // 强制从服务器加载(忽略缓存)
关键考点:同源判断
"同源" 指 协议、域名、端口号完全相同,是浏览器跨域限制的核心依据。例如:
-
https://www.hkc.com和http://www.hkc.com不同源(协议不同) -
https://www.hkc.com和https://blog.hkc.com不同源(域名不同) -
https://www.hkc.com:80和https://www.hkc.com:8080不同源(端口不同)
1.2 history 对象:历史记录与 SPA 路由
history 管理浏览器的会话历史,核心用于单页应用(SPA)的路由控制,避免页面刷新。
核心方法与属性
// 添加历史记录(不刷新页面)
history.pushState({ page: 1 }, '页面1', '/page1');
// 替换当前历史记录(不刷新页面)
history.replaceState({ page: 2 }, '页面2', '/page2');
// 获取当前状态数据
console.log(history.state); // { page: 2 }
// 历史记录长度
console.log(history.length); // 2
// 前进/后退操作
history.back(); // 后退一步(等同于点击浏览器后退按钮)
history.forward(); // 前进一步(等同于点击浏览器前进按钮)
history.go(-2); // 后退两步(参数为正数前进,负数后退)
关键考点:popstate 事件
监听浏览器前进 / 后退操作,实现 SPA 路由切换:
window.addEventListener('popstate', (event) => {
console.log('路由变化:', event.state); // 获取pushState/replaceState存储的状态
// 执行路由更新逻辑
});
1.3 navigator 对象:浏览器与系统信息
navigator 提供浏览器和操作系统的核心信息,常用于浏览器兼容性判断、用户画像收集。
核心用法
// 用户代理字符串(关键!判断浏览器类型)
const ua = navigator.userAgent;
// 示例:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...
// 浏览器检测函数(面试高频)
function detectBrowser() {
const ua = navigator.userAgent.toLowerCase();
return {
isChrome: ua.includes('chrome') && !ua.includes('edge'),
isFirefox: ua.includes('firefox'),
isSafari: ua.includes('safari') && !ua.includes('chrome'),
isEdge: ua.includes('edge'),
isIE: ua.includes('trident') || ua.includes('msie'),
isMobile: /mobile|android|iphone/i.test(ua)
};
}
// 其他常用属性
console.log(navigator.language); // 浏览器语言:"zh-CN"
console.log(navigator.onLine); // 是否在线:true/false
console.log(navigator.cookieEnabled); // 是否启用Cookie:true/false
// 现代API(拓展功能)
navigator.clipboard.readText(); // 读取剪贴板(需授权)
navigator.geolocation.getCurrentPosition(pos => { // 获取地理位置
console.log('经纬度:', pos.coords.latitude, pos.coords.longitude);
});
1.4 screen 与元素尺寸:视口与布局计算
screen 描述屏幕信息,结合元素尺寸属性,可实现响应式布局、元素定位等功能。


核心尺寸属性
// 视口尺寸(浏览器可视区域)
const viewport = {
innerWidth: window.innerWidth, // 视口宽度(含滚动条)
innerHeight: window.innerHeight, // 视口高度
outerWidth: window.outerWidth, // 浏览器窗口总宽度
outerHeight: window.outerHeight // 浏览器窗口总高度
};
// 屏幕尺寸
const screenInfo = {
width: screen.width, // 屏幕总宽度
height: screen.height, // 屏幕总高度
availWidth: screen.availWidth, // 屏幕可用宽度(不含任务栏)
availHeight: screen.availHeight // 屏幕可用高度
};
// 元素尺寸(面试高频考点)
const el = document.getElementById('myEl');
const elSize = {
// client:内容+内边距,不含边框、滚动条、外边距
clientWidth: el.clientWidth,
clientHeight: el.clientHeight,
// offset:内容+内边距+边框+滚动条
offsetWidth: el.offsetWidth,
offsetHeight: el.offsetHeight,
offsetTop: el.offsetTop, // 相对于父定位元素的顶部距离
offsetLeft: el.offsetLeft, // 相对于父定位元素的左侧距离
// scroll:元素实际内容尺寸(含滚动隐藏部分)
scrollWidth: el.scrollWidth,
scrollHeight: el.scrollHeight,
scrollTop: el.scrollTop, // 垂直滚动距离
scrollLeft: el.scrollLeft // 水平滚动距离
};
关键考点:clientHeight vs offsetHeight
-
clientHeight:内容区 + 内边距(padding),不含边框、滚动条、外边距; -
offsetHeight:内容区 + 内边距 + 边框(border) + 滚动条,不含外边距。
二、DOM:文档对象模型与事件系统
DOM(Document Object Model)是 HTML 文档的结构化表示,提供了操作页面元素的 API。核心重点是 事件模型 和 元素操作。
2.1 事件模型:捕获与冒泡
JS 的事件传播遵循 "捕获→目标→冒泡" 的完整流程,这是事件委托的核心原理。
事件流详解
<div id="parent">
<button id="child">点击我</button>
</div>
// 捕获阶段:从顶层(document)到目标元素(child)
document.addEventListener('click', () => console.log('捕获:document'), true);
parent.addEventListener('click', () => console.log('捕获:parent'), true);
// 冒泡阶段:从目标元素(child)到顶层(document)
parent.addEventListener('click', () => console.log('冒泡:parent'), false);
document.addEventListener('click', () => console.log('冒泡:document'), false);
点击按钮后输出顺序:
捕获:document → 捕获:parent → 冒泡:parent → 冒泡:document
事件对象核心方法(面试高频)
element.addEventListener('click', (event) => {
// 阻止事件传播(停止捕获/冒泡)
event.stopPropagation();
// 阻止同元素的其他事件监听器执行
event.stopImmediatePropagation();
// 阻止默认行为(如a标签跳转、表单提交)
event.preventDefault();
// 关键属性
console.log(event.target); // 实际触发事件的元素(点击的按钮)
console.log(event.currentTarget); // 绑定事件的元素(parent)
console.log(event.eventPhase); // 事件阶段:1=捕获,2=目标,3=冒泡
});
2.2 事件委托:性能优化核心
事件委托利用 冒泡机制,将子元素的事件统一绑定到父元素,减少事件监听器数量,提升性能(尤其适用于列表、动态渲染元素)。
传统方式 vs 事件委托
<ul class="list">
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
<!-- 可能动态添加更多li -->
</ul>
传统方式(低效):
const lis = document.querySelectorAll('li');
lis.forEach(li => {
li.addEventListener('click', () => {
console.log('点击了:', li.textContent);
});
});
// 动态添加的li需要重新绑定事件
事件委托(高效):
const list = document.querySelector('.list');
list.addEventListener('click', (event) => {
// 判断点击的是li元素
if (event.target.tagName === 'LI') {
console.log('点击了:', event.target.textContent);
// 获取li索引
const index = Array.from(list.children).indexOf(event.target);
console.log('索引:', index);
}
});
// 动态添加的li自动继承事件,无需重新绑定
三、网络请求:XMLHttpRequest 与缓存策略
前端与后端通信的核心是网络请求,XMLHttpRequest 是传统方案,现代开发中常用 Fetch API 或 Axios,但理解 XHR 是掌握网络请求原理的关键。
3.1 XMLHttpRequest 完整用法
// 1. 创建XHR对象
const xhr = new XMLHttpRequest();
// 2. 配置请求(方法、URL、异步标识)
xhr.open('POST', 'https://api.example.com/data', true);
// 3. 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer token123');
// 4. 监听状态变化
xhr.onreadystatechange = function() {
// readyState=4 表示请求完成
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status < 300) {
// 成功:解析响应数据
const data = JSON.parse(xhr.responseText);
console.log('请求成功:', data);
} else {
// 失败:处理错误
console.error('请求失败:', xhr.status);
}
}
};
// 5. 监听进度(可选)
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const progress = (e.loaded / e.total) * 100;
console.log('上传进度:', progress + '%');
}
};
// 6. 发送请求(POST请求需传参)
xhr.send(JSON.stringify({ username: 'test' }));
// 7. 中止请求(可选)
// xhr.abort();
3.2 核心考点:HTTP 方法与状态码
常用 HTTP 方法(RESTful 规范)
-
GET:查询数据(幂等,可缓存)
-
POST:提交数据(非幂等,不可缓存)
-
PUT:更新数据(幂等)
-
DELETE:删除数据(幂等)
-
OPTIONS:预检请求(跨域时触发)
关键状态码
-
2xx:成功(200 OK、204 No Content)
-
3xx:重定向 / 缓存(301 永久重定向、304 协商缓存命中)
-
4xx:客户端错误(400 Bad Request、401 未授权、403 禁止访问、404 资源不存在)
-
5xx:服务端错误(500 服务器内部错误、503 服务不可用)
3.3 缓存策略:强缓存与协商缓存
浏览器缓存是性能优化的核心,分为强缓存和协商缓存:
-
强缓存 :通过
Cache-Control或Expires控制,直接从缓存读取,不发请求; -
协商缓存 :通过
ETag/If-None-Match或Last-Modified/If-Modified-Since控制,需发请求验证缓存是否有效。
缓存优先策略示例(Service Worker)
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 缓存命中则返回缓存,否则从网络获取
return response || fetch(event.request)
.then(networkResponse => {
// 缓存新的响应
caches.open('v1').then(cache => {
cache.put(event.request, networkResponse.clone());
});
return networkResponse;
});
})
);
});
四、核心面试考点与性能优化
4.1 高频面试题总结
-
BOM 相关:
-
同源策略的定义(协议、域名、端口相同);
-
location.assign与location.replace的区别; -
如何通过
history实现 SPA 路由。
-
-
DOM 事件相关:
-
事件流的三个阶段(捕获、目标、冒泡);
-
事件委托的原理与应用场景;
-
stopPropagation与stopImmediatePropagation的区别。
-
-
网络相关:
-
304 状态码的含义(协商缓存命中);
-
OPTIONS 请求的作用(跨域预检);
-
强缓存与协商缓存的区别。
-
4.2 性能优化实战
1. 减少重排重绘
-
批量 DOM 操作:使用
DocumentFragment一次性插入多个元素; -
避免频繁修改样式:通过修改
class而非直接操作style; -
脱离文档流操作:使用
position: absolute/fixed减少重排影响。
2. 防抖与节流
// 防抖:触发后延迟执行,频繁触发则重置延迟
function debounce(func, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
// 节流:固定时间内只执行一次
function throttle(func, limit) {
let inThrottle;
return (...args) => {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 应用:监听窗口resize
window.addEventListener('resize', debounce(handleResize, 300));
3. 虚拟列表(大数据渲染优化)
核心思路:只渲染可视区域内的元素,避免一次性渲染大量 DOM:
class VirtualList {
constructor(container, items, itemHeight) {
this.container = container;
this.items = items; // 所有数据
this.itemHeight = itemHeight; // 单个元素高度
this.visibleCount = Math.ceil(container.clientHeight / itemHeight); // 可视元素数量
this.container.addEventListener('scroll', () => this.render());
this.render();
}
render() {
const scrollTop = this.container.scrollTop;
const startIndex = Math.floor(scrollTop / this.itemHeight); // 可视区域起始索引
const endIndex = startIndex + this.visibleCount; // 可视区域结束索引
const visibleItems = this.items.slice(startIndex, endIndex); // 只取可视数据
// 渲染可视元素(省略DOM操作)
this.container.innerHTML = visibleItems.map(item => `<div style="height: ${this.itemHeight}px">${item}</div>`).join('');
// 偏移容器,模拟滚动效果
this.container.style.transform = `translateY(${startIndex * this.itemHeight}px)`;
}
}
五、总结
浏览器中的 JavaScript 核心围绕 "交互""操作""通信" 三大场景:
-
BOM 负责与浏览器交互(地址、历史、系统信息);
-
DOM 负责操作页面元素(事件、布局、渲染);
-
网络请求负责与后端通信(数据交互、缓存策略)。
掌握这些知识点,不仅能应对日常开发中的交互、布局、性能问题,更能轻松搞定面试中的高频考点。建议结合实战多练习(如实现 SPA 路由、封装事件委托、优化大数据渲染),将理论转化为实际能力。
浏览器 JS 核心面试题汇总(高频考点 + 标准答案)
以下是浏览器环境中 JavaScript 的高频面试题,涵盖 BOM、DOM、事件模型、网络请求、性能优化等核心模块,每个题目均附标准答案和关键考点解析,适合面试冲刺和知识梳理。
一、BOM(浏览器对象模型)高频题
1. 什么是同源策略?同源的判断依据是什么?
标准答案:
同源策略是浏览器的安全机制,限制不同源的文档或脚本对当前文档的资源访问。
判断依据:协议、域名、端口号三者完全相同(缺一不可)。
示例:
-
同源:
https://www.test.com和https://www.test.com:443(默认端口可省略); -
不同源:
https://www.test.com和http://www.test.com(协议不同)、https://blog.test.com(域名不同)、https://www.test.com:8080(端口不同)。
关键考点:跨域限制的核心原因,后续跨域解决方案的前置知识。
2. location.assign ()、location.replace ()、location.reload () 的区别?
标准答案:
-
location.assign('url'):导航到新 URL,保留当前页面历史记录(可通过浏览器后退返回); -
location.replace('url'):替换当前 URL,不保留历史记录(无法后退到原页面); -
location.reload():重新加载当前页面,默认使用缓存;location.reload(true)强制从服务器加载(忽略缓存)。
关键考点:路由跳转的历史记录管理,SPA 路由实现的基础。
3. history.pushState () 和 history.replaceState () 的作用?如何监听浏览器前进 / 后退?
标准答案:
-
两者均用于修改浏览器历史记录,
不触发页面刷新
(SPA 路由核心 API);
-
pushState(state, title, url):新增一条历史记录,URL 可自定义; -
replaceState(state, title, url):替换当前历史记录,不新增条目;
-
-
监听前进 / 后退:通过
popstate事件,只能监听浏览器主动触发的历史记录变化(push/replaceState 调用不会触发)。
示例代码:
window.addEventListener('popstate', (e) => {
console.log('历史记录变化:', e.state); // 获取push/replaceState存储的state数据
});
关键考点:SPA 路由的实现原理(history 模式)。
4. 如何通过 navigator 判断浏览器类型和设备类型?
标准答案:
通过 navigator.userAgent(用户代理字符串)解析,核心逻辑:
function detectEnv() {
const ua = navigator.userAgent.toLowerCase();
return {
browser: {
isChrome: ua.includes('chrome') && !ua.includes('edge'),
isFirefox: ua.includes('firefox'),
isSafari: ua.includes('safari') && !ua.includes('chrome'),
isIE: ua.includes('trident') || ua.includes('msie'),
isEdge: ua.includes('edge')
},
isMobile: /mobile|android|iphone|ipad/i.test(ua)
};
}
关键考点:浏览器兼容性处理的前置逻辑,用户画像收集方案。
5. clientHeight、offsetHeight、scrollHeight 的区别?
标准答案:
三者均用于获取元素尺寸,核心差异在于包含的范围:
-
clientHeight:内容区高度 + 内边距(padding),不含边框、滚动条、外边距; -
offsetHeight:内容区高度 + 内边距 + 边框(border) + 滚动条,不含外边距; -
scrollHeight:元素实际内容高度(含滚动隐藏部分),不含滚动条、外边距。
关键考点:元素尺寸计算,滚动逻辑实现,响应式布局开发。
二、DOM 事件模型高频题
1. JS 事件流的三个阶段是什么?如何控制事件传播?
标准答案:
事件流顺序:捕获阶段 → 目标阶段 → 冒泡阶段;
-
捕获阶段:事件从顶层(document)向下传播到目标元素;
-
目标阶段:事件到达实际触发的元素;
-
冒泡阶段:事件从目标元素向上传播回顶层。
控制事件传播的方法:
-
event.stopPropagation():阻止事件继续传播(中断捕获 / 冒泡); -
event.stopImmediatePropagation():不仅阻止传播,还会阻止当前元素上的其他同类事件监听器执行。
关键考点:事件委托的核心原理,事件传播的实际应用。
2. 什么是事件委托?它的优势是什么?适用场景?
标准答案:
事件委托是利用事件冒泡机制,将子元素的事件统一绑定到父元素,通过判断事件源(event.target)执行对应逻辑。
优势:
-
减少事件监听器数量,降低内存消耗;
-
支持动态新增的子元素(无需重新绑定事件);
-
简化代码维护,统一事件处理逻辑。
适用场景:列表渲染、动态元素(如表单新增字段)、大量相似元素的事件处理。
示例代码:
const list = document.querySelector('.list');
list.addEventListener('click', (e) => {
if (e.target.tagName === 'LI') { // 判断事件源
console.log('点击了:', e.target.textContent);
}
});
关键考点:性能优化方案,DOM 事件的实际应用。
3. event.preventDefault () 和 event.stopPropagation () 的区别?
标准答案:
-
event.preventDefault():阻止元素的默认行为(如 a 标签跳转、表单提交、鼠标右键菜单),不影响事件传播; -
event.stopPropagation():阻止事件在捕获 / 冒泡阶段传播,不影响默认行为。
示例:点击 a 标签时,既阻止跳转(preventDefault),又阻止事件冒泡(stopPropagation)。
关键考点:事件对象的核心方法,避免混淆默认行为和事件传播。
4. 相同元素绑定多个同类事件,执行顺序是什么?如何阻止?
标准答案:
-
执行顺序:按事件绑定的先后顺序执行;
-
阻止后续事件:使用
event.stopImmediatePropagation(),会中断当前元素上后续同类事件的执行,同时阻止事件传播。
示例代码:
const btn = document.querySelector('button');
btn.addEventListener('click', () => console.log('事件1'));
btn.addEventListener('click', (e) => {
console.log('事件2');
e.stopImmediatePropagation(); // 阻止后续事件和传播
});
btn.addEventListener('click', () => console.log('事件3')); // 不会执行
关键考点:事件监听器的执行机制,特殊场景下的事件控制。
三、网络请求与缓存高频题
1. XMLHttpRequest 的核心步骤是什么?readyState 的取值含义?
标准答案:
核心步骤(异步请求):
-
创建 XHR 对象:
const xhr = new XMLHttpRequest(); -
配置请求:
xhr.open(method, url, async)(async 为 true 表示异步); -
设置请求头(可选):
xhr.setRequestHeader('Content-Type', 'application/json'); -
监听状态变化:
xhr.onreadystatechange = () => {}; -
发送请求:
xhr.send(data)(POST 请求需传参)。
readyState 取值含义:
-
0:未初始化(open 未调用);
-
1:加载中(open 已调用,send 未调用);
-
2:已加载(send 已调用,响应头接收完成);
-
3:交互中(响应体部分接收);
-
4:完成(响应体完全接收)。
关键考点:网络请求底层原理,传统请求方案的理解。
2. HTTP 状态码 304 的含义是什么?与浏览器缓存的关系?
标准答案:
304 表示 "协商缓存命中",服务器告知浏览器:"当前资源未修改,可直接使用本地缓存"。
与缓存的关系:
-
浏览器缓存分为强缓存(Cache-Control/Expires)和协商缓存(ETag/Last-Modified);
-
强缓存失效后,浏览器会发送协商缓存请求(携带 If-None-Match/If-Modified-Since);
-
服务器验证资源未修改时,返回 304,浏览器直接使用缓存;资源修改则返回 200 和新资源。
关键考点:浏览器缓存机制,性能优化方案。
3. RESTful API 的核心规范是什么?常用 HTTP 方法的含义?
标准答案:
RESTful API 是基于 HTTP 协议的接口设计规范,核心是 "资源导向",使用 HTTP 方法表达操作意图:
-
GET:查询资源(幂等,可缓存);
-
POST:创建资源(非幂等,不可缓存);
-
PUT:更新资源(幂等,全量更新);
-
DELETE:删除资源(幂等);
-
OPTIONS:预检请求(跨域时触发,验证服务器是否允许跨域)。
关键考点:接口设计规范,HTTP 协议的实际应用。
4. 什么是跨域?OPTIONS 请求的作用是什么?
标准答案:
跨域是指浏览器因同源策略限制,阻止不同源的脚本访问当前文档的资源(如接口请求、DOM 操作)。
OPTIONS 请求的作用:
属于 "预检请求",当跨域请求满足以下条件时触发:
-
请求方法不是 GET/POST/HEAD;
-
请求头包含自定义字段(如 Authorization);
-
POST 请求的 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data、text/plain。
OPTIONS 请求的目的是让服务器告知浏览器:"是否允许该跨域请求的方法、请求头等"。
关键考点:跨域解决方案的前置知识,HTTP 协议细节。
四、性能优化高频题
1. 如何减少 DOM 重排和重绘?
标准答案:
重排(回流)是计算元素位置和大小,重绘是填充像素,两者都会影响性能,优化方案:
-
批量 DOM 操作:使用 DocumentFragment 一次性插入多个元素,避免频繁修改 DOM;
-
样式集中修改:通过修改 class 而非直接操作 style,减少样式变更次数;
-
脱离文档流操作:使用 position: absolute/fixed 或 display: none,操作后再恢复;
-
避免频繁读取尺寸:读取 offsetHeight、clientWidth 等属性会触发重排,应集中读取后批量操作;
-
使用虚拟列表:大数据渲染时,只渲染可视区域的元素(如 10000 条数据只渲染 10 条)。
关键考点:浏览器渲染机制,前端性能优化核心方案。
2. 防抖和节流的区别是什么?实现原理?适用场景?
标准答案:
两者都是限制函数执行频率的优化方案,核心差异在于执行时机:
-
防抖(debounce):触发后延迟 n 秒执行,若 n 秒内再次触发则重置延迟,适用于高频触发后只需要一次结果(如搜索框输入、窗口 resize);
-
节流(throttle):固定 n 秒内只执行一次,适用于高频触发但需要持续响应(如滚动加载、按钮点击防重复提交)。
实现代码:
// 防抖
function debounce(func, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
// 节流
function throttle(func, limit) {
let inThrottle;
return (...args) => {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
关键考点:性能优化方案,高频事件处理。
3. 浏览器的渲染流程是什么?如何优化渲染性能?
标准答案:
渲染流程:
-
解析 HTML,构建 DOM 树;
-
解析 CSS,构建 CSSOM 树;
-
合并 DOM 和 CSSOM,生成渲染树(只包含可见元素);
-
布局(重排):计算元素的位置和大小;
-
绘制(重绘):根据渲染树填充像素;
-
合成:将绘制的图层组合成最终页面。
渲染性能优化:
-
减少 CSS 阻塞:避免 @import,使用 link 引入 CSS,关键 CSS 内联;
-
减少 JS 阻塞:使用 defer/async 加载脚本,避免同步脚本阻塞渲染;
-
优化 DOM 结构:简化 DOM 层级,减少不必要的元素;
-
优化 CSS 选择器:避免复杂选择器(如后代选择器),减少 CSSOM 构建时间;
-
使用 CSS 硬件加速:通过 transform、opacity 等属性触发 GPU 渲染(避免重排重绘)。
关键考点:浏览器底层原理,前端性能优化的核心思路。
五、核心误区辨析题
1. HTTP 协议和 XMLHttpRequest/Fetch/axios 的关系?
标准答案:
-
HTTP 协议是客户端与服务器通信的 "规范 / 标准",定义了请求方法、状态码、报文格式等;
-
XMLHttpRequest、Fetch API 是浏览器提供的 "HTTP 协议实现工具"(原生 API);
-
axios 是基于 XMLHttpRequest 封装的 "第三方库",简化了请求配置、拦截器、错误处理等逻辑。
三者的关系:工具(XMLHttpRequest/Fetch/axios)实现了规范(HTTP 协议)。
2. history 模式和 hash 模式的 SPA 路由有什么区别?
标准答案:
两者都是 SPA 路由的实现方式,核心差异在于 URL 格式和底层原理:
-
hash 模式:URL 包含 #(锚点),如
http://test.com/#/page1,通过监听hashchange事件实现路由切换,无需后端配置(# 后的内容不会发送到服务器); -
history 模式:URL 无 #,如
http://test.com/page1,通过 history.pushState/replaceState 修改 URL,需要后端配置(所有路由都指向 index.html,否则刷新会 404)。
关键考点:SPA 路由的实现细节,前后端配合方案。