闭包一般是ES5中用来解决函数外变量的问题的。
首先介绍一下变量作用域
// 1. 变量作用域:变量在什么范围内是可用的。
// name 在{}内外都可以使用,对于{}来说它并不是变量的一个限制
{
var name = 'why';
console.log(name);
}
console.log(name);
上面的代码看起来没有什么问题,甚至有人还觉得代码块外还能调用变量是一个很方便的事情。
那么没有块级作用域
会引起什么问题呢?
// 2.没有块级作用域引起的问题: if的块级
// func()目的就是为了打印'why',但是func()在外部调用的时候,有可能把name进行修改,就导致func()的输出和预期的不一样
var func;
if(true){
var name = 'why';
func = function(){
console.log(name);
}
}
name = 'kobe'
func()
再来看看for的块级问题.
需求是点击哪个按钮就输出哪个按钮的下标。但是运行起来后无论点击哪个 输出的i都是5
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
</body>
<script>
// 2.没有块级作用域引起的问题: for的块级
var btns =document.getElementsByTagName("button");
for(var i=0; i<btns.length;i++){
btns[i].addEventListener('click',function(){ // 方法中的i 引用的一直是外面的变量i 而外部变量i已经被修改成了5
console.log('第'+ i +'个按钮被点击 ') // 第5个按钮被点击
})
}
</script>
</html>
那么如何解决呢?就是使用闭包。
// 为什么闭包可以解决问题:因为函数是一个作用域
var btns = document.getElementsByTagName("button");
for(var i=0; i<btns.length;i++){
// 这种写法就相当于定义一个匿名函数 然后直接调用
(function (num){
btns[i].addEventListener('click',function(){
console.log('第'+ num +'个按钮被点击 ')
})
})(i)
}
// 写成这种方式可能更容易理解一点
var btns =document.getElementsByTagName("button");
for(var i=0; i<btns.length;i++){
function func(num){
btns[i].addEventListener('click',function(){
console.log('第'+ num +'个按钮被点击 ')
})
}
func(i)
}
什么是闭包?
简单来说就是定义一个匿名函数然后立即执行,将需要块级作用域的变量通过参数传入。
再来看一个简单的代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id='btn'>输出why</button>
</body>
<script>
var btn =document.getElementsById("btn");
var name = 'why'
btn.addEventListener('click',function(){
function func(name){ //定义函数
console.log(name) //这个name就不是函数外的name,而是参数name
}
func(name) //立即调用
})
name = 'kobe'; // 即便这里将name修改了,也不会影响func(name)的输出
</script>
</html>
写成闭包就是如下
var name = 'why'
(function(name){
console.log(name)
})(name)
但是写成闭包整个代码结构就很难阅读了
在ES6中加入了let
就很方便了,因为使用了let
, for{}中就有了块级作用域
const btns = document.getElementsByTagName("button");
for(let i=0; i < btns.length; i++){
btns[i].addEventListener('click',function(){
console.log('第'+ i +'个按钮被点击 ');
})
}
总结
ES5中只有function是作用域
ES5之前因为if和for都没有块级作用域的概念,所以在很多时候,我们都必须借助于function的作用域来解决应用外面变量的问题
ES6中加入了let,let它是有if和for的块级作用域
ES5中的var是没有块级作用域(if/for)
ES6中的let是有块级作用域的(if/for)