<div dir="ltr">On Thu, Sep 11, 2008 at 11:11 AM, Brendan Eich <span dir="ltr">&lt;<a href="mailto:brendan@mozilla.org">brendan@mozilla.org</a>&gt;</span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<div style=""><div><div class="Ih2E3d"><div>On Sep 9, 2008, at 11:11 AM, Mark S. Miller wrote:</div><blockquote type="cite"><div style="margin: 0px;">On Tue, Sep 9, 2008 at 9:21 AM, Mark S. Miller &lt;<a href="mailto:erights@google.com" target="_blank">erights@google.com</a>&gt; wrote:</div>
 <blockquote type="cite"><div style="margin: 0px;">What should be the rules for coercing non-object &#39;this&#39; values?</div> </blockquote><div style="margin: 0px; min-height: 14px;"><br></div><div style="margin: 0px;">
In a previous thread we&#39;ve already settled that ES3.1-strict will not</div><div style="margin: 0px;">coerce null or undefined &#39;this&#39; values. In order to do this, we&#39;re</div><div style="margin: 0px;">going to migrate the spec language for coercing a null-or-undefined</div>
<div style="margin: 0px;">&#39;this&#39; from the caller-side that scattered all over the spec (where</div><div style="margin: 0px;">it&#39;s hard to make it conditional on strictness), to the callee side in</div><div style="margin: 0px;">
section <a href="http://11.1.1." target="_blank">11.1.1.</a></div></blockquote><div><br></div></div>Great, this improves the spec.</div><div><div class="Ih2E3d"></div></div></div></blockquote><div><br>Good. We seem to have no disagreement on this point.<br>
<br>&nbsp;</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div style=""><div><div class="Ih2E3d"><br><blockquote type="cite"><div style="margin: 0px;">
 For non-strict code, this should make no observable</div><div style="margin: 0px;">difference.</div><div style="margin: 0px; min-height: 14px;"><br></div><div style="margin: 0px;">Some open questions:</div><div style="margin: 0px; min-height: 14px;">
