为何代码测试正常,添加window.load事件后报错‘test未定义’?
嘿,这个问题我太熟了!咱们来一步步拆解为啥会出这问题哈:
原代码正常运行的原因
原代码里的test函数是定义在全局作用域下的,哪怕你用setTimeout("test()")这种字符串形式调用,JS引擎也能在全局作用域里找到test函数,所以运行完全没问题。
改成load事件后出错的核心原因
当你把所有代码塞进window.addEventListener('load')的回调函数里后,test就变成了这个回调函数的局部函数——它只在这个回调的作用域内部可见,全局作用域根本访问不到它。
而这里的坑就出在setTimeout("test()")这个写法上:当你给setTimeout传字符串参数时,JS会在全局作用域下去解析并执行这段字符串。这时候全局里没有test函数,自然就抛出ReferenceError: test is not defined的错误了。
顺便提一句:为啥点击按钮时调用test()没问题?因为按钮的点击回调和test函数处于同一个作用域(load回调的局部作用域),所以能直接访问到test,这部分是没问题的。
解决办法
推荐用下面两种方式修复,优先选第一种:
最推荐:直接传递函数引用给setTimeout
把setTimeout("test()", 1000)改成setTimeout(test, 1000)。这样setTimeout会直接使用当前作用域下的test函数,不需要去全局找,完美解决问题。修改后的代码片段:window.addEventListener('load', function () { var count = 0; var timeout; function test() { console.log(count); count = count + 1; timeout = setTimeout(test, 1000); // 这里改成函数引用 } document.getElementById("click").addEventListener("click", function (e) { test(); }, false); });不推荐:把test暴露到全局(会污染全局变量)
如果非要保留字符串形式的调用,可以在load回调里把test挂载到全局对象上:window.addEventListener('load', function () { var count = 0; var timeout; function test() { console.log(count); count = count + 1; timeout = setTimeout("test()", 1000); } window.test = test; // 把test暴露到全局 document.getElementById("click").addEventListener("click", function (e) { test(); }, false); });但这种方式会污染全局作用域,容易引发变量冲突,除非特殊情况不建议用。
内容的提问来源于stack exchange,提问作者ii iml0sto1

