From: Richard Cornford on
On Feb 17, 6:05 pm, Jorge wrote:
<snip>
> But you'll admit that this -ES3 behaviour- is not what one
> would -for many reasons- expect:

Generally the handling of - this - in ES3 is found unexpected/
unintuitive by many people.

> var x= 3;
> Number.prototype.test= function () { return this; };
> x.test() === x.test()
> --> false
>
> Yes ?

No. In an environment where the - this - value is always an object,
type-conversion from primitive for property accessors create objects
internally and temporarily during evaluation, and objects are only
equal if they share identity the expected result is the one observed.

And for someone who doesn't understand the language they are writing
having any expectations is unrealistic.

>> And we are not looking at a voluntary opt-in here, this is not
>> 'strict mode' only.
>
> Are you sure ?

Earlier in this thread, didn't Thomas go to a great deal of effort to
show that in ES5 it is possible for - this - values to be numeric
primitives regardless of the mode?

Richard.
From: Jorge on
On Feb 17, 7:45 pm, Richard Cornford <Rich...(a)litotes.demon.co.uk>
wrote:
> On Feb 17, 6:05 pm, Jorge wrote:
> <snip>
>
> > But you'll admit that this -ES3 behaviour- is not what one
> > would -for many reasons- expect:
>
> Generally the handling of - this - in ES3 is found unexpected/
> unintuitive by many people.
>
> > var x= 3;
> > Number.prototype.test= function () { return this; };
> > x.test() === x.test()
> > --> false
>
> > Yes ?
>
> No. In an environment where the - this - value is always an object,
> type-conversion from primitive for property accessors create objects
> internally and temporarily during evaluation, and objects are only
> equal if they share identity the expected result is the one observed.

It's totally unexpected because the left hand x is exactly the same x
as the right hand x. It's only expected behaviour after 1st scratching
your head for a -presumably long- while and 2nd having read the
related chapter of the ES3 spec. But -I guess- this won't happen
anymore in ES5: the wrapper object will be used just to lookup the
property in the appropriate .proto chain, but won't be passed on to
"this".
--
Jorge.
From: Richard Cornford on
On Feb 17, 7:00 pm, Jorge wrote:
> On Feb 17, 7:45 pm, Richard Cornford wrote:
>> On Feb 17, 6:05 pm, Jorge wrote:
>> <snip>
>
>>> But you'll admit that this -ES3 behaviour- is not what one
>>> would -for many reasons- expect:
>
>> Generally the handling of - this - in ES3 is found unexpected/
>> unintuitive by many people.
>
>>> var x= 3;
>>> Number.prototype.test= function () { return this; };
>>> x.test() === x.test()
>>> --> false
>
>>> Yes ?
>
>> No. In an environment where the - this - value is always an
>> object, type-conversion from primitive for property accessors
>> create objects internally and temporarily during evaluation,
>> and objects are only equal if they share identity the expected
>> result is the one observed.
>
> It's totally unexpected because the left hand x is exactly the
> same x as the right hand x.

Only in as far as:-

var x = 3;
x.something = 'something';

alert(x.something); //outputs undefined.

- is unexpected because you write to and then read from the same x.
Changing the way that the - this - value works does not 'solve'
implicit type-conversion confusions.

> It's only expected behaviour after 1st scratching
> your head for a -presumably long- while

That sounds like a waste.

> and 2nd having read the
> related chapter of the ES3 spec.

Any decent book on the subject should have made the point. It is a
pity that there are so few decent books on the subject but the good
ones should explain the aspects of the language that people find
difficult to understand.

> But -I guess- this won't happen anymore in ES5: the wrapper
> object will be used just to lookup the property in the
> appropriate .proto chain, but won't be passed on to "this".

Hence the incompatibility with ES3.

