解释JavaScript中的奇怪行为

3

我在 Twitter 上看到了这个问题,也无法解释。以下两种方式定义 onload 函数都可以:

1)JSFiddle

<html>
    <head>
        <script>
            onload = function(){
                alert('this works');
            };
        </script>
</head>
<body>
</body>
</html>

2) JSFiddle

<html>
    <head>
        <script>
            window.onload = function(){
                alert('this works');
            };
        </script>
</head>
<body>
</body>
</html>

但是,如果定义如下,即使被分配到window.onload上,它也不起作用。

3)JSFiddle

<html>
    <head>
        <script>
            function onload(){
                alert('this doesnt work');
            };
            alert(window.onload); // this shows the definition of above function
        </script>
</head>
<body>
</body>
</html>

这里发生了什么?

1
你的问题真的让我很感兴趣,我正在进行一些研究。我不确定是因为 hoisting 的原因,就像 dystroy 所说的那样。它可能与浏览器处理事件处理程序的方式有关,但我也不确定... - bfavaretto
3个回答

9
前两个例子将函数分配给window.onload属性(第一个示例中隐含了window.)。实际上,onload属性属于window的原型(方便地称为Window)。
第三种变体声明了一个同名的新的本地函数,并且该函数遮蔽了原型中的属性。这意味着,当你请求window.onload时,引擎首先找到本地版本,然后放弃查找原型链。因此,alert(window.onload);确实会警告您的函数源代码。然而,为了使事件处理程序起作用,它必须被分配到原型对象的onload属性上。
然而,有一些奇怪的事情正在发生:当你尝试分配一个从prototype继承的属性时,它不应该工作,并且应该在对象上创建一个“拥有”的属性,从原型的那个属性上面遮蔽掉(例如:http://jsfiddle.net/ssBt9/)。但是window的行为不同(http://jsfiddle.net/asHP7/),并且行为甚至可能因浏览器而异。

“Shadow”是什么意思,它是一个技术术语吗?我无法理解为什么它在仍然可以从window.onload中引用时不执行。您能否添加更多的解释? - Ashfame
不妨补充一下,声明在全局作用域中的函数会成为窗口对象的属性,但它仍会遮盖原始窗口的 onload 属性。http://jsfiddle.net/Ad7JG/1/ - Fabrício Matté
事实证明我的答案有点儿正确。但我不得不提一个单独的问题才意识到这一点。 - bfavaretto
我认为这不正确。在两种情况下,您都直接分配了窗口的属性,而不是其原型。 - Denys Séguret
@dystroy 别担心。我会尝试在 SO 聊天室里讨论这个问题。昨天试过了,但没人关心。顺便说一下,这就是让我相信 Window.prototype 实际上正在被更改的原因:http://jsfiddle.net/asHP7/(与我上面链接的问题中评论中指出的 http://jsfiddle.net/ssBt9/ 不同)。 - bfavaretto
显示剩余4条评论

3
那是因为在你的脚本运行之前,onload 已经被声明并设为 null
这与以下代码类似:
var v=null;
function v(){
    console.log('hi');
}​​​​
console.log(v); // alerts null

这与这个不同:
function v(){
    console.log('hi');
}​​​​
console.log(v); // alerts the function

当你像这样声明一个函数时,声明和赋值在逻辑上被提升到作用域的“开始”位置,因此赋值实际上并不会发生在onload函数被赋予null值之后。

这就是为什么它与其他情况不同。

window.onload=...

这不是一个声明,而只是一个赋值操作,无法提升。


我还有点困惑。所以你的意思是说有一种隐式的 var onload = null 覆盖了被提升的函数声明? - bfavaretto
是的,在运行脚本之前,窗口对象已经被初始化。您可以在控制台中输入以下内容:console.log(window, window.onload) - Denys Séguret

0
在前两种情况下,您正在定义一个名为onload的window成员。在第三种情况下,您仅定义了一个函数,但它不是当前窗口的成员。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接