题目为什么叫setTimeout的第一个参数而不是回调函数?如果你心中有稍有疑惑,或许应该看看下面的文章
我们日常使用setTimeout(),一般是将函数作为第一个参数,但是也有例外情况,先看以下代码:
function test() {
var cl = function() {
console.log(666)
}
setTimeout('cl()', 1500)
}
test()
将以上代码CV到chrome中的console,运行发现:
Uncaught ReferenceError: cl is not defined
没有定义cl函数,奇怪是并没有报Uncaught SyntaxError: Unexpected identifier
这样的语法错误,查javascript MDN我们就会发现:
setTimeout允许讲一个字符串作为第一个参数,而且js内部将会调用eval()函数用来动态执行一段字符串脚本,至于为什么找不到cl函数,我们猜想是作用域问题,既然是eval动态执行,我们在字符串参数中输出当前this绑定的对象:
function test() {
var cl = function() {
console.log(666)
}
setTimeout('console.log(this);cl()', 1500)
}
test()
执行后发现:
原来this绑定window全局对象,这下明白了,eval()执行动态脚本的时候,在全局作用域并没有找到我们定义在函数test内部的cl,所以会报错。
我们将cl定义移到外部:
ok了
————分割线————
var cl = function() {console.log(666)}
setTimeout(cl(), 1500)
经常看到有新人在社区上问这段代码为什么没有延迟执行,只需注意这边的cl()是一个函数执行而不是函数定义,如果想延迟执行,我们需要传递一个函数地址,比如:
var cl = function() {console.log(666)}
setTimeout(cl, 1500)
或者直接return一个函数:
var cl = function() {
return function() {
console.log(666)
}
}
setTimeout(cl(), 1500)
又或者参数处直接定义:
setTimeout(function() {console.log(666)}, 1500)
归根结底还是搞清引用函数地址和执行函数的区别
————分割线————
以上规则也同样适用于字符串参数,只是字符串参数中要加上()保证eval的时候执行,不要到时候只是eval了一个地址 =。=,比如这样:
var cl = function() {
console.log(666)
}
function test() {
setTimeout('cl', 1500)
}
test()
执行一下,啥屁也没看到 = 。=
总之,在setTimeout的时候尽量不要用字符串的参数,因为eval()具有许多不可预见的危险性,比如说可能有意外的运行结果,可能隐式创建全局变量,闭包作用域解析过多消耗,xss,运行慢啊巴拉巴拉之类的。但是我们也需要了解下js的一些黑魔法,以防到时候懵逼。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。