面试题答案
一键面试1. JavaScript事件循环机制与DOM操作的相互影响
JavaScript是单线程语言,事件循环机制负责协调任务执行。DOM操作与JavaScript在同一线程,当执行JavaScript代码时会阻塞DOM渲染。事件循环分为宏任务队列和微任务队列,宏任务(如setTimeout
、setInterval
、DOM事件)在宏任务队列,微任务(如Promise.then
、MutationObserver
)在微任务队列。当执行栈为空时,事件循环会先从微任务队列中取任务执行,直到微任务队列为空,再从宏任务队列中取任务执行。每次从宏任务队列取任务执行前,浏览器会进行渲染。
2. 场景问题分析
渲染卡顿
在循环中频繁创建DOM元素并绑定事件,会导致大量计算和渲染工作。由于JavaScript执行阻塞DOM渲染,循环执行时间过长会使渲染长时间停滞,造成卡顿。
事件触发异常
异步任务(如setTimeout
、Promise
)穿插其中,可能导致事件绑定顺序与预期不符。例如,Promise.then
微任务可能在DOM元素创建并绑定事件之前执行,使得事件处理函数在元素尚未完全准备好时被调用,引发异常。
3. 解决方案
分批创建DOM元素
通过requestAnimationFrame
将创建DOM元素的任务分批处理,每次处理少量元素,让浏览器有机会进行渲染。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="container"></div>
<script>
const elementsToCreate = 1000;
const batchSize = 100;
let currentIndex = 0;
const container = document.getElementById('container');
function createBatch() {
for (let i = 0; i < batchSize && currentIndex < elementsToCreate; i++) {
const div = document.createElement('div');
div.textContent = `Element ${currentIndex + 1}`;
div.addEventListener('click', function () {
console.log(`Clicked on ${this.textContent}`);
});
container.appendChild(div);
currentIndex++;
}
if (currentIndex < elementsToCreate) {
requestAnimationFrame(createBatch);
}
}
requestAnimationFrame(createBatch);
</script>
</body>
</html>
原理
requestAnimationFrame
会在浏览器下一次重绘之前调用回调函数,将任务拆分成多个小任务,分散在不同的渲染周期执行,避免长时间阻塞渲染。
正确处理异步任务
确保异步任务的执行顺序与DOM操作协调。例如,使用async/await
来控制Promise
的执行顺序,保证DOM元素创建和事件绑定完成后再执行相关异步操作。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="asyncContainer"></div>
<script>
async function createElementWithEvent() {
const div = document.createElement('div');
div.textContent = 'Async Element';
div.addEventListener('click', function () {
console.log('Clicked on async element');
});
document.getElementById('asyncContainer').appendChild(div);
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Async operation after element creation');
}
createElementWithEvent();
</script>
</body>
</html>
原理
async/await
使异步操作以同步方式书写,await
会暂停函数执行,直到Promise
被解决(resolved),确保后续异步操作在DOM相关操作完成后执行。这样可以避免事件触发异常,保证代码逻辑的正确性。