对于这个小demo,其目的就是为了搞清楚,var和let的区别,前者是函数级作用域,后者是块级作用域;
关于更详细的解释可参考:let、const和var的区别
举个很小的demo,
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
对于这个案例,我们会发现不管最后输出的是a[6],还是a[5],其实结果都是10
原因如下:
变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10
此时我们如果将var换成let,就会出现不一样的结果,那就是a[6]就是6,a[5]就是5
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
原因如下:
变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域
那么接下来切入正题,来看一个tab栏切换的小demo,
* {
margin: 0;
padding: 0;
}
body {
background-color: gray;
}
.container {
width: 300px;
height: 300px;
background-color: white;
margin: 100px auto;
}
.up {
height: 50px;
width: 100%;
}
.up ul {
list-style: none;
text-align: center;
}
.up ul li {
width: 60px;
height: 50px;
float: left;
line-height: 50px;
}
.up ul li a {
text-decoration: none;
font-size: 20px;
}
.down {
height: 250px;
width: 100%;
position: relative;
text-align: center;
}
.down div {
position: absolute;
line-height: 100px;
height: 100%;
width: 100%;
display: none;
}
.select {
border-bottom: 2px solid orange;
}
.down .div_select {
display: block;
}
<div class="container">
<div class="up">
<ul>
<li class="select"><a href="#">百度</a></li>
<li><a href="#">新浪</a></li>
<li><a href="#">淘宝</a></li>
<li><a href="#">京东</a></li>
<li><a href="#">网易</a></li>
</ul>
</div>
<div class="down">
<div class="div_select">百度百度百度</div>
<div>新浪新浪新浪</div>
<div>淘宝淘宝淘宝</div>
<div>京东京东京东</div>
<div>网易网易网易</div>
</div>
</div>
<script>
//获取元素
let lis = document.querySelector(".up").querySelectorAll("li");
let divs = document.querySelector(".down").querySelectorAll("div");
//利用闭包
//i 给每个li添加滑动事件
// for (var i = 0; i < lis.length; i++) {
// (function(i) {
// lis[i].onmouseover = function(event) {
// //清除效果
// for (var j = 0; j < lis.length; j++) {
// // console.log(i);
// //先移除所有class
// lis[j].removeAttribute("class");
// divs[j].removeAttribute("class");
// //判断当前所选的元素是否与之对应
// if (lis[j] === lis[i]) {
// lis[j].setAttribute("class", "select");
// divs[i].setAttribute("class", "div_select");
// }
// }
// }
// })(i);
// }
let timer = null;
// i 给每个li添加滑动事件
for (let i = 0; i < lis.length; i++) {
lis[i].onmouseover = event => {
timer = setTimeout(function() {
//清除效果
for (let j = 0; j < lis.length; j++) {
// console.log(i);
//先移除所有class
lis[j].removeAttribute("class");
divs[j].removeAttribute("class");
//判断当前所选的元素是否与之对应
if (lis[j] == lis[i]) {
lis[j].setAttribute("class", "select");
divs[i].setAttribute("class", "div_select");
}
}
}.bind(this), 500);
}
lis[i.onmouseout = () => {
clearTimeout(timer);
}]
}
</script>