ok, 6 years of this question playing on my mind, and today I ran into a similar problem and decided to address these once and for all, although spoiler the onload answer is not as satisfying
the window.onload is actually a getter/setter and it is the setter function that actually registers the event handler, all event handlers are getter/setter ie div.onclick
<script src='https://javascript-2020.github.io/stackoverflow/libs/stringify.js'></script>
<div id=output style='font-family:monospace;white-space:pre'></div>
<script>
var desc = Object.getOwnPropertyDescriptor(window,'onload');
output.textContent = stringify(desc);
</script>
only a runtime assignment that goes through the [[set]] operation will invoke the setter defined on window.onload and hence register the handler
when the function onload is declared it overwrites the original property accessor
<script src='https://javascript-2020.github.io/stackoverflow/libs/stringify.js'></script>
<div id=output style='font-family:monospace;white-space:pre'></div>
<script>
var desc = Object.getOwnPropertyDescriptor(window,'onload');
output.textContent = stringify(desc);
function onload(){}
</script>
this is a direct write to the internal property slot, not a JavaScript-level assignment
the ECMAScript spec defines this behavior under Global Environment Records.
tc39.es : Spec Reference: Global Environment Record
when a function is declared in the global scope:
this property is set via internal methods so no setter is triggered
the similar problem i faced today, i was generating some sample html, clicking the submit button enters an infinite loop
<form onsubmit='onsubmit(event)' action='javascript:void(0)'>
<label for=email>Email address:</label>
<br>
<input type=email id=email name=email required value='[email protected]'>
<br>
<button type=submit>Subscribe</button>
</form>
<script>
function onsubmit(e){
alert('you subscribed!');
}//onsubmit
</script>
ok, thats another oddity i thought
turns out javascript uses the string from the tag attribute to generate a function via new Function, and assigns it to the form.onsubmit property, hence
form.onsubmit = new Function('event','onsubmit(event)');
then due to function name inference spec introduced in ES2015. tc30.es : SetFunctionName ( name is property name, then 3 )
the function name becomes 'onsubmit', and an infinit loop results, it effectively becomes
form.onsubmit = function onsubmit(event){onsubmit(event)};
tl;dr
event handler property assignment is a useful tool
when dealing with functions for use as event handler property assignment be careful and either drop the 'on' or declare it as 'onloadh' .. other suggestions welcome in the comments