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 = func开发者_JAVA技巧tion(id){return document.getElementById(id)};
But why doesn't this more direct code work?
var g = document.getElementById;
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!
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.
Because when you call "g('somestr')" the "this" value is bound to the "window" and "document.getElementById" seens to expect to be bound to "document" instead "window"(makes sense to me =) ). Doing:
var g = document.getElementById;
g('somestr');
is like:
document.getElementById.call(window, 'somestr');
The problem is the context. document.getElementById(xxx)
runs in the context of document
, but when you assign it to something, you get a TypeError because the context is probably window
. See this jsFiddle: http://jsfiddle.net/minitech/as3AQ/
So, you could use call
or apply
, but that's ugly. Just do the second way instead, or assign it to document
and assign document
to something if your objective is to minify your code.
@minitech and @Tim Down Thanks a lot for your feedback.
We have a lot of old code which worked like this
var $ = document.getElementById;
$("First");
It Failed with error message in modern browsers Uncaught TypeError: Illegal invocation
All i did is replaced it as
var $ = function(x) { return document.getElementById(x); };
$("First");
Now we are back in the game :)
精彩评论