題目爲何叫setTimeout的第一個參數而不是回調函數?若是你心中有稍有疑惑,或許應該看看下面的文章javascript
咱們平常使用setTimeout(),通常是將函數做爲第一個參數,可是也有例外狀況,先看如下代碼:java
function test() { var cl = function() { console.log(666) } setTimeout('cl()', 1500) } test()
將以上代碼CV到chrome中的console,運行發現:chrome
Uncaught ReferenceError: cl is not defined閉包
沒有定義cl函數,奇怪是並無報Uncaught SyntaxError: Unexpected identifier
這樣的語法錯誤,查javascript MDN咱們就會發現:xss
setTimeout容許講一個字符串做爲第一個參數,並且js內部將會調用eval()函數用來動態執行一段字符串腳本,至於爲何找不到cl函數,咱們猜測是做用域問題,既然是eval動態執行,咱們在字符串參數中輸出當前this綁定的對象:ide
function test() { var cl = function() { console.log(666) } setTimeout('console.log(this);cl()', 1500) } test()
執行後發現:函數
原來this綁定window全局對象,這下明白了,eval()執行動態腳本的時候,在全局做用域並無找到咱們定義在函數test內部的cl,因此會報錯。this
咱們將cl定義移到外部:code
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的一些黑魔法,以防到時候懵逼。