Why can't I omit the parameter of getElementBy

2020-04-17 04:31发布

问题:

Say I have 3 <img> tags on a page, and I would like to get these as an array, so I wrote:

let myArray = ['img1', 'img2', 'img3'].map(id => document.getElementById(id));

... which worked well.

Then I thought, hey, getElementById takes exactly 1 argument. Isn't there a syntax sugar for that? So I wrote:

let myArray = ['img1', 'img2', 'img3'].map(document.getElementById);

... but that didn't work. I got "Illegal invocation" on Chrome.

So it's not syntax sugar then. What's behind all these?

回答1:

JavaScript has a difference between "method call" and "function call". The former will set this, the latter won't. Syntactically, method call must be of form receiver.method(...args). No dot, no method call. So, this:

document.getElementById(id) // method call, `this` is set to `document`
m = document.getElementById; m(id) // function call, `this` is not set

When you do map(document.getElementById), document.getElementById is a function plucked from its object; when map invokes it, it will invoke it without the receiver, this will not be set, and things get bad.

There is a way to save it: bind, which "methodifies" a function by binding a receiver to it: map(document.getElementById.bind(document)) should work.

EDIT: to further illustrate it:

let foo = {
  bar: function(context) {
    let receiver =
      (this == window) ? "window" :
      (this == foo) ? "foo" :
      "unknown";
    console.log(context + ", `this` is `" + receiver + "`");
  }
}

function call(fn, param) {
  fn(param);
}

foo.bar("In direct call");
let baz = foo.bar; baz("When assigned to a variable");
call(foo.bar, "When passed as a parameter")
let quux = foo.bar.bind(foo); quux("When bound to foo");



回答2:

You can if you pass in document to map()'s second this argument, which will provide the correct context for calling getElementById. Not sure if this is an improvement over just using the arrow function, though.

let myArray = ['img1', 'img2', 'img3'].map(document.getElementById, document)

console.log(myArray)
<p id="img1"><p>
<p id="img2"><p>
<p id="img3"><p>