前端面试题(六)
28. JavaScript 事件循环机制
-
什么是事件循环(Event Loop)?
- 事件循环 是 JavaScript 中的执行机制,用来处理异步操作。JavaScript 是单线程的,它通过事件循环来管理执行栈中的同步任务和任务队列中的异步任务。
-
事件循环的工作流程是什么?
- 执行栈(Call Stack):同步代码会依次压入执行栈,并按顺序执行。执行完一个任务后从栈中弹出,继续下一个任务。
- 任务队列(Task Queue):异步操作(如
setTimeout
、Promise
)会将回调放入任务队列中等待执行。 - 事件循环:事件循环不断检查执行栈是否为空,若为空,会从任务队列中取出下一个任务,压入执行栈中执行。
-
微任务和宏任务的区别?
- 宏任务(Macro Task):包括
setTimeout
、setInterval
、DOM 事件、HTTP 请求等,这些任务在每次事件循环结束后会被放入任务队列中等待执行。 - 微任务(Micro Task):包括
Promise.then
、MutationObserver
等,微任务的执行优先级高于宏任务,它们会在当前任务执行完之后立即执行,而不是等待下一个事件循环。
- 宏任务(Macro Task):包括
-
代码执行顺序示例:
console.log('1'); // 同步任务,立即执行 setTimeout(() => console.log('2'), 0); // 宏任务,下一次事件循环执行 Promise.resolve().then(() => console.log('3')); // 微任务,当前任务完成后执行 console.log('4'); // 同步任务,立即执行 // 输出顺序为:1, 4, 3, 2
29. JavaScript 异步编程
-
JavaScript 中有哪些处理异步编程的方法?
-
回调函数(Callback):通过将一个函数作为参数传递给另一个函数,在异步操作完成后调用该回调函数。
setTimeout(() => { console.log('异步操作'); }, 1000);
-
Promise:
Promise
是用于处理异步操作的对象。它有三种状态:pending
(进行中)、fulfilled
(已成功)、rejected
(已失败)。通过.then()
和.catch()
来处理成功和失败的结果。const promise = new Promise((resolve, reject) => { setTimeout(() => resolve('成功'), 1000); }); promise.then(result => console.log(result)); // 输出 "成功"
-
async/await:基于
Promise
的语法糖,使得异步代码看起来像同步代码。await
关键字用于等待一个Promise
完成,async
函数会返回一个Promise
。async function fetchData() { const response = await fetch('https://api.example.com/data'); const data = await response.json(); console.log(data); } fetchData();
-
-
如何处理多个异步请求?
-
Promise.all()
:用于并行执行多个异步操作,并在所有操作完成后返回结果。const promise1 = fetch('https://api.example.com/data1'); const promise2 = fetch('https://api.example.com/data2'); Promise.all([promise1, promise2]) .then(responses => Promise.all(responses.map(r => r.json()))) .then(data => console.log(data));
-
Promise.race()
:只要其中一个Promise
完成,Promise.race()
就会返回那个Promise
的结果或错误。const promise1 = new Promise(resolve => setTimeout(resolve, 100, 'One')); const promise2 = new Promise(resolve => setTimeout(resolve, 200, 'Two')); Promise.race([promise1, promise2]).then(result => console.log(result)); // 输出 "One"
-
30. 前端性能优化进阶
-
如何优化 JavaScript 的性能?
- 减少 DOM 操作:DOM 操作是性能开销较大的操作,应该尽量减少频繁的 DOM 读写。例如,可以将多次 DOM 操作合并,或使用文档片段(
DocumentFragment
)。 - 事件委托:利用事件冒泡机制,将多个子元素的事件绑定到父元素上,减少事件监听器的数量,提升性能。
document.getElementById('parent').addEventListener('click', (event) => { if (event.target.matches('.child')) { console.log('子元素被点击'); } });
- 避免深层次的嵌套:深层次的循环或递归会导致性能问题,尤其是在处理大量数据时。
- 延迟加载(Lazy Loading):对于不在首屏显示的内容(如图片、脚本),可以使用延迟加载来减少首屏加载时间。
<img src="image.jpg" loading="lazy" alt="Lazy loaded image">
- 减少 DOM 操作:DOM 操作是性能开销较大的操作,应该尽量减少频繁的 DOM 读写。例如,可以将多次 DOM 操作合并,或使用文档片段(
-
如何优化 CSS 的性能?
- 减少重排和重绘:避免频繁修改影响布局的属性(如
width
、height
、padding
),因为这些操作会触发浏览器的重排(reflow)和重绘(repaint)。 - CSS 选择器优化:尽量使用简单、高效的 CSS 选择器,避免使用过于复杂的选择器(如后代选择器
div div div
),它们会增加样式计算的复杂度。 - 合并 CSS 文件:减少 HTTP 请求,使用工具如 Webpack 将多个 CSS 文件合并成一个。
- 减少重排和重绘:避免频繁修改影响布局的属性(如
31. Web 安全
-
什么是 XSS(跨站脚本攻击)?如何防范?
- XSS(Cross-Site Scripting) 是指攻击者通过在网页中注入恶意脚本,使得其他用户执行这些恶意代码,通常用于窃取用户的敏感信息。
- 防范措施:
- 输出内容时进行转义:对用户输入的数据进行 HTML 转义,防止恶意脚本被执行。例如,将
<
和>
转换为<
和>
。 - 使用 CSP(内容安全策略):通过设置 HTTP 响应头
Content-Security-Policy
来限制页面可以加载的外部资源,防止恶意脚本的执行。Content-Security-Policy: default-src 'self'; script-src 'self';
- 输出内容时进行转义:对用户输入的数据进行 HTML 转义,防止恶意脚本被执行。例如,将
-
什么是 CSRF(跨站请求伪造)?如何防范?
- CSRF(Cross-Site Request Forgery) 是一种通过伪造用户请求,使用户在不知情的情况下执行某些操作的攻击方式。攻击者通常会通过用户已经登录的身份,伪造请求来执行敏感操作(如转账、修改密码)。
- 防范措施:
- 使用 CSRF Token:在每次请求时生成唯一的 CSRF Token,并将其附加到表单或 AJAX 请求中。服务器在处理请求时会验证该 Token。
<input type="hidden" name="csrf_token" value="token_value">
- 验证 Referer 或 Origin:在服务器端验证请求的
Referer
或Origin
字段,确保请求来源合法。 - 使用 SameSite Cookie 属性:设置 Cookie 的
SameSite
属性为Strict
或Lax
,防止跨站请求携带 Cookie。Set-Cookie: sessionId=abc123; SameSite=Strict;
- 使用 CSRF Token:在每次请求时生成唯一的 CSRF Token,并将其附加到表单或 AJAX 请求中。服务器在处理请求时会验证该 Token。
32. 前端模块化与打包
- 前端模块化的演变:
-
IIFE(立即调用函数表达式):早期的模块化方式,通过闭包的方式将代码封装在一个函数中,避免变量污染全局作用域。
(function() { const privateVariable = 'IIFE 模块化'; console.log(privateVariable); })();
-
CommonJS:Node.js 环境中的模块化规范,使用
require
导入模块,module.exports
导出模块。
-