錯誤代碼
window.onload = function() {
var xmlHttp = createXMLHttpRequest();
xmlHttp.open("GET", "/ajax/PServlet", true);
xmlHttp.send(null);
xmlHttp.onreadystatechange = load(xmlHttp); // 錯誤:立即執行 load,返回值 (undefined) 賦給處理器
};
function load(xmlHttp) {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { ... }
}
三大錯誤
- 賦值處理器時調用了函數:
load(xmlHttp) 會**立即執行** load,返回 undefined 賦給 onreadystatechange。正確是賦值**函數引用**:load。
- 局部變量
xmlHttp 在回調時可能已變/丟失: 雖然閉包能捕獲,但異步回調時外層函數已結束,若複用變量發起多請求會串聯。
- 未處理
readyState 變化流程: 必須在回調內判斷 readyState === 4。
修正方案一:全局/外部變量 + 函數引用 (傳統修法)
var xmlHttp; // 全局/模塊級
window.onload = function() {
xmlHttp = createXMLHttpRequest();
xmlHttp.open("GET", "/ajax/PServlet", true);
xmlHttp.send(null);
xmlHttp.onreadystatechange = load; // 僅函數名
};
function load() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
// 處理 xmlHttp.responseText
}
}
修正方案二:閉包捕獲單個請求對象 (推薦,支持並發)
window.onload = function() {
var xhr = createXMLHttpRequest(); // 局部常量
xhr.open("GET", "/ajax/PServlet", true);
xhr.send(null);
xhr.onreadystatechange = function() { // 箭頭函數/匿名函數形成閉包
if (xhr.readyState == 4 && xhr.status == 200) {
// 直接使用 xhr
console.log(xhr.responseText);
}
};
};
現代最佳實踐:Fetch API / Axios (拋棄原生 XHR)
// Fetch + async/await
async function fetchData() {
try {
const response = await fetch('/ajax/PServlet');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.text(); // 或 json()
console.log(data);
} catch (err) {
console.error(err);
}
}
// Axios (需引入庫)
axios.get('/ajax/PServlet')
.then(res => console.log(res.data))
.catch(err => console.error(err));
關鍵點總結
| 錯誤寫法 | 正確寫法 | 原因 |
onreadystatechange = load(xhr) | onreadystatechange = load 或 = () => { ... } | 賦值函數引用而非調用結果 |
全局 var xhr 共用 | 請求內部 const xhr + 閉包 | 避免併發請求覆蓋/競態 |
同步 open(..., false) | 異步 open(..., true) + 回調/Promise | 不阻塞主線程,體驗好 |