创建10个标签,点击时弹出对应的序号
理解 JavaScript 闭包和作用域的经典面试题,掌握循环中事件绑定的正确方式
问题
这是一道经典的 JavaScript 闭包面试题。需要创建 10 个标签(如按钮或 div),当点击每个标签时,弹出对应的序号(0-9)。
这道题的考点是:
- JavaScript 的作用域和闭包机制
- 循环中绑定事件的常见陷阱
- 如何正确保存循环变量的值
解答
方法一:使用闭包(IIFE)
// 创建容器
const container = document.getElementById('container');
// 使用 var 声明 + 闭包解决
for (var i = 0; i < 10; i++) {
const btn = document.createElement('button');
btn.innerText = `按钮${i}`;
// 使用立即执行函数创建闭包,保存当前的 i 值
(function(index) {
btn.onclick = function() {
alert(`当前点击的是第 ${index} 个按钮`);
};
})(i);
container.appendChild(btn);
}
方法二:使用 let 块级作用域
const container = document.getElementById('container');
// 使用 let 声明,每次循环创建新的块级作用域
for (let i = 0; i < 10; i++) {
const btn = document.createElement('button');
btn.innerText = `按钮${i}`;
btn.onclick = function() {
alert(`当前点击的是第 ${i} 个按钮`);
};
container.appendChild(btn);
}
方法三:使用自定义属性
const container = document.getElementById('container');
for (var i = 0; i < 10; i++) {
const btn = document.createElement('button');
btn.innerText = `按钮${i}`;
// 将索引保存在元素的自定义属性中
btn.setAttribute('data-index', i);
btn.onclick = function() {
const index = this.getAttribute('data-index');
alert(`当前点击的是第 ${index} 个按钮`);
};
container.appendChild(btn);
}
方法四:使用事件委托
const container = document.getElementById('container');
// 创建所有按钮
for (let i = 0; i < 10; i++) {
const btn = document.createElement('button');
btn.innerText = `按钮${i}`;
btn.setAttribute('data-index', i);
container.appendChild(btn);
}
// 在父元素上绑定事件(事件委托)
container.onclick = function(e) {
if (e.target.tagName === 'BUTTON') {
const index = e.target.getAttribute('data-index');
alert(`当前点击的是第 ${index} 个按钮`);
}
};
使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>点击标签弹出序号</title>
<style>
#container {
padding: 20px;
}
button {
margin: 5px;
padding: 10px 20px;
cursor: pointer;
font-size: 14px;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
const container = document.getElementById('container');
// 推荐方案:使用 let
for (let i = 0; i < 10; i++) {
const btn = document.createElement('button');
btn.innerText = `按钮${i}`;
btn.onclick = function() {
alert(`当前点击的是第 ${i} 个按钮`);
};
container.appendChild(btn);
}
</script>
</body>
</html>
关键点
-
闭包原理:使用 IIFE(立即执行函数)创建独立的作用域,每次循环都会保存当前的
i值,避免所有事件处理函数共享同一个变量 -
var vs let:
var是函数作用域,循环结束后i的值为 10;let是块级作用域,每次循环都会创建新的变量绑定 -
错误示范:直接使用
var而不使用闭包会导致所有按钮点击时都显示 10for (var i = 0; i < 10; i++) { btn.onclick = function() { alert(i); // 所有按钮都会弹出 10 }; } -
最佳实践:在现代开发中,推荐使用
let或const,代码更简洁易懂 -
事件委托优势:当需要创建大量元素时,使用事件委托可以减少内存占用,只需绑定一个事件监听器
-
性能考虑:事件委托方式在创建大量元素时性能更好,因为只有一个事件监听器
目录