On Sep 9, 2008, at 11:11 AM, Mark S. Miller wrote:
On Tue, Sep 9, 2008 at 9:21 AM, Mark S. Miller <[EMAIL PROTECTED]>
wrote:
What should be the rules for coercing non-object 'this' values?
In a previous thread we've already settled that ES3.1-strict will not
coerce null or undefined 'this' values. In order to do this, we're
going to migrate the spec language for coercing a null-or-undefined
'this' from the caller-side that scattered all over the spec (where
it's hard to make it conditional on strictness), to the callee side in
section 11.1.1.
Great, this improves the spec.
For non-strict code, this should make no observable
difference.
Some open questions:
The ES3 spec language uses null as the implicit 'this' value
associated with calling a function as a function. However, since the
current spec language also coerces both null and undefined to the
global object, it is unobservable whether null or undefined is used as
the implicit 'this' value. In ES3.1 strict this difference becomes
observable. In the interests of explaining 'this'-binding as being
more like parameter-binding, I would like to see this changed to
undefined. Calling a function as a function is like calling it without
an argument for its 'this' parameter. I think this is more intuitive.
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's called as a function. But it's
still not enough in general:
var funnyObj = {method: Date}
funnyObj.method(2008,9,11)
would bind funnyObj to |this| in Date, which would flow into the
constructor-case code as the new Date object to initialize.
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.
When a primitive non-object type (number, boolean, string, presumably
decimal) is passed as a 'this' value, the ES3 spec coerces it to a new
corresponding wrapper object each time. This fresh allocation is
observable and therefore expensive.
This has been covered in past threads here, and in ES4 work. The
relevant quote from
http://www.ecmascript.org/es4/spec/incompatibilities.pdf
is this:
The wrapping of primitives on calls to methods that do not have bound-
this types might at first glance
appear to reintroduce the very problem we were trying to avoid, but
this is not so. The predefined String,
Number, and Boolean prototype methods in ES4 all have bound-this
types (the types are Object,
AnyNumber, and AnyBoolean, respectively), so no wrapping occurs on
calls to predefined methods – this
optimization is also performed in some existing ES3 implementations.
Furthermore, wrapping can be
performed lazily; it does not have to occur until a method references
its this value.
This is what some implementations in fact do. For example,
SpiderMonkey passes primitive |this| to native functions that
announce they'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.
IIRC, the ES4 spec avoids requiring
this allocation, as that was felt to be an incompatibility everyone
could live with.
No, see above. ES4 (and real implementations that use primitive types
as ES4 proposed here) does not break compatibility here. The "built-
in" 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.
The trac ticket
http://bugs.ecmascript.org/ticket/281
notes that lazy wrapping must preserve identity.
I agree. I propose that primitive 'this' values not
be coerced at all.
This is a pretty big incompatibility. I'll scan some Ajax libraries
to look for obvious breaks. I suspect they'll be easy to find.
If primitive 'this' values are no longer coerced, we can still explain
the semantics of property lookup of an expression like 'foo.capture()'
by saying that the property is looked up in the wrapper's prototype.
ES4 did this by uniting string.prototype and String.prototype, etc.
Or we could say that the property is looked up after wrapping, but a
method call proceeds with the original unwrapped value. In other words
"foo".capture()
should be equivalent to
Object("foo").capture.call("foo")
given the original bindings for Object and Function.call.
This much is compatible, provided the right wrapper is used (the one
from the current execution context's scope chain's final object),
since there is no string constructor or string.prototype.
/be
_______________________________________________
Es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss