闭包:如大家所知,通俗讲,可以访问其他函数内部变量的函数
// 创建闭包最常见的方式函数作为返回值 function fn() { let name = "小帆"; return function() { console.log(name); }; } let log = fn(); log(); //打印“小帆” --外部函数访问内部变量
下面来实现一个简单的demo:计数器
var number = 0; function creat() { number = number + 1; console.log(number ); } creat(); //确实实现了需求
//但是如果需要第二个计数器呢? //确定要像下面这样写吗? var num = 0; function creat_1() { num = num + 1; console.log(num ); } creat_1();
如果我们需要的更多计数器,上面的写法就感觉代码很冗余了,这个时候可以考虑一下闭包:
function createNum() { var number = 0; return function() { number = number + 1; console.log(number ); }; } var fun1 = createNum(); fun1(); //1 fun1(); //2 var fun2 = createNum(); fun2(); //1 fun2(); //2
有一种经典题目:for循环里的定时器引发的思考
下面这道题的结果是多少?
for (var i = 0; i < 4; i++) { setTimeout(function() { console.log(i); }, 300); }
依次打印0,1,2,3 ???,然而打印的全都是4
原因:JS 执行的时候首先会先执行主线程,异步相关的会存到异步队列里,当主线程执行完毕开始执行异步队列, 主线程执行完毕后,此时 i 的值为 4,说以在执行异步队列的时候,打印出来的都是 4(这里需要大家对 event loop 有所了解(js 的事件循环机制))
要使其依次打印0,1,2,3,则可以考虑闭包:
//方法一: //这个是通过自执行函数返回一个函数,然后在调用返回的函数去获取自执行函数内部的变量,此为闭包 for (var i = 0; i < 4; i++) { setTimeout( (function(i) { return function() { console.log(i); }; })(i), 300 ); } //方法二: // 大部分都认为方法一和方法二都是闭包,我认为方法一是闭包,而方法二是通过创建一个自执行函数,使变量存在这个自执行函数的作用域里 for (var i = 0; i < 4; i++) { (function(i) { setTimeout(function() { console.log(i); }, 300); })(i); }
再举一个例子:获取多个元素并添加点击事件
var op = document.querySelectorAll("p"); for (var j = 0; j < op.length; j++) { op[j].onclick = function() { console.log(j); }; } //log出来的值是一样的 // 解决办法一: for (var j = 0; j < op.length; j++) { (function(j) { op[j].onclick = function() { alert(j); }; })(j); } // 解决办法二: for (var j = 0; j < op.length; j++) { op[j].onclick = (function(j) { return function() { alert(j); }; })(j); }
既然这样,那每次遇到这种问题的时候我是不是可以都这样使用闭包?
最后说一下,闭包有它的好处,也有它的坏处