"use strict" VS setTimeout
On Sun, Sep 7, 2014 at 7:29 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
This looks like a potential problem when possible passed methods are not bound + it looks inconsistent with "use strict" expectations.
It’s not just setTimeout
– other DOM timer methods have the same
behavior. The spec is here, FWIW:
www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#dom-windowtimers-settimeout
Pretty sure this cannot be changed without breaking the Web.
Fair enough, I was looking for that part indeed but couldn't find anything explicitly related in here: www.w3.org/TR/2011/WD-html5-20110525/timers.html
On Sun, Sep 7, 2014 at 10:36 AM, Mathias Bynens <mathiasb at opera.com> wrote:
On Sun, Sep 7, 2014 at 7:29 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
This looks like a potential problem when possible passed methods are not bound + it looks inconsistent with "use strict" expectations.
Yes. This looks like a typical screwup. Thanks for pointing it out.
It’s not just
setTimeout
– other DOM timer methods have the same behavior. The spec is here, FWIW:www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#dom-windowtimers-settimeout Pretty sure this cannot be changed without breaking the Web.
Why? If "undefined" were passed instead, sloppy callback functions would still see the same behavior -- on entry they would coerce undefined to their global object. Do you think the web already depends on strict functions seeing the global object here, rather than undefined?
var temp = window; document.querySelector('iframe').contentWindow.setTimeout(function() { console.log(temp === window); // false })
setTimeout is a method of global object, not a standalone function.
2014-09-07 19:47 GMT+02:00 Mark S. Miller <erights at google.com>:
My same thoughts on "break the web" ... I think whoever put "use strict" in
there would eventually never expect the this
to be the global context.
@Michał Wadas ... you haven't proved much in there ... you should look at global methods more like this:
// your global context to be executed
with({
get window() {return this},
setTimeout: function () { ... },
alert: function () { ... },
// all other window methods
}) {
setTimeout('alert("hello world")');
}
The fact is a window
method, whatever window it is and of course an
iframe will have its own, does not mean every method is automatically bound
- even if that was the case, what you pass in there is a user defined function, not a windows method.
I think this is an issue I am not sure how come it didn't bite already out there ... but I also understand that putting together everyone to fix this seems utopia so ... watch out your bounds or you might leak or change the global context by accident where "use strict" will not save you.
Best
On Sep 7, 2014, at 19:47 , Mark S. Miller <erights at google.com> wrote:
On Sun, Sep 7, 2014 at 10:36 AM, Mathias Bynens <mathiasb at opera.com> wrote: On Sun, Sep 7, 2014 at 7:29 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
This looks like a potential problem when possible passed methods are not bound + it looks inconsistent with "use strict" expectations.
Yes. This looks like a typical screwup. Thanks for pointing it out.
Interesting. Follow-up question: isn’t strictness propagated lexically? That is, shouldn’t the parameter of setTimeout()
be strict even without being explicitly declared as such?
On Sun, Sep 7, 2014 at 10:29 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
I know this is probably W3C land but the following code shows the global object in every JS engine I could test:
(function () { 'use strict'; setTimeout(function () { 'use strict'; console.log(this); // [window/global Object] }, 0); }());
On Sun, Sep 7, 2014 at 11:02 AM, Axel Rauschmayer <axel at rauschma.de, mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]>
wrote:
On Sep 7, 2014, at 19:47 , Mark S. Miller <erights at google.com, mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]> wrote:
On Sun, Sep 7, 2014 at 10:36 AM, Mathias Bynens <mathiasb at opera.com, mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]> wrote:
On Sun, Sep 7, 2014 at 7:29 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com, mail.google.com/mail/?view=cm&fs=1&tf=1&[email protected]> wrote:
This looks like a potential problem when possible passed methods are not bound + it looks inconsistent with "use strict" expectations.
Yes. This looks like a typical screwup. Thanks for pointing it out.
Interesting. Follow-up question: isn’t strictness propagated lexically?
Yes.
That is, shouldn’t the parameter of
setTimeout()
be strict even without being explicitly declared as such?
Yes, it is. That's what demonstrates that this is a W3C land screwup, as Andrea inferred, rather than a JS issue. The callback is strict. It is setTimeout itself which is explicitly passing it the global as a this-binding, rather than passing it undefined. If the callback function were sloppy, it would see the global object in either case.
Yes Axel, that's how it works, this will show undefined indeed all over
(function () {
'use strict';
function g() {
console.log(this);
}
g(); // undefined
setTimeout(function () {
g(); // undefined
}, 0);
}());
or testing other use strict restrictions:
(function () {
'use strict';
setTimeout(function () {
argument.callee
}, 0);
}());
The strict behavior is preserved, it's not an opt-out, but the invoked function within setTimeout has the global context regardless it has been defined under the strict directive + regardless it defines itself as strict.
Basically if you feel secure about "use strict" here you have a case that shows you shouldn't ... making one point of strict directive kinda broken/pointless.
On Sun, Sep 7, 2014 at 11:07 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
Yes Axel, that's how it works, this will show undefined indeed all over
(function () { 'use strict'; function g() { console.log(this); } g(); // undefined setTimeout(function () { g(); // undefined }, 0); }());
or testing other use strict restrictions:
(function () { 'use strict'; setTimeout(function () { argument.callee }, 0); }());
The strict behavior is preserved, it's not an opt-out, but the invoked function within setTimeout has the global context regardless it has been defined under the strict directive + regardless it defines itself as strict.
Basically if you feel secure about "use strict" here you have a case that shows you shouldn't ... making one point of strict directive kinda broken/pointless.
Agreed. I would remove only "kinda" from that statement ;).
It feels to me also a vector that will happily pass all linters and code analyzers giving users a door to reach native context and start playing in there with everything else. I'm pretty sure you would agree on this too :)
Please let us know if there's any follow up, it's probably easier/faster if some googler mention this issue to other googlers that are collaborating with WHATWG or W3C or both (or none)
I don't understand why this is any more surprising than any other function that calls its callback with .call(something). It doesn't matter whether the callback is strict or not; .call(window), which is what the spec does, will override it.
As far as I can see this issue has absolutely nothing to do with strict vs. sloppy.
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20140907/05af85d7/attachment
On 9/7/14, Domenic Denicola <domenic at domenicdenicola.com> wrote:
I don't understand why this is any more surprising than any other function that calls its callback with .call(something). It doesn't matter whether the callback is strict or not; .call(window), which is what the spec does, will override it.
As far as I can see this issue has absolutely nothing to do with strict vs. sloppy.
I agree on that point; setTimeout passes the window to the callback as the this value. But that has nothing to do with inheritance.
Method setTimeout uses the global object as the this
value for the
callback function, much like addEventListener, etc, as does
.call(window)
like you mentioned.
On Sun, Sep 7, 2014 at 11:27 AM, Domenic Denicola < domenic at domenicdenicola.com> wrote:
I don't understand why this is any more surprising than any other function that calls its callback with .call(something).
The issue is what the something should be, and which choices for something are surprising for what APIs.
It doesn't matter whether the callback is strict or not; .call(window), which is what the spec does, will override it.
I don't understand what you're trying to say here. What will override what?
As far as I can see this issue has absolutely nothing to do with strict vs. sloppy.
As Andrea initially stated, this is a DOM-spec issue, not a JS issue, in that JS allows setTimeout to call the callback with WTF it wants. However, the purpose of setTimeout, etc, from the JS programmer's perspective, is to postpone some action to some future time. From this understanding, there's no reason to give the postponed action access to the global object. From a POLA perspective, there is thus strong reason not to.
Put another way, were the functionality of setTimeout etc only to postpone calling its callback, and not to provide it access to the global object, then we might consider moving it from the W3C side to the JS side, like we did for promises. After all, this temporal postponement function, by itself, would seem equally useful in non-browser contexts, like Node. Speaking of which:
On Sun, Sep 7, 2014 at 11:35 AM, Alex Kocharin <alex at kocharin.ru> wrote:
I would add that in node.js it returns neither undefined nor window, but a timer object, which you can clear up with
clearInterval(this)
inside the callback.
Since these are specced only for the browser, this divergent behavior doesn't violate anything. Were setTimeout etc to be moved from W3C to JS, we would not pass the global object, so again you would not be violating the spec by virtue of not passing the global object.
However, in order to not break the web, as was pointed out, any such possible future JS std setTimeout etc would only pass undefined, so that sloppy functions still see their global object. So in that sense, this node behavior is incompatible with any behavior that could be standardized by a future JS for setTimeout etc.
On Sun, Sep 7, 2014 at 11:44 AM, Garrett Smith <dhtmlkitchen at gmail.com>
wrote:
On 9/7/14, Domenic Denicola <domenic at domenicdenicola.com> wrote:
I don't understand why this is any more surprising than any other function that calls its callback with .call(something). It doesn't matter whether the callback is strict or not; .call(window), which is what the spec does, will override it.
As far as I can see this issue has absolutely nothing to do with strict vs. sloppy.
I agree on that point; setTimeout passes the window to the callback as the this value. But that has nothing to do with inheritance.
Who said anything about inheritance?
Method setTimeout uses the global object as the
this
value for the callback function, much like addEventListener, etc, as does.call(window)
like you mentioned.
Yes it does. The question is: should it?
You wrote itself the surprise, nobody wrote .call(window)
or
.bind(window)
so you receive implicitly a global context in a place you
were not expecting it.
Think about object methods instead of that one shot but also think that
under "use strict" .call(null)
whould not bring the global context in
there.
Accordingly, this feels like some sort of W3C specs misalignment with JS land and the fact at least 2 of us think it's surprising to receive that context automagically in non bound functions maybe indicates something does not feel right, no matter what's written in that specific part of timers specs (also non respected in nodejs land)
But here I go back to the utopia I've already mentioned on putting everyone together to fix this, and I'm fine if it won't change but it's good to know that "use strict" could implicitly fail when the DOM is involved.
On Sep 7, 2014, at 19:54, "Andrea Giammarchi" <andrea.giammarchi at gmail.com<mailto:andrea.giammarchi at gmail.com>> wrote:
But here I go back to the utopia I've already mentioned on putting everyone together to fix this, and I'm fine if it won't change but it's good to know that "use strict" could implicitly fail when the DOM is involved.
To be clear, "use strict" is not failing; it's not like the DOM APIs are doing anything against the JS spec. That is, I wouldn't say that function f(g) { g.call({}); }
makes "use strict" fail.
As for knowing that sometimes DOM APIs call passed-in functions with a thisArg, you should probably already be used to that from addEventListener.
On Sun, Sep 7, 2014 at 7:27 PM, Domenic Denicola <domenic at domenicdenicola.com<mailto:domenic at domenicdenicola.com>> wrote:
I don't understand why this is any more surprising than any other function that calls its callback with .call(something). It doesn't matter whether the callback is strict or not; .call(window), which is what the spec does, will override it.
As far as I can see this issue has absolutely nothing to do with strict vs. sloppy.
On 9/7/14, Mark Miller <erights at gmail.com> wrote:
On Sun, Sep 7, 2014 at 11:27 AM, Domenic Denicola < domenic at domenicdenicola.com> wrote:
I don't understand why this is any more surprising than any other function that calls its callback with .call(something).
The issue is what the something should be, and which choices for something are surprising for what APIs.
It doesn't matter whether the callback is strict or not; .call(window), which is what the spec does, will override it.
I don't understand what you're trying to say here. What will override what?
As far as I can see this issue has absolutely nothing to do with strict vs. sloppy.
As Andrea initially stated, this is a DOM-spec issue, not a JS issue, in that JS allows setTimeout to call the callback with WTF it wants. However, the purpose of setTimeout, etc, from the JS programmer's perspective, is to postpone some action to some future time. From this understanding, there's no reason to give the postponed action access to the global object. From a POLA perspective, there is thus strong reason not to.
If legacy compatibility is needed then setTimeout's callback function must be called with the same window of setTimeout, just as with global addEventListener.
<script> "use strict" addEventListener("click", function(ev){ console.log(this === window); }); </script>
implicitly fail from a user point of view that used "use strict" to avoid receiving the global context in there ... I am not sure how much you want to turn it back to me but you are missing the point and I've not much else to say.
On Sun, Sep 7, 2014 at 7:29 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
I know this is probably W3C land ...
First hit for "callback use strict inurl:lists.w3.org":
lists.w3.org/Archives/Public/public-script-coord/2011JulSep/thread.html#msg3
Garret for legacy we are good to go with .call(undefined)
that will bring
window in there but addEventListener
is a different intent/operation than
setTimeout
plus you lways have e.currentTarget
which is always
preferable anyway since this will not produce what you expect:
addEventListener("click", {handleEvent: function(ev){
console.log(this === window); // nope
}});
Yes Anne, reason I've posted here was to ask opinions from JS land + I am not sure it's that easy to post in W3C mailing list as random chap while here I'm already registered (and here I was looking for opinions beside what specs say)
Good to see MM was already on fire in there :D
On Sun, Sep 7, 2014 at 9:06 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
Yes Anne, reason I've posted here was to ask opinions from JS land + I am not sure it's that easy to post in W3C mailing list as random chap while here I'm already registered (and here I was looking for opinions beside what specs say)
Everyone should be able to post there just fine, you don't even need to subscribe. The only thing you need to take into account if it is really your first time is that you need to give the W3C permission to republish your email (the first time you post you get an email about that). And that first time it'll take a bit longer therefore for your email to reach the list and archives.
I might try again but they'll probably tell me "specs say so, must be good" as others here so not sure I should bother.
Thanks though.
On 9/7/14, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
implicitly fail from a user point of view that used "use strict" to avoid receiving the global context in there ... I am not sure how much you want to turn it back to me but you are missing the point and I've not much else to say.
"use strict" doesn't avoid receiving the global object.
For function calls in strict code, "use strict" says the this
value
is supplied by the caller, but if it isn't, the this
value is
undefined.
The caller of the callback your example below was setTimeout. Method
setTimeout maintains a reference to the window and calls callback
function, a
, with the window as the thisValue.
setTimeout( function a() { console.log(this); }, 1);
this is getting nowhere ... yeah Garret, you can use .call
and we all
know that ...
Now I want you to answer this: why on earth would you expect a global context in a setTimeout or setInterval operation for a function/method you have explicitly defined as strict ?
One single use case ... do you have it ?
'cause you don't use "use strict" inside method/functions passed to
addEventListener
as example, do you?
So I will close the loop with the very initial snippet and just one extra comment
(function () {
'use strict'; // <== as a developer, I don't want implicit window
setTimeout(function () {
'use strict'; // <== as a developer, I don't want implicit window
console.log(this);
// [window/global Object]
// SO WHY ON EARTH I HAVE AN IMPLICIT window HERE ?
}, 0);
}());
Best
On 9/7/14, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
this is getting nowhere ... yeah Garret, you can use
.call
and we all know that ...Now I want you to answer this: why on earth would you expect a global context in a setTimeout or setInterval operation for a function/method you have explicitly defined as strict ?
One single use case ... do you have it ?
'cause you don't use "use strict" inside method/functions passed to
addEventListener
as example, do you?So I will close the loop with the very initial snippet and just one extra comment
(function () { 'use strict'; // <== as a developer, I don't want implicit window setTimeout(function () { 'use strict'; // <== as a developer, I don't want implicit window console.log(this); // [window/global Object] // SO WHY ON EARTH I HAVE AN IMPLICIT window HERE ? }, 0); }());
setTimeout is explicitly calling your callback with the window. It's specified to do so in HTML5. It is a little strange. Should HTML5 be changed?
www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#dom
On Sun, Sep 7, 2014 at 12:50 PM, Garrett Smith <dhtmlkitchen at gmail.com>
wrote:
On 9/7/14, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:
this is getting nowhere ... yeah Garret, you can use
.call
and we all know that ...Now I want you to answer this: why on earth would you expect a global context in a setTimeout or setInterval operation for a function/method you have explicitly defined as strict ?
One single use case ... do you have it ?
'cause you don't use "use strict" inside method/functions passed to
addEventListener
as example, do you?So I will close the loop with the very initial snippet and just one extra comment
(function () { 'use strict'; // <== as a developer, I don't want implicit window setTimeout(function () { 'use strict'; // <== as a developer, I don't want implicit window console.log(this); // [window/global Object] // SO WHY ON EARTH I HAVE AN IMPLICIT window HERE ? }, 0); }());
setTimeout is explicitly calling your callback with the window. It's specified to do so in HTML5. It is a little strange. Should HTML5 be changed?
Yes, this is indeed the only question that Andrea and I are raising in this thread. As you acknowledge, providing window here is a little strange. I quibble with "a little". When a surprise surprises by providing less authority than expected, I don't much care. When the surprise is that more authority is provided than expected, that's a more serious issue.
On 9/7/14, 1:29 PM, Andrea Giammarchi wrote:
I know this is probably W3C land but the following code shows the global object
Careful with your use of the word "the". Your ES5-centric assumptions are showing. ;)
The function passed to setTimeout will be invoked with "this" set to the window that was the "this" of the setTimeout call. Note that setTimeout itself is a sloppy function for historicaly reasons, which is why it has a non-undefined "this" even if called as a bareword.
Now, and here's where I have a problem with your use of "the": the Window that setTimeout is invoked on is NOT necessarily the same as the global object of the function. And the web totally depends (or at least depended a few years ago) on not getting the global object of the function, but rather the Window setTimeout was called on as the "this" in the callback. We tried changing that at one point in Gecko as an experiment, and websites broke.
This looks like a potential problem when possible passed methods are not bound + it looks inconsistent with "use strict" expectations.
If your expectations include "never called via .call() with an explicit 'this' passed in", then the expectations might need adjusting.
On 9/7/14, 9:35 PM, Boris Zbarsky wrote:
Now, and here's where I have a problem with your use of "the": the Window that setTimeout is invoked on is NOT necessarily the same as the global object of the function.
Just to make this concrete, see fiddle.jshell.net/tmt5e9m6/2/show which has pretty much the following source, for posterity:
<!DOCTYPE html> <iframe></iframe> <script> onload = function() { var win = frames[0]; win.test = "INNER"; window.test = "OUTER"; win.setTimeout(function() { alert(this.test); }); } </script>
As far as I can tell, this alerts "INNER" in IE 10 or later, Firefox 2 or later (and probably earlier; I don't have earlier versions on hand right this second), Chrome 14 or later (and likely earlier; once again I have no earlier versions on hand), Safari 4 or later (and again likely earlier).
IE6 through IE9 alert "OUTER", for what it's worth.
On Sun, Sep 7, 2014 at 6:35 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:
On 9/7/14, 1:29 PM, Andrea Giammarchi wrote:
I know this is probably W3C land but the following code shows the global object
Careful with your use of the word "the". Your ES5-centric assumptions are showing. ;)
The function passed to setTimeout will be invoked with "this" set to the window that was the "this" of the setTimeout call. Note that setTimeout itself is a sloppy function for historicaly reasons, which is why it has a non-undefined "this" even if called as a bareword.
Now, and here's where I have a problem with your use of "the": the Window that setTimeout is invoked on is NOT necessarily the same as the global object of the function. And the web totally depends (or at least depended a few years ago) on not getting the global object of the function, but rather the Window setTimeout was called on as the "this" in the callback. We tried changing that at one point in Gecko as an experiment, and websites broke.
This is the crucial experiment. That negative result probably kills the possibility of the improvement we're discussing. Too bad, but better to know early. Thanks.
This looks like a potential problem when possible passed methods are not
bound + it looks inconsistent with "use strict" expectations.
If your expectations include "never called via .call() with an explicit 'this' passed in", then the expectations might need adjusting.
I think this still misses the point. Given that we understand the purpose of setTimeout etc to be postponing action, it is surprising for setTimeout to provide any global object to the callback. Since it does, a) we need to find a way to explain setTimeout etc so that this is not surprising, and b) we need safe alternatives to setTimeout etc that only postpones, perhaps like Q.delay.
No one is saying that it is always surprising for a strict function to be called with a this-binding other than undefined. Indeed, this is necessary for strict functions used as conventional methods.
I wonder what was breaking, specially after showing there were inconsistencies between browsers. Yet nobody wrote a use case where a dev explicitly marks a callback as strict expecting to receive a global context in it once passed without bind to a setTimeout or setInterval ... Anyway, thanks for the heads up on the failing experiment. Curious to know if ES5 at that time was popular.
Sent from my Windows Phone
From: Mark S. Miller <erights at google.com>
Sent: 9/8/2014 5:08 To: Boris Zbarsky <bzbarsky at mit.edu>
Cc: es-discuss <es-discuss at mozilla.org>
Subject: Re: "use strict" VS setTimeout
On Sun, Sep 7, 2014 at 6:35 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:
On 9/7/14, 1:29 PM, Andrea Giammarchi wrote:
I know this is probably W3C land but the following code shows the global object
Careful with your use of the word "the". Your ES5-centric assumptions are showing. ;)
The function passed to setTimeout will be invoked with "this" set to the window that was the "this" of the setTimeout call. Note that setTimeout itself is a sloppy function for historicaly reasons, which is why it has a non-undefined "this" even if called as a bareword.
Now, and here's where I have a problem with your use of "the": the Window that setTimeout is invoked on is NOT necessarily the same as the global object of the function. And the web totally depends (or at least depended a few years ago) on not getting the global object of the function, but rather the Window setTimeout was called on as the "this" in the callback. We tried changing that at one point in Gecko as an experiment, and websites broke.
This is the crucial experiment. That negative result probably kills the possibility of the improvement we're discussing. Too bad, but better to know early. Thanks.
This looks like a potential problem when possible passed methods are not
bound + it looks inconsistent with "use strict" expectations.
If your expectations include "never called via .call() with an explicit 'this' passed in", then the expectations might need adjusting.
I think this still misses the point. Given that we understand the purpose of setTimeout etc to be postponing action, it is surprising for setTimeout to provide any global object to the callback. Since it does, a) we need to find a way to explain setTimeout etc so that this is not surprising, and b) we need safe alternatives to setTimeout etc that only postpones, perhaps like Q.delay.
No one is saying that it is always surprising for a strict function to be called with a this-binding other than undefined. Indeed, this is necessary for strict functions used as conventional methods.
On 9/8/14, 3:50 AM, Andrea Giammarchi wrote:
I wonder what was breaking
I don't remember, unfortunately. :(
specially after showing there were inconsistencies between browsers.
It's worth asking the IE team whether they changed because of concrete web compat issues or just to align with the spec and other browsers.
Yet nobody wrote a use case where a dev explicitly marks a callback as strict
It doesn't matter. The web compat constraint, if any, would be with sloppy callbacks. If the callback expects a "this" object other than its global, the caller needs to pass it explicitly. Unless you want callers introspecting the strictness of the callee before deciding on what to do with "this"... but that seems pretty weird too.
Curious to know if ES5 at that time was popular.
I don't know what you mean here.
no introspection or nothing magic and weird, simply .call(undefined)
would do for sloppy and strict, preserving global in sloppy, avoiding
shenanigans in strict.
Hence my curiosity: when this experiment was made, which code with "use strict"
failed ? 'cause that would be the only one that either was working
"by accident" polluting a global context or the use case I am
asking/looking for.
On 9/8/14, 8:15 AM, Andrea Giammarchi wrote:
no introspection or nothing magic and weird, simply
.call(undefined)
would do for sloppy and strict, preserving global in sloppy, avoiding shenanigans in strict.
You seem to be assuming there is only one global involved again. Did you look at my testcase I posted earlier in this thread? Again, fiddle.jshell.net/tmt5e9m6/2/show.
The behavior that testcase shows is not achievable by doing .call(undefined). If the web depends on that behavior (which is worth verifying if someone wants to experiment!), then we can't blindly do .call(undefined). Even if that is case, we could still do .call(undefined) when the callee is strict, since I'm fairly certain the web does not depend on that behavior for strict callees, but that involves introspecting the strictness of the callee.
So we have three options, as I see it:
-
Keep existing behavior; always pass in a "this" value that's the window that was the "this" of the setTimeout call.
-
Change behavior for strict callees only. This involves introspecting the strictness of the callee, which is clearly doable in implementations, but weird and magical from the ES point of view.
-
Change behavior across the board to passing undefined as the this value, and deal with any resulting compat issues via evangelism.
Hence my curiosity: when this experiment was made, which code with
"use strict"
failed ?
This only matters for option 2 above, right? The compat constraints for option 3 are all around sloppy functions, which is what most people use today. That's why I even brought up option 2: the question you were asking was presupposing that this option should be on the table.
Apologies, now I see what you meant and I think option 2 would be probably ideal. ES5+ engines can easily retrieve "strictness" so while it might seem weird it would surprise less, syntax and explicit intent speaking, and will remove the right to pass a global context to the callback.
Going through the list of all properties it looks like at the end of the day only things to improve/change are:
requestAnimationFrame setInterval setTimeout
Although I have honestly no idea how to explain via W3C pages that JS might be strict or not ... seems to me that "strictness" should be outside the DOM knowledge so .... probably this should be part of ES specification.
However, it seems at this point an overly-complicated tiny little change full of cross-web alignment that might not even have a concrete impact in the real world ... so maybe all this won't be worth to fix.
Thanks all for thoughts, hints, and insights on this matter.
Best
On Mon, Sep 8, 2014 at 7:25 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
Apologies, now I see what you meant and I think option 2 would be probably ideal.
I disagree. I think option #2 is rather horrible. Strictness can't be tested in JS user code, and shouldn't be. And sloppy functions can't be emulated by proxies, and shouldn't be. Whatever we spec as the caller's interaction with the callee, it shouldn't be predicated on "if the callee is strict....". We've taken pains to avoid this everywhere else[*].
The status quo, though we agree on the pain it causes, is less painful than introducing this conditional.
[*] SloppyFunction.caller must not reveal a strict function. This means that calling into a sloppy function can reveal whether the caller is strict -- but only if the sloppy function has the legacy magic caller property and calls a sloppy function.
On 9/8/14, 10:25 AM, Andrea Giammarchi wrote:
Apologies, now I see what you meant and I think option 2 would be probably ideal. ES5+ engines can easily retrieve "strictness"
In script? How? (Again, clearly in the VM implementation I can do this.)
Going through the list of all properties it looks like at the end of the day only things to improve/change are:
requestAnimationFrame setInterval setTimeout
requestAnimationFrame doesn't have this problem. See www.w3.org/TR/animation-timing/#dfn-invoke-callbacks-algorithm
and the text about "callback this value" at heycam.github.io/webidl/#es-invoking-callback-functions: it
defaults to undefined unless specified otherwise, and the requestAnimationFrame spec doesn't specify otherwise. You can see a testcase for this behavior at jsfiddle.net/xpwr1ozx
Now it just happens that Blink, IE and WebKit (or at least Safari) have buggy requestAnimationFrame implementations. Specifically, Blink and IE pass a Window as "this" (I didn't check which Window) and WebKit passes the function itself as "this" as far as I can tell(!). Firefox follows the spec and passes undefined. Please feel free to file bugs on the other implementations!
Although I have honestly no idea how to explain via W3C pages that JS might be strict or not ... seems to me that "strictness" should be outside the DOM knowledge so .... probably this should be part of ES specification.
The normal way this would be done is that ES would expose an abstract operation that returns true or false for strictness of a function and then DOM would invoke that abstract operation. Though obviously the DOM could just directly check the [[Strict]] internal slot too if there is no abstract operation defined for it.
Boris and Mark, I was talking about engines, already inevitably able to distinguish strict from sloppy, but in any case in JS is straight forward to know if you are under strict directive or not.
var isStrictAvailable = (function(){'use strict';return !this}());
var isThisStrict = isStrictAvailable && (function(){return !this}());
Btw, I laughed hard than I've should have on this:
"Now it just happens that Blink, IE and WebKit (or at least Safari) have
buggy requestAnimationFrame implementations."
Best
On Mon, Sep 8, 2014 at 8:35 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
Boris and Mark, I was talking about engines, already inevitably able to distinguish strict from sloppy,
We have made great progress in JS better able to implement/emulate the APIs we expect browsers to provide. One of the design constraints on WebIDL is "can it be implemented in JS, perhaps by a proxy?" Exotic object behavior that cannot be implemented even by proxies is held with great suspicion, though some of this remains.
So I would generally avoid having the engine observably make a test that JS can't do. Again, one of these remains as well: a magical sloppy.caller can't reveal a non-sloppy caller.
but in any case in JS is straight forward to know if you are under strict directive or not.
var isStrictAvailable = (function(){'use strict';return !this}()); var isThisStrict = isStrictAvailable && (function(){return !this}());
That test only one's own lexical scope. It does not test a function value given to you across an API boundary.
Btw, I laughed hard than I've should have on this:
"Now it just happens that Blink, IE and WebKit (or at least Safari) have buggy requestAnimationFrame implementations."
Best
;)
Please do file bugs on these and post the links. Thanks.
On Mon, Sep 8, 2014 at 8:45 AM, Mark S. Miller <erights at google.com> wrote:
On Mon, Sep 8, 2014 at 8:35 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
Boris and Mark, I was talking about engines, already inevitably able to distinguish strict from sloppy,
We have made great progress in JS better able to implement/emulate the APIs we expect browsers to provide. One of the design constraints on WebIDL is "can it be implemented in JS, perhaps by a proxy?" Exotic object behavior that cannot be implemented even by proxies is held with great suspicion, though some of this remains.
So I would generally avoid having the engine observably make a test that JS can't do. Again, one of these remains as well: a magical sloppy.caller can't reveal a non-sloppy caller.
but in any case in JS is straight forward to know if you are under strict directive or not.
var isStrictAvailable = (function(){'use strict';return !this}()); var isThisStrict = isStrictAvailable && (function(){return !this}());
That test only one's own lexical scope. It does not test a function value given to you across an API boundary.
Agreed. Can you say why this is a desirable property? I ask mostly to understand what the consequences are of some side-channel leak which allows a caller to decide the strictness of a particular function (say via toSource fr'instance) that it is calling - are there security consequences?
No, this isn't an information disclosure or any other security issue. It is "only" a modularity issue.
so far I've managed to file Chromium and WebKit only: code.google.com/p/chromium/issues/detail?id=411959, bugs.webkit.org/show_bug.cgi?id=136635
with IE I've had some trouble finding a "file a bug" link ...
Best
Mark Miller wrote:
Yes, this is indeed the only question that Andrea and I are raising in this thread. As you acknowledge, providing window here is a little strange. I quibble with "a little". When a surprise surprises by providing less authority than expected, I don't much care. When the surprise is that more authority is provided than expected, that's a more serious issue.
In 1995-96, this wasn't surprising. Frames and framesets were there, also window.open. Calling otherWindowOrFrame.setTimeout(func, delay, args) worked by passing otherWindowOrFrame bound to |this| when invoking func.
(Er, only in 1996 -- setTimeout originally took a string as first parameter and eval'ed it, no function variant.)
It is what it was :-P.
The surprise may be in thinking of sloppy-mode global functions as |this|-free procedures, when they were rather global object methods. Calling otherWindowOrFrame.func(args) bound |this| as you would expect, and calling func in the context of otherWindowOrFrame bound the same |this| value. Sloppy FTW :-P.
Agreed it is an implicit parameter capability leak. I'm just giving the view as it was in the past, which is enshrined not only in sloppy mode as you note, but in the spec for Window::setTimeout.
Thanks for the background history, however I am still not sold the fact it's a global object method should mean a global context should be passed.
Following, a snippet simulating what would be my expectations
window.myTimer = function (callback, delay) {
// queue the callback as the task in delay milliseconds
// then in that future ...
callback(); // invoke such callback, that's it !
};
I wasn't expecting setTimeout
or setInterval
to be in charge of passing
a context ... this looks to me as wrong as the old ([].sort)()
returning
the global context, completely unexpected being not a matter/responsibility
of that method.
At least most of us agreed that's a potential implicit parameter leak but I am still sceptic the effort to fix it cross platform would be worth saving such leak.
Best
On Mon, Sep 8, 2014 at 3:15 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
Thanks for the background history, however I am still not sold the fact it's a global object method should mean a global context should be passed.
Following, a snippet simulating what would be my expectations
window.myTimer = function (callback, delay) { // queue the callback as the task in delay milliseconds // then in that future ... callback(); // invoke such callback, that's it ! };
I wasn't expecting
setTimeout
orsetInterval
to be in charge of passing a context ... this looks to me as wrong as the old([].sort)()
returning the global context, completely unexpected being not a matter/responsibility of that method.At least most of us agreed that's a potential implicit parameter leak but I am still sceptic the effort to fix it cross platform would be worth saving such leak.
Yeah, it sounds like we've all arrived at the same conclusion: This is unlikely to get fixed, and so will remain a host provided API (browser and node, incompatibly) rather than ever migrating into JS.
Let's just be sure that we avoid this mistake when promises grow something like Q's Q.delay. Promise.delay? Promise.prototype.delay?
Andrea Giammarchi wrote:
Thanks for the background history, however I am still not sold the fact it's a global object method should mean a global context should be passed.
"Is" (or "was" and therefore "is" because "don't break the web") -- not "ought".
You need a way out of Hume's Guillotine on the Web. I don't know of one. Ideals don't cut it without God-mode powers to rewrite unknown amounts of content. Strict mode could eventually pave a cowpath, but as Mark said, we don't want runtime strictness-testing magic in setTimeout or other builtins.
The point here is that otherWin.setTimeout(func, ...) must -- because of "is" not "ought" -- not pass undefined to func in case it is strict mode, because if it's sloppy and if it is scoped by the current window (not otherWin), then the wrong window will be used.
Is there any point in continuing this thread?
On 9/8/14, 6:49 PM, Mark S. Miller wrote:
Let's just be sure that we avoid this mistake when promises grow something like Q's Q.delay. Promise.delay? Promise.prototype.delay?
Yes, absolutely. This is why requestAnimationFrame is specced to pass undefined for "this", implementation bugs notwithstanding.
On 9/8/14, 7:10 PM, Brendan Eich wrote:
The point here is that otherWin.setTimeout(func, ...) must -- because of "is" not "ought" -- not pass undefined to func in case it is strict mode, because if it's sloppy and if it is scoped by the current window (not otherWin), then the wrong window will be used.
Though again, IE9 and before use that wrong window. So it's at least possible that UAs could change to that behavior (change back, in the case of IE).
I'm just not willing to have Gecko go first on this one given our past experiences around this stuff.
Boris Zbarsky wrote:
Though again, IE9 and before use that wrong window. So it's at least possible that UAs could change to that behavior (change back, in the case of IE).
What, my "original intent" argument didn't work? :-P
arstechnica.com/information-technology/2014/09/august-growth-puts-windows-8-back-on-track/#p3
Ok, so IE9 and below form a majority. They are the past.
If we don't end up emails with questions, maybe? :P
Thanks Boris for the improved report bug in webkit.
Best
Sent from my Windows PhoneFrom: Brendan Eich Sent: 9/9/2014 0:10 To: Andrea Giammarchi Cc: Mark Miller; Mark S. Miller; es-discuss list Subject: Re: "use strict" VS setTimeout Andrea Giammarchi wrote:
Thanks for the background history, however I am still not sold the fact it's a global object method should mean a global context should be passed.
"Is" (or "was" and therefore "is" because "don't break the web") -- not "ought".
You need a way out of Hume's Guillotine on the Web. I don't know of one. Ideals don't cut it without God-mode powers to rewrite unknown amounts of content. Strict mode could eventually pave a cowpath, but as Mark said, we don't want runtime strictness-testing magic in setTimeout or other builtins.
The point here is that otherWin.setTimeout(func, ...) must -- because of "is" not "ought" -- not pass undefined to func in case it is strict mode, because if it's sloppy and if it is scoped by the current window (not otherWin), then the wrong window will be used.
Is there any point in continuing this thread?
I know this is probably W3C land but the following code shows the global object in every JS engine I could test:
(function () { 'use strict'; setTimeout(function () { 'use strict'; console.log(this); // [window/global Object] }, 0); }());
This looks like a potential problem when possible passed methods are not bound + it looks inconsistent with "use strict" expectations.
Thoughts ?
Best