参考
1. MDN window.setTimeout
2. JavaScript 标准参考教程 - 定时器
3. 你所不了解的setTimeout
4. 彻底理解setTimeout()
API语法
var timerId = setTimeout(func | code[, delay]);
var timerId = setTimeout(func[, delay, param1, param2])
- API中第一个参数是
function
或者字符串形式的code
,正常情况下使用function
; - Node中第一个参数非
function
会报错; - IE10+才支持带
param
写法; delay
可省略,默认为0;this
指向默认为window
;code
形式是没有第三个参数的,即使写了也读取不到arguments
。
1. 写法对比
// 浏览器
var func = function() {
console.log(1)
}
// 正常写法,延迟输出1;
setTimeout(func, 1000)
// 此处func()立即执行,返回undefined,相当于
// func()
// setTimeout(undefined, 1000)
// 立即输出1
setTimeout(func(), 1000)
// 字符串写法,同正常写法,延迟输出1
setTimeout('func()', 1000)
2. 异步输出
delay为0依然是异步输出
console.log(1)
setTimeout(function () {
console.log(2)
}, 0)
console.log(3)
// 1 3 2
3. 见怪不怪的一题
for (var i = 1; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, i * 1000)
}
// 5 5 5 5
for
循环中var
声明的是全局作用域setTimeout
中function
现在一边观望- 同步操作完成后,
function
中i
随着作用域链找到全局中的i
使结果预期为1 2 3 4
的方法之一就是创建一个作用域用来覆盖全局作用域中的i
变量
for (var i = 1; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, i * 1000)
})(i)
}
// 1 2 3 4
方法之二是利用API中的第三个参数,传参进去
for (var i = 1; i < 5; i++) {
setTimeout(function (i) {
console.log(i)
}, i * 1000, i)
}
// 1 2 3 4
此时function
中的i
其实与全局的i
没什么关系了,相当于:
for (var i = 1; i < 5; i++) {
setTimeout(function (j) {
console.log(j)
}, i * 1000, i)
}
// 1 2 3 4
方法之三是ES6中let
声明的块级作用域
for (let i = 1; i < 5; i++) {
setTimeout(function () {
console.log(i)
}, i * 1000)
}
// 1 2 3 4
那以下代码输出什么呢?
var i = 100;
for (let i = 1; i < 5; i++) {
setTimeout(function (i) {
console.log(i)
}, i * 1000)
}
// undefined undefined undefined undefined
出这题应该很变态了,但想到第一个参数总是一个不能传参的函数,假设是temp
,执行时相当于temp()
,并没有任何参数传入,所以预期的i
始终为undefined
。感觉可以将每个setTimeout函数都可以如下
var i = 100;
for (let i = 1; i < 5; i++) {
var temp = function (i){
console.log(i)
}
setTimeout(temp, i * 1000)
}
// undefined undefined undefined undefined
方法之四是使用bind传入参数
for (var i = 1; i < 5; i++) {
setTimeout(function (i) {
console.log(i)
}.bind(null, i), i * 1000)
}
// 1 2 3 4
4. Todo
- 传参
- debounce--见参考2