22

JavaScript is purported to have first-class functions, so this seems like the following ought to work:

var f = document.getElementById;
var x = f('x');

But it fails on all browsers, with a different cryptic error message on each one. Safari says “Type error”. Chrome says “Illegal invocation”. Firefox says “Could not convert JavaScript argument”.

Why?

Rory O'Kane
  • 25,436
  • 11
  • 86
  • 123
Lisper
  • 223
  • 1
  • 6

3 Answers3

37

When you call obj.method() in Javascript the method is passed obj as this. Calling document.getElementById('x') with therefore set this to document.

However if you just write f = document.getElementById you now have a new reference to the function, but that reference is no longer "bound" to document.

So your code doesn't work because when you call f as a bare function name it ends up bound to the global object (window). As soon as the innards of the function try to use this it finds that it now has a window instead of a document and unsurprisingly it doesn't like it.

You can make f work if you call it so:

var x = f.call(document, 'x');

which calls f but explicitly sets the context to document.

The others way to fix this is to use Function.bind() which is available in ES5 and above:

var f = document.getElementById.bind(document);

and is really just a generalised short cut for creating your own wrapper that correctly sets the context:

function f(id) {
    return document.getElementById(id);
}
Daniel Morell
  • 1,316
  • 12
  • 22
Alnitak
  • 313,276
  • 69
  • 379
  • 466
22

Because in JavaScript functions arent bound to context (this). You may use bind():

var f = document.getElementById.bind(document);
Tomasz Nurkiewicz
  • 311,858
  • 65
  • 665
  • 652
  • 4
    To clarify this answer, `this` in `document.getElementById()` is `document`, while `this` where you're executing your scope is the global scope, aka `this` is the `window` object. – Andy Ray Jan 07 '12 at 22:28
4

Using ES6's spread operator, you could also try:

function f(){
    return document.getElementById(...arguments);
};

Babel gives this:

function f() {
    var _document;
    return (_document = document).getElementById.apply(_document, arguments);
};