Perfection Kills

by kangax

Exploring Javascript by example

← back 738 words

onload=function(){} considered harmful

Harmful pattern

There seems to be a new pattern appearing on the web — attaching window load listener through undeclared assignment:


  onload = function(){
    /* ... */
  };

I’d like to explain why it’s a good idea to avoid it.

A conventional approach to perform this task is to explicitly assign to window.onload property. That is, not counting other means like DOM L2 methods — addEventListener (as well as proprietary attachEvent), or intrinsic event attributes — <body onload="...">:


  window.onload = function(){
    /* ... */
  };

How does it work?

A tempting “short” version takes advantage of Javascript loose nature with regards to variable declarations. In Javascript, assigning to undeclared variable actually creates a property on a Global Object — global property. Since Global Object in browsers is usually a window object (or at least it often behaves that way), undeclared assignment essentially results in creation of property on window. As long as Global Object and window are the same entity, window.onload = ... and onload = ... should have identical results. At least, that’s how it is in theory, and in practice there are more implications, as you will see later on.

So if two are identical, why would we ever prefer longer version?

Because shorter one relies on undeclared assignment.

Who cares?

Undeclared assignments have been frowned upon for a long time, and rightfully so. Global variables declared locally are hard to maintain and generally cause confusion. It’s not always clear whether such assignments are intentional or simply an oversight. It is why validators like JSLint have been emiting warnings when encountering them.

MSHTML peculiarities

Another reason to avoid undeclared assignments is due to rather destructive behavior of MSHTML DOM. When undeclared assignment happens in IE, an obscure error is thrown if identifier is named as id or name of one of the elements in a document:


  <p id="foo"></p>
  <form name="bar" action=""><p></p></form>

  <script type="text/javascript">
    try {
      foo = 1;
    }
    catch(e) {
      document.write(e); // TypeError: Object doesn't support this property or method
    }
    try {
      bar = 1;
    }
    catch(e) {
      document.write(e); // ReferenceError: Illegal assignment
    }
  </script>

Note that plain variable declarations in global scope, or explicit assignments have no such problems:


  <p id="foo"></p>
  <form name="bar" action=""><p></p></form>

  <script type="text/javascript">
    var foo = 1; // declares (and initializes) global `foo` variable
    window.foo = 1; // assigns to a "foo" property of `window` object
    this.foo = 1; // assigns to a "foo" property of Global Object
  </script>

But “onload” is different!

Technically speaking, the case with onload = function(){ } can be considered an exception. After all, an intention to create global onload property is rather clear there. It’s also unlikely that there will be an element with such id/name (although, you never know!). There’s, however, another problem rising up, and that problem is strict mode of ECMA-262 5th edition — a standard for an upcoming version of ECMAScript language, approved officially only few days ago.

Strict what?

The premise of strict mode is to provide higher security level for an ECMAScript program (or part of it): avoid features that are considered error-prone or inefficient; employ stricter error checking; provide increased performance. One of such “stricter error checks” happens to be the one with undeclared assignments, which simply throw error:


  "use strict";
  onload = function(){ // ReferenceError
    /* ... */
  };
  window.onload = function(){ // Works as expected
    /* ... */
  };

Now, it’s worth mentioning that not all browsers would throw error. Some of them (e.g. WebKit) actually have properties corresponding to event handlers — such as “onload” — declared implicitly, before script execution occurs. Those that don’t — such as Firefox or Opera — would miserably fail.


  "onload" in window; // true in WebKit, but not Firefox or Opera
  window.onload; // `null` in WebKit, `undefined` in Firefox or Opera
  onload; // `null` in WebKit, ReferenceError in Firefox or Opera

Does it really matter?

It’s good to understand that strict mode is not a requirement, but is merely an option. It is there to provide stricter rules for those who need it, and are willing to cope with (and enjoy) consequences. So if you are planning to make your code “strict”, don’t forget to avoid undeclared assignments — even as innocent-looking as “onload” ones.

Did you like this? Donations are welcome

comments powered by Disqus