Why can't I directly assign document.getElementById to a different function?
So I'm trying to define a function g() that is like document.getElementById. The following works just fine:
var g = function(id){return document.getElementById(id)};
But why doesn't this more direct code work?
var g = document.getElementById;
Solution 1:
The problem is that of context. When you fire an object's function, it is fired with the object as the value of this
(unless you specify otherwise). g = document.getElementById
puts the function getElementById
into the variable g
, but doesn't set the context.
Therefore, when you run g(someId)
, there is no context on which the function can run. It is run with the global object window
as the value of this
, and that doesn't work. (To be precise, it doesn't work because you could be operating with any document
object, not just window.document
, and you haven't specified one.)
You could get around this with call
, where you set the context:
g.call(document, someId);
However, this isn't an improvement over the original!
Solution 2:
As lonesomeday's answer already shows, the problem is with the execution context. I would like to add that there is a way to create a function with the execution context. Open your browser's Developer Tools for this page and try the following in the Console:
> document.getElementById('notify-container');
// <div id="notify-container"></div>
> var _d = document.getElementById;
// undefined
> _d
// ƒ getElementById() { [native code] }
> _d('notify-container');
// Uncaught TypeError: Illegal invocation
// at <anonymous>:1:1
As before, directly invoking the variable _d
which is assigned to document.getElementById
fails. But you can use Function.prototype.bind
to bind the execution context.
The
bind()
method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
> var d = document.getElementById.bind(document);
// undefined
> d('notify-container');
// <div id="notify-container"></div>
You also do not have to worry much about browser compatibility if you want to use bind
: See https://caniuse.com/#feat=es5.