闭包治愈“全局变量恐惧症”,利用闭包实现JavaScript私有变量(一)https://developer.aliyun.com/article/1426535
2. 事件绑定
在JavaScript中,事件处理程序通常需要访问元素的一些属性或者其他变量。由于事件处理程序通常作为全局函数定义,因此它无法访问事件处理程序函数之外的变量。但是通过使用闭包,可以在事件处理程序内部访问外部的变量。
function addClickHandler(el, fn) { el.addEventListener('click', function() { fn(); }); } const button = document.querySelector('#myButton'); const name = '张三'; addClickHandler(button, function() { alert(`你好, ${name}!`); });
在这个例子中, addClickHandler
函数通过闭包,允许事件处理程序内部访问外部变量 name
,而且这种方式不会导致全局变量的污染。
3. 循环变量绑定
在循环语句中,经常需要将循环变量绑定到按钮或者链接等元素中。使用循环变量绑定时,需要注意循环变量和事件处理程序之间的作用域问题。使用闭包,可以解决循环变量和事件处理程序之间的作用域问题。
function createHandler(i) { return function() { console.log(`按钮 ${i} 被点击了`); } } for(let i = 0; i < 5; i++) { const button = document.createElement('button'); button.innerText = `按钮 ${i}`; button.addEventListener('click', createHandler(i)); document.body.appendChild(button); }
在这个例子中, createHandler
函数使用闭包来创建一个可以访问循环变量的事件处理程序。而不使用闭包的话,事件处理程序将只能访问循环结束时循环变量的值。
IV. 闭包的优缺点
闭包的优点
数据的封装
闭包在数据封装上有以下优点:
1. 私有化变量和方法
闭包可以将变量和方法封装在一个函数作用域内,使得这些变量和方法只能在函数内部被访问,从而实现数据的封装。外部作用域无法直接访问闭包内部的变量和方法,只能通过相关的公有方法来访问或者修改数据。
function Counter() { let count = 0; return { increment: function() { count++; }, decrement: function() { count--; }, getCount: function() { return count; } }; } const counter = Counter(); counter.increment(); // count+1 console.log(counter.getCount()); // 输出 1
在这个例子中, count
变量和三个方法都被封装在 Counter
函数内部,只能通过 Counter
函数返回的方法来访问或者修改 count
变量的值。
2. 避免全局变量的污染
使用闭包可以避免全局变量的污染。在函数作用域内部定义变量和方法时,它们不会影响全局作用域中已经存在的同名变量和方法,从而避免了命名冲突等问题。
let count = 0; function Counter() { count = 10; return { increment: function() { count++; }, decrement: function() { count--; }, getCount: function() { return count; } }; } const counter = Counter(); counter.increment(); // count+1 console.log(counter.getCount()); // 输出 11 console.log(count); // 输出 10
在这个例子中, count
变量已经被定义在全局作用域中,但是通过在 Counter
函数内部重新定义一个同名的 count
变量,实现了局部变量的封装,避免了对全局 count
变量的修改。
因此,使用闭包可以实现对数据的封装,并且避免全局变量的污染,有助于提高代码的可读性和可维护性。
可以实现高阶函数
闭包可以实现高阶函数,并且在高阶函数中发挥重要作用,具有以下优点:
1. 强大的表现力
闭包可以将函数作为参数和返回值传递,实现更加灵活的函数组合和转换,从而快速构建复杂的功能模块。这种能力称为函数式编程中的柯里化(currying)和组合(composition)。
function add(x) { return function(y) { return x + y; }; } function multiply(x) { return function(y) { return x * y; }; } const add5 = add(5); const multiply2 = multiply(2); const result = add5(2); // 5 + 2 = 7 const result2 = multiply2(result); // 7 * 2 = 14 console.log(result2); // 输出 14
在这个例子中,add
和 multiply
函数都是返回一个闭包,而且这两个闭包接受一个参数并进行计算。通过组合这两个闭包,可以实现一个更加复杂的计算过程,从而实现高阶函数的功能。
2. 支持延迟求值
闭包支持延迟求值,也就是说,在某些情况下,可以将函数的执行延迟到需要的时候再执行,从而避免不必要的计算和内存占用。这种方法称为惰性求值(lazy evaluation)。
function createSquares(max) { const squares = []; function getSquare(i) { return function() { return i * i; }; } for(let i = 0; i <= max; i++) { squares.push(getSquare(i)); } return squares; } const squares = createSquares(10000); console.log(squares[0]()); // 输出 0 console.log(squares[1]()); // 输出 1 console.log(squares[2]()); // 输出 4 // 将一次性计算所有的平方数改成延迟计算 const squares2 = createSquares(10000); console.log(squares2[0]()); // 输出 0 console.log(squares2[1]()); // 输出 1
在这个示例中,createSquares
函数返回一个闭包数组,这些闭包可以在需要时延迟计算实际的值,解决了在一次性计算所有平方数时可能面临的性能问题,从而提高了运行效率。对于大数据量的计算,使用惰性求值可以节省大量的内存和计算资源。
因此,利用闭包可以创建高阶函数,实现更加灵活的函数组合和转换,还可以支持惰性求值,提高代码性能和运行效率。
闭包治愈“全局变量恐惧症”,利用闭包实现JavaScript私有变量(三)https://developer.aliyun.com/article/1426537