修改《JavaScript DOM Programming Art (2nd Ed)》中的 moveElement 函數後,動畫無法運行。原始碼透過 setTimeout("moveElement('" + elementID + "'," + final_x + "," + final_y + ")", interval) 使用字串作為第一個參數。
當 setTimeout 第一個參數為字串時,該字串會在全域作用域 (global scope / window) 下執行評估 (相當於 eval)。
若你將函數定義在另一個函數內部 (如 moveElement2 內),或使用 var moveElement = function() {} / const moveElement = () => {} 等區域變數宣告方式,該函數**不會**掛載在 window 物件上。字串評估時找不到該函數,導致報錯 moveElement is not defined 或無反應。
// 強制變成全域函數
window.moveElement = function(elementID, final_x, final_y, interval) {
// ... 你的動畫邏輯
// 遞迴呼叫時也要用 window.moveElement 或直接 moveElement (此時已是全域)
var repeat = "window.moveElement('" + elementID + "'," + final_x + "," + final_y + ")";
setTimeout(repeat, interval);
};
缺點: 污染全域命名空間,不利於模組化,字串模式效能差且有安全風險 (類 eval)。
function moveElement(elementID, final_x, final_y, interval) {
var elem = document.getElementById(elementID);
if (!elem) return;
// ... 計算位移邏輯 ...
// 使用閉包保留參數,傳遞函數引用而非字串
var repeat = function() {
moveElement(elementID, final_x, final_y, interval);
};
setTimeout(repeat, interval);
// 或 ES6 箭頭函數更簡潔:
// setTimeout(() => moveElement(elementID, final_x, final_y, interval), interval);
}
優點: 符合 ES5/ES6 標準,利用閉包自然保留參數,無全域污染,效能優於字串解析,程式碼可讀性高,易於除錯。
原書範例中 moveElement 定義在最外層 (function moveElement() {}),在瀏覽器環境下頂層函數宣告會自動成為 window 的屬性,所以字串 "moveElement(...)" 能被全域找到。你的修改將其巢狀在別的函數中,打破了這個前提條件。
setTimeout/setInterval 中使用字串參數。window (如 window.myFunc = ...)。