Richard.
From: kangax on
On 2/17/10 5:52 AM, Thomas 'PointedEars' Lahn wrote:
> kangax wrote:
>
>> Thomas 'PointedEars' Lahn wrote:
>>> Cody Haines wrote:
>>>> Jorge wrote:
>>>>> Do you think -as I do- that the Math object is an ugly artifact ?
>>>>> Well, here's a nice way of getting rid of it :
>>>>>
>>>>> Number.prototype.sin= function () { return Math.sin(+this); };
>>>>> [...]
>>>>> (1.234).sin().asin()
>>>>> [...]
>>>>
>>>> Just out of curiosity, does IE support this? I don't see why it
>>>> wouldn't, just making sure
>>>
>>> [...]
>>> In fact, given that, according to Editions 3 and 5 of the ECMAScript
>>> Language Specification, when evaluating the CallExpression's
>>> /MemberExpression/ any primitive value would be converted into an
>>> appropriate value of type Object (ES3/5, 11.2.3, 11.2.1, and 9.9), this
>>> SHOULD work everywhere where `Number.prototype' is supported (which
>>> AFAIK excludes only JavaScript 1.0 as of Netscape 2.x, and JScript 1.0
>>> as of IE 3.0 and IIS 3.0 ).¹ [...]
>>> ___________
>>> ¹ To be sure of that, I have debugged the algorithms of ECMAScript Ed.
>>> 5,
>>> and compared with Ed. 3; I can post more detailed results of my
>>> research if anyone is interested.
>>
>> I'm interested.
>
> In the following, I will use `Number.prototype.sin' as defined above and
> `(1.234).sin()' for the example.
>
> The basic productions that apply to the expression
>
> (1.234).sin()
>
> in both Editions are:
>
> CallExpression :
> MemberExpression Arguments
>
> MemberExpression :
> PrimaryExpression
> MemberExpression . Identifier
>
> PrimaryExpression :
> Literal
> ( Expression )
>
> Expression :
> AssignmentExpression
>
> ...
>
> LeftHandSideExpression :
> NewExpression
>
> NewExpression:
> MemberExpression
>
> Literal ::
> NumericLiteral
>
> NumericLiteral ::
> DecimalLiteral
>
> DecimalLiteral ::
> DecimalIntegerLiteral . DecimalDigits_opt ExponentPart_opt
>
> Arguments :
> ()
>
> As a result, the following unifications take place:
>
> In the algorithm for the production
> CallExpression : MemberExpression Arguments
> (section 11.2.3):
>
> CallExpression := (1.234).sin()
> MemberExpression := (1.234).sin
> Arguments := ()
>
> And the following steps:
>
> 1. Evaluate MemberExpression.
>
> In the algorithm for
> MemberExpression : MemberExpression [ Expression ]
> (section 11.2.1):
>
> MemberExpression := (1.234)
> Expression := "sin"
>
> Steps per ES3F:
>
> 1. Evaluate MemberExpression.
> --> Result(1) := 1.234
> 2. Call GetValue(Result(1)).
> --> Result(2) := 1.234
> 3. Evaluate Expression.
> --> Result(3) := "sin"
> 4. Call GetValue(Result(1)).
> --> Result(4) := "sin"
> 5. Call ToObject(Result(2)).
> ^^^^^^^^^^^^^^^^^^^^^^^^^
>
> (9.9) ToObject
>
> The operator ToObject converts its argument to a value
> of type Object according to the following table:
>
> [...]
> Number Create a new Number object whose [[value]] property is
> set to the value of the number. See section 15.7 for a
> description of Number objects.
> [...]
>
> --> Result(5) := new Number(1.234)
>
> 6. Call ToString(Result(4)).
> --> Result(6) := "sin"
> 7. Return Reference(base=Result(5), name=Result(6)):
> --> Return Reference(base=new Number(1.234), name="sin").
>
> It is thus quite obvious already that the primitive Number value is being
> converted into an equivalent Number object that has the current value of
> `Number.prototype' next in its prototype chain, and we can skip analyzing
> the subsequent steps for ES3F in detail. JFTR, they are:
>
> 2. Evaluate /Arguments/, producing an internal list of argument values
> (section 11.2.4).
> 3. Call GetValue(Result(1)).
> 4. If Type(Result(3)) is not Object, throw a *TypeError* exception.
> 5. If Result(3) does not implement the internal [[Call]] method, throw
> a TypeError exception.
> 6. If Type(Result(1)) is Reference, Result(6) is GetBase(Result(1)).
> Otherwise, Result(6) is *null*.
> 7. If Result(6) is an activation object, Result(7) is *null*.
> Otherwise, Result(7) is the same as Result(6).
> 8. Call the [[Call]] method on Result(3), providing Result(7) as the
> *this* value and providing the list Result(2) as the argument values.
> 9. Return Result(8).
>
> However, the conversion of the primitive value to an object for the purpose
> of property lookup is a lot harder to see with the ES5 algorithms, which is
> why I am going deeply into detail there:

[snip explanation of ES5 algorithm]

> What I found most interesting here is that the primitive value is converted
> to an object solely for the purpose of property lookup; and that the
> primitive value, _not_ a reference to that object becomes the `this' value
> of the called method. (IOW, if the `this' value of such a method is used
> repeatedly with property accessors, it could be more efficient to convert
> it to an object once explicitly, and then use the object reference
> instead.)

Interesting indeed. Thanks for looking into this.

>
> I have also learned in the process that the more complex algorithms of ES5
> do have a purpose beyond simply much a greater abstraction level; for
> example, they support the implementation of mutable property attributes
> like [[Enumerable]] -- which finally would allow such nice features like
>
> var o = {foo: "bar"};
>
> /* shows "foo" */
> for (var p in o) console.log(p);
>
> Object.defineProperty(o, "bar", {enumerable: false});
>
> /* shows nothing */
> for (p in o) console.log(p);
>
> (if they were implemented; not so in JavaScript 1.8.1 as of
> Firefox/Iceweasel 3.5.6 or any other widely distributed implementation¹) --

FWIW, some of ES5 bits are already implemented in, for example, nightly
WebKit (and Opera 10.50 alpha, IIRC).

Running your example (slightly modified) in WebKit:

var obj = Object.defineProperty({ }, 'foo', {
value: 'bar',
enumerable: false
});

Object.keys(obj); // []

Object.defineProperty(obj, 'foo', {enumerable:true});

Object.keys(obj); // ['foo']

(`Object.keys`, in a nutshell, returns an array of strings corresponding
to _own_ _enumerable_ properties of an object; see 15.2.3.14)

And other fields of property descriptor:

Object.defineProperty(obj, 'foo', {configurable:false});
delete obj.foo; // false (as expected)
object.foo; // 'bar'

[...]

> ___________
> ¹<http://www.robertnyman.com/javascript/javascript-getters-setters.html>

First example under `Object.defineProperty` on that page seems to be a
duplicate of second one. It claims to test objects (not DOM elements)
but procedes with testing `Object.defineProperty` on `document.body`.

--
kangax
From: Dmitry A. Soshnikov on
On Feb 17, 10:58 pm, kangax <kan...(a)gmail.com> wrote:

[...]

>
> some of ES5 bits are already implemented in, for example, nightly
> WebKit (and Opera 10.50 alpha, IIRC).
>

Also if interested, almost all features of ES5 (except of "strict
mode") are implemented in Rhino 1_7R3pre: <URL: ftp://ftp.mozilla.org/pub/mozilla.org/js/>.
I use it for test - as in sources there's a runnable js-console.

/ds