今天有人在群里問setTimeout第一次參數為字符串的時候為什么會報錯,代碼如下:
function display(obj) { obj.style.display='none'; window.setTimeout("obj.style.display='inline'", 500); }
報obj is not defined。
經過我自己測試了列子,自己總結出了幾個結論,真實性有待考證。下面講講我的例子與結論。
首先,setTimeout的第一個參數分為3類,1.字符串代碼 2.method 3.function 。
1.字符串代碼:
function display(obj) { obj.style.display='none'; window.setTimeout("obj.style.display='inline'", 5000); }
當setTimeout第一個參數為字符串代碼時,執行這段代碼會報obj未定義,原因是因為setTimeout方法是window的方法,是個全局方法。執行這個方法的代碼的作用域環境是window。
obj是當初display方法的參數傳進來的,是個局部變量。在window下找不到obj這個變量所以報未定義。
2.method:
在這兒我把method和function區分開了,區別在哪兒呢?代碼如下:
function display(obj) { obj.style.display='none'; window.setTimeout("fn(obj)", 5000); } function fn(o){ o.style.display='inline' }
像上面代碼一樣執行,還是會報obj未定義,原因和上面一樣的,這兒調用的fn()是全局方法,它的參數obj在全局變量中找不到。
這個和參數為function的區別在於,method是定義好了方法,當成第一個參數傳過setTimeout(),而function是在第一個參數的位置定義function,兩者有很大的區別,下面會講解。
注意,這兒提醒下,我在寫代碼的時候犯了個錯誤,代碼如下:
function display(obj) { obj.style.diaplay='none'; window.setTimeout(fn(obj),500); } function fn(0){ o.style.display='inline'; }
我把fn(obj)的引號去掉了,這樣的寫法是錯誤的,這樣寫,fn會被立即調用,而不是500毫秒之后。
3.function:
function display(obj) { obj.style.diaplay='none'; window.setTimeout(function(){ obj.style.display='inline';},500); }
這種寫法是把function直接寫在了setTimeout的第一個參數位置,這樣寫就和display()形成了一個閉包。所以setTimeout執行function的時候display的作用域是存在的。
這樣就會先去display()的作用域找obj這個變量,obj是當方法的參數傳進來的,所以是能找到的。這樣就能正確的執行這段代碼。
總結:
要解決這種問題又兩個方法,第一:把obj聲明成全局變量,setTimeout是可以調用的。全局變量會一直存在,直到頁面關閉。
第二種:把setTimeout方法的第一個參數寫成function,這樣形成一個閉包來訪問局部變量。但是閉包會使得它的父函數的變量和方法一直處於內存,直到閉包函數調用結束。
此兩種方法各有優缺點,根據自己的需求來取舍。