平时开发中很少注意到var和let的区别,只知道var是ES5定义变量的标准,let是ES6的标准。对于后端程序员来说很少会注意到两者的区别,大概了解两者均是定义变量的一种关键字,下面以一个有趣的代码执行现象来看两者的区别
需求
为页面中五个按钮绑定点击事件,点击其中任意一个按钮页面弹出点击按钮值
我们先简单实现这个需求,实现的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>the difference between var and let</title>
</head>
<script type="application/javascript">
// 为每一个button绑定一个点击函数,然后弹出按钮的值
window.onload = function () {
var btns = document.getElementsByTagName("button")
for (var i = 0; i <= btns.length - 1; i++) {
var btn = btns[i];
btn.addEventListener('click', function () {
alert(btn.innerText)
})
}
}
</script>
<body>
<div id="box">
<button>button1</button>
<button>button2</button>
<button>button3</button>
<button>button4</button>
<button>button5</button>
</div>
</body>
</html>
执行页面效果如下所示:
看到这里是不是很奇怪,无论哪个按钮点击弹出的都是button5而不是我们期望的值,但是如果将上面for循环代码定义变量i与btn的关键字由var
改为let
重新运行效果如下所示:
看到这里两种运行效果,有同学可能会问为什么将var变为let 就实现了我们想要的运行效果了呢?解释这个问题我们需要先了解一个概念:块级作用域
块级作用域
先看一下以下代码:
{
var x =123
}
// 控制台可打印123
console.log(x)
向上述被{}
包括的代码我们称之为代码块,有后端开发经验的同学对此肯定不陌生,上面的代码控制台可以输出x的值为123。但是如果我们将var改为let控制台将会报下面的错误:
由此我们知道let定义的变量只对当前代码块内有效,而ES5只有函数作用域和全局作用域,没有块级作用域,所以{}限定不了var声明变量的访问范围。
正是由于ES5 中没有块级作用域,所以很好理解之前需求代码中for循环定义的变量i泄露为全局变量,所以无论怎么点击都变成了button5。如果将变量i用let声明,当前的i就只会在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以点击哪个按钮就会弹出当前按钮的值。我们在ES5中还可以使用闭包函数解决这个问题:
window.onload = function () {
var btns = document.getElementsByTagName("button")
for (var i = 0; i <= btns.length - 1; i++) {
(function () {
var btn = btns[i];
btn.addEventListener('click', function () {
alert( btn.innerText)
})
})(i)
}
}
虽然ES5中没有没有块级作用域,只包含函数作用域和全局作用域。函数作用域定义的变量只对当前函数内有效。所以就不存在有变量泄露的问题。