首先来看一个比较简单的问题,咱们想实现的就是每隔1s输出0-4的值,就是这么简单,看下错误写法:jquery
1
2
3
4
5
6
7
8
|
function test() {
for
(
var
i = 0; i < 5; ++i) {
setTimeout(function() {
console.log(
"index is :"
, i);
}, 1000);
}
}
test();
|
以上代码会如何输出?输出以下:数组
1
2
3
4
5
|
index
is
: 5
index
is
: 5
index
is
: 5
index
is
: 5
index
is
: 5
|
并且该操做几乎是在同一时间完成,setTimeout定时根本就没有起做用,这是由于:单线程的js在操做时,对于这种异步操做,会先进行一次“保存”,等到整个for循环执行结束后,此时i的值已经变成5,由于setTimeout是写在for循环中的,至关于存在5次定时调用,这5次调用均是在for循环结束后进行的,因此天然而然输出都是5,正确的实现有几种,通常状况下,咱们使用递归实现,以下:闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// var i = 0;
// var arr = [0, 1, 2, 3, 4];
// function box6() {
// if (i < arr.length) {
// setTimeout(function() {
// console.log("index is : ", i);
// i++;
// box6();
// }, 1000);
// }
// }
box6();
function box7(param) {
if
(param < 5) {
console.log(
"index is :"
, param);
setTimeout(function() {
box7(param + 1);
}, 1000)
}
}
box7(0);
|
正确实现每隔1s打印输出以下:异步
1
2
3
4
5
|
index
is
: 0
index
is
: 1
index
is
: 2
index
is
: 3
index
is
: 4
|
使用递归实现的倒计时:async
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function showTime(count) {
console.log(
"count is : "
, count);
if
(count == 0) {
console.log(
"All is Done!"
);
}
else
{
count -= 1;
setTimeout(function() {
showTime(count);
}, 1000);
}
}
showTime(20);
|
递归调用很好的解决了setTimeout同时执行的状况,若是使用async、await实现,能够以下写法:函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var
asyncFunc = function(arr, i) {
return
new
Promise(function(resolve, reject) {
setTimeout(function() {
arr.push(i);
console.log(
"index is : "
, i);
resolve();
}, 1000);
});
}
var
box5 = async function() {
var
arr = [];
for
(
var
i = 0; i < 5; i++) {
await asyncFunc(arr, i);
}
console.log(arr);
}
box5();
|
一样实现每隔1s正确地打印输出以下:post
1
2
3
4
5
6
|
index
is
: 0
index
is
: 1
index
is
: 2
index
is
: 3
index
is
: 4
[ 0, 1, 2, 3, 4 ]
|
接下来再看个需求:构建一个函数数组,该数组每一项函数的功能是依次输出0-4,错误写法以下:测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function buildList(list) {
var
result = [];
for
(
var
i = 0; i < list.length; i++) {
var
item =
'item'
+ list[i];
result.push(function() { console.log(item +
' '
+ list[i]) });
}
return
result;
}
function testList() {
var
fnlist = buildList([1, 2, 3]);
for
(
var
j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
testList();
|
输出以下:ui
1
2
3
|
item3 undefined
item3 undefined
item3 undefined
|
for循环里面使用匿名函数和直接写setTimeout调用比较相似,可是这里又有点不一样,for循环执行结束后,匿名函数开始调用,发现里面存在“item”变量,这时依次会向上级查找,刚好找到循环结束时的item变量值为“list[2]”即为3,item为3可是i的值已经变为3,又由于list[3]的值为undefined,因此这里输出3遍item3 undefined。不信能够修改下数组以下,其他代码不变:
1
2
3
4
5
6
7
8
9
10
|
function buildList(list) {
...
}
function testList() {
var
fnlist = buildList([6, 7, 8]);
...
}
testList();
|
这里绝对输出的是:
1
2
3
|
item8 undefined
item8 undefined
item8 undefined
|
再来看下正确的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
function buildList(list) {
var
result = [];
for
(
var
i = 0; i < list.length; i++) {
var
item =
'item'
+ list[i];
result.push(function(index, it) {
return
function() {
console.log(it +
' '
+ list[index]);
}
}(i, item));
}
return
result;
}
function testList() {
var
fnlist = buildList([6, 7, 8]);
for
(
var
j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
testList();
|
输出以下:
1
2
3
|
item6 6
item7 7
item8 8
|
这里主要使用的是即时执行函数,什么是即时执行函数?能够理解为一个封闭的代码块,该代码块中的代码会在定义时当即执行一遍,各个代码块的做用域彼此独立,不会污染外部环境,写法其实有不少种,上面只是一种,一样的还有使用void、+、-、!等等,jquery源码就是直接使用的这里的圆括号写法的这种。
再看几个测试例子:
1
2
3
4
5
6
7
8
9
10
|
function box2() {
var
arr = [];
for
(
var
i = 0; i < 5; i++) {
arr[i] = (function(num) {
//自我执行,并传参(将匿名函数造成一个表达式)(传递一个参数)
return
num;
//这里的num写什么均可以
})(i);
//这时候这个括号里面的i和上面arr[i]的值是同样的都是取自for循环里面的i
}
return
arr;
}
console.log(box2());
//[ 0, 1, 2, 3, 4 ]
|
1
2
3
4
5
6
7
8
9
10
11
12
|
function box4() {
var
arr = [];
for
(
var
i = 0; i < 5; i++) {
arr[i] = (function(num) {
//自我执行,并传参(将匿名函数造成一个表达式)(传递一个参数),在这个闭包里面再写一个匿名函数
return
function() {
return
num;
}
})(i);
//这时候这个括号里面的i和上面arr[i]的值是同样的都是取自for循环里面的i
}
return
arr;
}
console.log(box4());
//[ [Function], [Function], [Function], [Function], [Function] ]
|
box4这种写法其实跟上面有一种是一致的,就很少说了,其实主要就是闭包,稍微改变一下代码,实现的结果却大相径庭,共勉吧。。。