前一段時間做一個東西,出現幾次“缺少對象”的錯誤,總結了一下,都和javascript的執行順序有關,但是搜尋了一下,javascript中好像沒有線程的概念,只存在執行的先後順序(某一個部分完成以後,才開始另外的部分),這種情況一般在用setTiemout或者XMLHttpRequest(Asynchronous)時出現。
一、首先是非同步XMLHttpRequest(async)的回呼函數(callback)的執行順序,一開始我寫了類似下面的代碼,但是變數b一直是空的(非同步請求正常):
var a = "";
... ...
request.onreadystatechange = function(data){
if(request.readyState == 4 && request.status == 200){
a = data;
}
}
request.open ('GET', path, true);//如果設定為false,則不是非同步,代碼會按順序執行
var b = a.length;
最後把對資料的操作放到回呼函數裡面,解決問題,改成下面這樣:
request.onreadystatechange = function(data){
if(request.readyState == 4 && request.status == 200){
a = data;
var b = a.length;
}
}
二、其次是在javascript擷取動態產生的Dom元素的問題,用javascript來產生介面,然後對產生的元素進行操作,老是報錯“缺少對象”。簡化代碼如下:
window.onload = function(){
var body = document.getElementsByTagName("body")[0];
var elmt = document.createElement( "p" );
elmt.id = "hello";
var txt = document.createTextNode( "hello" );
elmt.appendChild(txt);
var did = document.getElementById("hello");
alert(did.id);
body.appendChild(elmt);
}
取不到用createElement建立的對象,可是,這個對象在調用前明明已經被建立了啊。後來發現,對這個對象進行操作之前,並沒有把它添加到整個文檔中去,於是調整了一下代碼的順序:
body.appendChild(elmt);
var did = document.getElementById("hello");
alert(did.id);
運行正常了。可是又有新問題,當我把它放到一個稍微複雜的環境中去,只能在最後的時候一次把所有的Dom元素添加到文檔中,也就是appendChild(elmt)不可能放到getElementById(elmtId)前面,於是又一番google/baidu,發現這是一個很普遍、基礎的問題,同時也發現setTimeout的奇妙用途,讓javascript代碼按非順序執行:
setTimeout(function(){
var did = document.getElementById("hello");
alert(did.id);
},0);
body.appendChild(elmt);
這樣的話,即使setTimeout設定的延遲為0,它裡面的函數也會在所有代碼執行完以後,才開始執行,於是問題也就解決了。
參考:
Realazy: 認識延遲時間為 0 的 setTimeout
Javascriptkata:
Ajax, javascript and threads : the final truth
Fitzblog:
Nine Javascript Gotchas