Global method calls

# Domenic Denicola (10 years ago)

(Cc'ing public-script-coord in case this answer gets more complicated in the presence of the window proxy/overriden this-in-global setup.)

Given code like

<script>
  addEventListener("foo", function () { });
</script>

Is this supposed to be an Invoke(current global, "addEventListener", <"foo", function () {}>)?

Or is it just a Call(addEventListener, undefined, <"foo", function () {}>)?

What about if I added "use strict"; to the top of the script?

My suspicion was Invoke, but then I came up with another example that threw me off:

<script>
window.globalFunc = function () {
    console.log("this", this);
};
</script>

<script>
"use strict";
globalFunc();
</script>

This gives undefined. (The result is the same if I also prepend "use strict"; to the top of the first script.) That nudges me toward the Call answer.

But under the Call answer, my first example would be broken since the implementation of addEventListener would not have a sensible this to operate on.

Anyway, I'm clearly missing something. What is it, and where are the relevant parts of the spec?

# Mark Miller (10 years ago)

On Sun, Feb 22, 2015 at 1:03 PM, Domenic Denicola <d at domenic.me> wrote:

(Cc'ing public-script-coord in case this answer gets more complicated in the presence of the window proxy/overriden this-in-global setup.)

Given code like

<script>
  addEventListener("foo", function () { });
</script>

Is this supposed to be an Invoke(current global, "addEventListener", <"foo", function () {}>)?

Or is it just a Call(addEventListener, undefined, <"foo", function () {}>)?

What about if I added "use strict"; to the top of the script?

If it is strict code, then this is definitely a Call(addEventListener, undefined, <"foo", function () {}>)

I won't try to speak definitively for what happens if the code above is sloppy. But I believe the answer is the same. If the receiver is sloppy, it is up to it to promote an undefined this-binding to its realm's global object. As a builtin, it is neither strict nor sloppy, and its spec should state what it does with an undefined this-binding.

My suspicion was Invoke, but then I came up with another example that threw me off:

<script>
window.globalFunc = function () {
    console.log("this", this);
};
</script>

<script>
"use strict";
globalFunc();
</script>

This gives undefined. (The result is the same if I also prepend "use strict"; to the top of the first script.)

It is quite bizarre that you also get undefined when you don't prepend "use strict" to the first script. globalFunc should then be sloppy. A sloppy function should never see its "this" bound to undefined, or indeed to any non-object value. I do not understand what might be going on here.

Regarding the second script, since you're calling globalFunc as a function from strict code, you are passing a this-binding of undefined, even though globalFunc is a global function.

That nudges me toward the Call answer.

But under the Call answer, my first example would be broken since the implementation of addEventListener would not have a sensible this to operate on.

If addEventListener was sloppy, it would promote the this-binding to the global of the realm it was defined in. As a builtin, it is neither strict nor sloppy, and its spec should state what it does with an undefined this-binding.

Anyway, I'm clearly missing something. What is it, and where are the relevant parts of the spec?

I remember where they were in ES5. I am also curious where to find this in ES6.

# Domenic Denicola (10 years ago)

Thanks Mark. At this point it may tend toward more of a public-script-coord question...

From: Mark Miller [mailto:erights at gmail.com]

If it is strict code, then this is definitely a Call(addEventListener, undefined, <"foo", function () {}>)

I won't try to speak definitively for what happens if the code above is sloppy. But I believe the answer is the same. If the receiver is sloppy, it is up to it to promote an undefined this-binding to its realm's global object. As a builtin, it is neither strict nor sloppy, and its spec should state what it does with an undefined this-binding.

The added weirdness here is that addEventListener is actually a method of EventTarget, which Window derives from. (And then, of course, the window proxy masks direct access, at least when you do window.addEventListener---but apparently the window proxy is not involved in my case.)

The spec for addEventListener 1 doesn't mention what to do with an undefined this binding. Although the language is of the typical imprecise-DOM-spec-type, as far as I can tell it assumes that its this is always an EventTarget instance, which then has "an associated list of event listeners" it operates on.

At this point I must imagine that there is some special handling taking place somewhere else in the web ecosystem, possibly in WebIDL, that will ensure addEventListener (and possibly any other method?) will use the global window (but not the window proxy?) when called with undefined this. I don't know where to find that, though: I looked through 2 without much luck, and Ctrl+Fing for [[Call]] throughout WebIDL does not give anything fruitful.

It is quite bizarre that you also get undefined when you don't prepend "use strict" to the first script. globalFunc should then be sloppy. A sloppy function should never see its "this" bound to undefined, or indeed to any non-object value. I do not understand what might be going on here.

You are right; please disregard this part. I don't get undefined in the given example---only when adding "use strict" to both.

# Domenic Denicola (10 years ago)

OK, I think I might be on the trail of this one.

1 indicates a plan to make [Global]-annotated objects, like the Window object, apply "[ImplicitThis] behavior" to the object's methods and the methods of anything that shows up in its prototype chain. [ImplicitThis] behavior is defined at 2, and does exactly what we need, i.e. makes calls with undefined this get translated into calls with the window.

However, the plan at 1 seems to be only half-executed, in that Window defined at 3 does not have [ImplicitThis], but the definition of [PrimaryGlobal] and [Global] at 4 does not imply [ImplicitThis] behavior. Due to this half-way state, [ImplicitThis] seems to be "dead code," as evidenced by 5.

If I am indeed reading the situation correctly, I think the spec-level fix is to either implement the plan in 1, or to put [ImplicitThis] (back?) on the globals and on EventTarget. I actually prefer the latter, since the way in which tagging [Window] as a [Global] implicitly makes EventTarget, defined in another spec, take on [ImplicitThis] behavior, seems hard to follow.

# Mark Miller (10 years ago)

heycam.github.io/webidl/#ImplicitThis says:

If the [ImplicitThis] heycam.github.io/webidl/#ImplicitThis extended

attribute heycam.github.io/webidl/#dfn-extended-attribute appears on an interface heycam.github.io/webidl/#dfn-interface, it indicates that when a Function corresponding to one of the interface’s operations heycam.github.io/webidl/#dfn-operation is invoked with thenull or undefined value as the this value, that the ECMAScript global object will be used as the this value instead. This is regardless of whether the calling code is in strict mode.

"the ECMAScript global object"? Which one? (Even if it is clear from context, please assume I do not have that context.)

# Domenic Denicola (10 years ago)

From: Mark Miller [mailto:erights at gmail.com]

"the ECMAScript global object"? Which one? (Even if it is clear from context, please assume I do not have that context.)

Heh; good catch. A contemporary thread to my [1] is lists.w3.org/Archives/Public/public-script-coord/2013JulSep/0658.html, which debates that very question.

# Mark Miller (10 years ago)

There, Boris writes:

Conceptually, using the global of the realm of the function involved (i.e. the Chrome/Firefox/IE10 behavior) makes sense to me.

Me too. This is in keeping with the spirit of lexical scoping. It is as if these built-in functions have lexically captured the global of the realm of their creation, and use that. Besides "throw", any other answer would be too much magic and (at least) hard to explain. Also, this aligns with the global capture of sloppy functions.

# Brendan Eich (10 years ago)

Mark Miller wrote:

There, Boris writes:

Conceptually, using the global of the realm of the function involved (i.e. the Chrome/Firefox/IE10 behavior) makes sense to me.

Me too. This is in keeping with the spirit of lexical scoping. It is as if these built-in functions have lexically captured the global of the realm of their creation, and use that. Besides "throw", any other answer would be too much magic and (at least) hard to explain. Also, this aligns with the global capture of sloppy functions.

This is how it was back in the beginning. Function objects each had a "parent" internal slot referencing their global.

# Boris Zbarsky (10 years ago)

On 2/22/15 4:55 PM, Domenic Denicola wrote:

[1] indicates a plan to make [Global]-annotated objects, like the Window object, apply "[ImplicitThis] behavior" to the object's methods and the methods of anything that shows up in its prototype chain.

More like a proposal than a plan, sadly. One that got no response.

However, the plan at [1] seems to be only half-executed, in that Window defined at [3] does not have [ImplicitThis], but the definition of [PrimaryGlobal] and [Global] at [4] does not imply [ImplicitThis] behavior.

Right, so...

I think the [ImplicitThis] thing is silly, because it requires anyone creating any mixin that will got on the window to remember to use it. It also requires adding [ImpliciThis] to Window and EventTarget, but even this low bar hasn't been cleared, obviously.

If I am indeed reading the situation correctly, I think the spec-level fix is to either implement the plan in [1], or to put [ImplicitThis] (back?) on the globals and on EventTarget.

And on every single thing that gets mixed into Window, afaict, though maybe we could wordsmith the bits around what [ImplicitThis] does such that it automatically applies to things pulled in via "implements"...

We'd also need it on all the worker global interfaces, right? And any other kinds of globals with nontrivial proto chains that anyone creates. Or do we want different behavior in workers?

If we do want this behavior in all Web IDL globals, we could just declare that all Web IDL methods are automatically [ImplicitThis], like I said in lists.w3.org/Archives/Public/public-script-coord/2013JulSep/0657.html

I actually prefer the latter, since the way in which tagging [Window] as a [Global] implicitly makes EventTarget, defined in another spec, take on [ImplicitThis] behavior, seems hard to follow.

I agree that this is rather annoying.

# Domenic Denicola (10 years ago)

(es-discuss to bcc)

I see the attraction of having [Global] do all the work for us, for all the logistical reasons you mention. But I still really don't like the action-at-a-distance on EventTarget.

Maybe this is a happy medium?

  • [Global]/[PrimaryGlobal] => operations from the interface or its consequential interfaces get [ImplicitThis] behavior.
  • EventTarget, and anything else people want to put on the global prototype chains, get either [ImplicitThis] or something like [AllowedInGlobalPrototypeChain] that has the same effect.

We could even disallow globals from inheriting from things without [ImplicitThis] / [AllowedInGlobalPrototypeChain].