<br></div><div style="margin: 0px;">The ES3 spec language uses null as the implicit &#39;this&#39; value</div><div style="margin: 0px;">associated with calling a function as a function. However, since the</div><div style="margin: 0px;">
current spec language also coerces both null and undefined to the</div><div style="margin: 0px;">global object, it is unobservable whether null or undefined is used as</div><div style="margin: 0px;">the implicit &#39;this&#39; value. In ES3.1 strict this difference becomes</div>
<div style="margin: 0px;">observable. In the interests of explaining &#39;this&#39;-binding as being</div><div style="margin: 0px;">more like parameter-binding, I would like to see this changed to</div><div style="margin: 0px;">
undefined. Calling a function as a function is like calling it without</div><div style="margin: 0px;">an argument for its &#39;this&#39; parameter. I think this is more intuitive.</div></blockquote><div><br></div></div>This is strictly more useful than the current mess, and David-Sarah made good use of it in his Date self-hosting for the Date constructor function, so it can tell when it&#39;s called as a function. But it&#39;s still not enough in general:</div>
<div><br></div><div>var funnyObj = {method: Date}</div><div>funnyObj.method(2008,9,11)</div><div><br></div><div>would bind funnyObj to |this| in Date, which would flow into the constructor-case code as the new Date object to initialize.</div>
</div></blockquote><div><br>Agreed that thsi proposal does not prevent this existing confusion. Neither does it make it worse. If there&#39;s something we can plausibly do to fix this, I&#39;d love to! Any suggestions?<br>
<br>&nbsp;</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div style=""><div></div><div>This is also badly incompatible. Global functions are used as methods of their window objects. Calling them via this.func() or otherFrame.func() works, but so do calls to func() from the same window.</div>
</div></blockquote><div><br>Since there&#39;s only one window per JS global object (obviously, since they&#39;re the same object), all the methods of the window could already be bound to their window, and so not care what their &#39;this&#39; is. However, that would break code such as &#39;window1.func.call(window2)&#39;. Currently, I expect func will operate on window2; but if func is already bound to window1 it will still operate on window1. Is this change of behavior a problem?<br>
&nbsp;</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div style=""><div></div><div class="Ih2E3d"><br><blockquote type="cite"><div style="margin: 0px; min-height: 14px;">
<span>When a primitive non-object type (number, boolean, string, presumably</span></div><div style="margin: 0px;">decimal) is passed as a &#39;this&#39; value, the ES3 spec coerces it to a new</div><div style="margin: 0px;">
corresponding wrapper object each time. This fresh allocation is</div><div style="margin: 0px;">observable and therefore expensive.</div></blockquote><div><br></div></div><div>This has been covered in past threads here, and in ES4 work. The relevant quote from</div>
<div><br></div><div><a href="http://www.ecmascript.org/es4/spec/incompatibilities.pdf" target="_blank">http://www.ecmascript.org/es4/spec/incompatibilities.pdf</a></div><div><br></div><div>is this:</div><div><br></div><div>
<div style="margin: 0px;"><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">The wrapping of primitives on calls to methods that do not have bound-</font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">this </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">types might at first glance</font></div>
<div style="margin: 0px;"><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">appear to reintroduce the very problem we were trying to avoid, but this is not so. The predefined </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">String</font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">,</font></div>
<div style="margin: 0px;"><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">Number</font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">, and </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">Boolean </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">prototype methods in ES4 all have bound-</font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">this </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">types (the types are </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">Object</font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">,</font></div>
<div style="margin: 0px;"><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">AnyNumber</font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">, and </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">AnyBoolean</font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">, respectively), so no wrapping occurs on calls to predefined methods  this</font></div>
<div style="margin: 0px;"><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">optimization is also performed in some existing ES3 implementations. Furthermore, wrapping can be</font></div>
<div style="margin: 0px;"><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">performed lazily; it does not have to occur until a method references its </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 9px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="1" face="Helvetica">this </font><font style="font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal;" size="2" face="Helvetica">value.</font></div>
</div><div><br></div><div>This is what some implementations in fact do. For example, SpiderMonkey passes primitive |this| to native functions that announce they&#39;re ready for this (relatively recent, in the long history of the API) change to the type of |this|, and we have a bug open to do likewise for scripted functions, with lazy wrapping as needed.</div>
<div><br></div><div><div class="Ih2E3d"><br><blockquote type="cite"><div style="margin: 0px;"><span>IIRC, the ES4 spec avoids requiring</span></div><div style="margin: 0px;">this allocation, as that was felt to be an incompatibility everyone</div>
<div style="margin: 0px;">could live with.</div></blockquote><div><br></div></div>No, see above. ES4 (and real implementations that use primitive types as ES4 proposed here) does not break compatibility here. The &quot;built-in&quot; functions that are methods of the primitives work because string.prototype === String.prototype, e.g. -- no string wrapped in new String required for |this| when calling such prototype-delegated methods. Scripted functions added to String.prototype do require wrapping, but as the doc notes it can be done lazily.</div>
<div><br></div><div>The trac ticket</div><div><br></div><div><a href="http://bugs.ecmascript.org/ticket/281" target="_blank">http://bugs.ecmascript.org/ticket/281</a></div><div><br></div><div>notes that lazy wrapping must preserve identity.</div>
<div><br></div><div><br></div><div><div class="Ih2E3d"><blockquote type="cite"><div style="margin: 0px;"> I agree. I propose that primitive &#39;this&#39; values not</div><div style="margin: 0px;">be coerced at all.</div>
</blockquote><div><br></div></div>This is a pretty big incompatibility. I&#39;ll scan some Ajax libraries to look for obvious breaks. I suspect they&#39;ll be easy to find.</div></div></blockquote><div><br>Any results?<br>
<br>If the answer is that we can&#39;t avoid wrapping unconditionally, then I propose that we wrap only on entry to non-strict functions, where this wrapping is again cetralized in the spec to section <a href="http://11.1.1.">11.1.1.</a> Elsewhere you&#39;ve written &quot;more carrots, less sticks&quot;, with which I heartily agree. If &quot;strict&quot; suppresses non-stratified reflection that prevents many useful optimizations (with, delete &lt;var&gt;, arguments.caller, Function.caller, Function.arguments) and removes pointless and costly wrapping, then the common wisdom may become &quot;Make your program work under strict mode so it&#39;ll be faster.&quot;<br>
<br>&nbsp;</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div style=""><div></div><div class="Ih2E3d"><br><blockquote type="cite"><div style="margin: 0px; min-height: 14px;">
<span>If primitive &#39;this&#39; values are no longer coerced, we can still explain</span></div><div style="margin: 0px;">the semantics of property lookup of an expression like &#39;foo.capture()&#39;</div><div style="margin: 0px;">
by saying that the property is looked up in the wrapper&#39;s prototype.</div></blockquote><div><br></div></div><div>ES4 did this by uniting string.prototype and String.prototype, etc.</div><div class="Ih2E3d"><div><br></div>
<div><br><blockquote type="cite"><div style="margin: 0px;">Or we could say that the property is looked up after wrapping, but a</div><div style="margin: 0px;">method call proceeds with the original unwrapped value. In other words</div>
<div style="margin: 0px; min-height: 14px;"><br></div><div style="margin: 0px;"><span>&nbsp; &nbsp; </span>&quot;foo&quot;.capture()</div><div style="margin: 0px; min-height: 14px;"><br></div><div style="margin: 0px;">should be equivalent to</div>
<div style="margin: 0px; min-height: 14px;"><br></div><div style="margin: 0px;"><span>&nbsp; &nbsp; </span>Object(&quot;foo&quot;).capture.call(&quot;foo&quot;)</div><div style="margin: 0px; min-height: 14px;"><br></div><div style="margin: 0px;">
given the original bindings for Object and Function.call.</div></blockquote><br></div></div><div>This much is compatible, provided the right wrapper is used (the one from the current execution context&#39;s scope chain&#39;s final object), since there is no string constructor or string.prototype.</div>
</div></blockquote><div><br>Yes. After ES3.1, I&#39;d like to shift to your way of explaining it, in terms of all values being objects. If we do this right, this shift should only be a better way of explaining the same behavior. <br>
</div></div><br clear="all"><br>-- <br> &nbsp; &nbsp;Cheers,<br> &nbsp; &nbsp;--MarkM<br>
</div>