闭包
什么是闭包:1.函数发生嵌套;2.内部函数使用了外部函数的变量;(我的理解)
var a = 'global a'
function test(){
var a = 'test a'
return function f(){
console.log(a)
}()
}
test(); //a = 'test a'
分析上述代码,定义了一个全局变量a,在test函数内部定义了局部变量a,以及一个嵌套函数f;我们都知道函数是会创建函数作用域的,当我们访问一个变量时会由内向外进行搜索;也就是说会先搜索内部是否有a的定义及赋值,若有则停止寻找;若没有则逐层向上寻找,若找到则取该变量的值,若未找到则报错。
闭包的应用场景
场景一:通过循环给页面上多个dom节点绑定事件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<button>btn1</button>
<button>btn2</button>
<button>btn3</button>
<button>btn4</button>
<button>btn5</button>
</body>
</html>
<script>
var btns = document.getElementsByTagName('button');
for(var i = 0, len = btns.length; i < len; i++) {
btns[i].onclick = function() {
alert(i);
}
}
</script>
该段代码执行后
结果:无论点击哪个按钮均会alter 5;
原因:当点击事件触发时,js中的for循环已经执行完毕,此时的i的值是5
解决办法:
1.var换成let 原理:由于let是块级作用域,即声明的变量只在当前代码块有用;每次循环是其实都是重新声明了一个i;且每个i只在当轮循环中有用;因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i
时,就在上一轮循环的基础上进行计算。在上述代码中使用了var,相当于i是一个全局变量,故其实只有一个i,当每次循环的时候改变的都是同一个i的值;
<script>
var btns = document.getElementsByTagName('button');
for(let i = 0, len = btns.length; i < len; i++) {
btns[i].onclick = function() {
alert(i);
}
}
</script>
2.使用闭包解决
<script>
var btns = document.getElementsByTagName('button');
for(let i = 0, len = btns.length; i < len; i++) {
function(i){
btns[i].onclick = function() {
alert(i);
}
}(i)
}
</script>
在闭包的作用下,定义事件函数的时候,每次循环的i值都被封闭起来,这样在函数执行时,会查找定义时的作用域链,这个作用域链里的i值是在每次循环中都被保留的,因此点击不同的button会alert出来不同的i。
场景二:封装变量/延长局部变量的寿命
可以将不希望暴露在全局的变量封装成私有变量,封装一个计算阶乘的方法,为了提高性能,将每次的计算结果缓存起来,下次遇到同样的参数时直接取值不计算;这里我们缓存的变量不想暴露给外部,故使用闭包,使该变量为私有的变量。
var mult = (function(){
//这个是缓存的结果,不暴露给外部
var cache = {};
//相当于这里是在进行计算
var calculate = function() {
var a = 1;
for(var i = 0, len = arguments.length; i < len; i++) {
a = a * arguments[i];
}
return a;
}
return function() {
var args = Array.prototype.join.call(arguments, ',');
if(args in cache) {
// debugger
console.log(cache)
return cache[args];
}
cache[args] = calculate.apply(null, arguments);
console.log(cache)
return cache[args]
}
}())