闭包是 JavaScript 中一个非常强大的特性,它允许函数访问其外部作用域中的变量,即使在该函数被调用时,外部作用域已经执行完毕。闭包可以帮助我们实现数据的私有化、封装和模块化,使代码更简洁、易读和可维护。
简单来说,闭包是指有权访问另一个函数作用域中变量的函数。
function outerFunction() { let outerVariable = "I am outside!"; function innerFunction() { console.log(outerVariable); // innerFunction 可以访问 outerVariable } return innerFunction; } const closure = outerFunction(); closure(); // 输出: I am outside!
在上述例子中,innerFunction
就是一个闭包,它可以访问 outerFunction
中的 outerVariable
,即使 outerFunction
已经执行完毕。
function createCounter() { let count = 0; return function() { count++; return count; }; } const counter = createCounter(); console.log(counter()); // 输出: 1 console.log(counter()); // 输出: 2 console.log(counter()); // 输出: 3
在这个例子中,count
变量被封装在 createCounter
函数的作用域内,只能通过返回的闭包函数进行访问和修改。
在 ES6 之前,JavaScript 没有块级作用域,我们可以使用闭包来模拟块级作用域。
for (var i = 1; i <= 3; i++) { (function(i) { setTimeout(function() { console.log(i); }, i * 1000); })(i); } // 输出: 1, 2, 3 (每隔一秒输出一个数字)
通过立即执行函数表达式 (IIFE),为每次循环创建了一个新的作用域,从而使 setTimeout
中的 i
保持正确的值。
事件委托是一种利用事件冒泡机制,将事件监听器添加到父元素上,从而管理多个子元素事件处理的一种技术。它可以减少内存占用,提高性能,特别是在需要处理大量动态生成的子元素事件时非常有用。
通过将事件监听器添加到父元素上,当子元素的事件被触发时,事件会冒泡到父元素,由父元素的事件监听器进行处理。
<ul id="parent"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul>
document.getElementById('parent').addEventListener('click', function(event) { if (event.target.tagName === 'LI') { console.log(event.target.innerText); // 输出点击的列表项的文本 } });
在这个例子中,我们只为 ul
元素添加了一个点击事件监听器,但可以处理所有 li
元素的点击事件。
当页面上有大量动态生成的元素时,使用事件委托可以简化事件处理。
const list = document.getElementById('parent'); document.getElementById('addItem').addEventListener('click', function() { const newItem = document.createElement('li'); newItem.innerText = `Item ${list.children.length + 1}`; list.appendChild(newItem); });
当需要为大量元素添加事件监听器时,事件委托可以显著提高性能,因为只需要为父元素添加一个事件监听器,而不是为每个子元素添加。
<button id="addItem">Add Item</button> <ul id="parent"> <!-- 动态生成的列表项 --> </ul>
闭包能够访问外部函数作用域中的变量,从而实现数据私有化和封装;
事件委托利用事件冒泡机制,通过将事件监听器添加到父元素上,简化了事件处理,提高了性能。