有意思的bug(all函数)

65 阅读2分钟

今天在练习 JS 的时候遇到了一个很有意思的 bug:

// 1到n
function all(num){
	var ans = 0;
	while(num > 0){
		ans += num;
		num--;
	}
	document.write(ans);
}
...
<input type="button" onclick="all(100)" value="all"/><br />			

这段代码没有任何语法错误,但是在浏览器中运行时,点击按钮后没有任何反应,使用 F12 调试也没有任何报错。

那么问题在哪呢?

答案在函数名 all。

这个 all 和浏览器内置属性 document.all 冲突了。当遇到onclick事件时,浏览器会优先匹配document对象中的函数,然后再匹配window对象中的函数。而我们在 <script></ script>中使用 function进行函数声明时会挂在window对象上。

因此,以上代码在运行时会优先匹配document中的all属性。

而document.all这个属性则是一个已经废弃的只读属性,用于获取当前页面中的元素,作用类似于现在的 document.getElementById等等方法,所以上面的all函数在执行时的意思应该是”获取当前页面的第101个元素“,但没有后续操作,自然点击后没有反应也没有报错。

我们也可以换一个方法来验证。

将代码修改成这样:

// 1到n
function all(num){
	var ans = 0;
	while(num > 0){
		ans += num;
		num--;
	}
	document.write(ans);
	return 5050;
}
...
<input type="button" onclick="alert(all(0))" value="all"/><br />			

运行后点击按钮,显示框弹出

[object HTMLHtmlElement]

说明现在all(0)返回的是一个<html> 标签,与前面document.all 返回的页面元素正好符合。

我们再将alert(all(0)) 改为 alert(window.all(0)) 此时显示框弹出5050

说明此时onclick事件连接的正是我们自己定义的all函数。

至此,这个bug的起因我们就明白了,本质还是因为函数命名的冲突,导致运行时浏览器访问到了一个已经废弃的旧属性,所以解决这种问题最好的方法还是慎用常用词给函数命名。

又: 有关document.all这个废弃属性还有onclick事件的作用域还有可以挖掘的地方。