From: Thomas 'PointedEars' Lahn on
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:

(11.2.3) Function Calls

The production /CallExpression/ : /MemberExpression/ /Arguments/
is evaluated as follows:

1. Let /ref/ be the result of evaluating /MemberExpression/.

--> ref := Evaluate( (1.234).sin )

(11.2.1) Property Accessors

[...]
The production
/MemberExpression/ : /MemberExpression/ [ /Expression/ ]"
(equivalence of dot and bracket property accessor notation for
Identifiers; the ed.) is evaluated as follows:

MemberExpression := (1.234)
Expression := "sin"

1. Let /baseReference/ be the result of evaluating
/MemberExpression/.
--> baseReference := 1.234

2. Let /baseValue/ be GetValue(/baseReference/).
--> baseValue := GetValue(baseReference)
--> baseValue := 1.234

3. Let /propertyNameReference/ be the result of evaluating
/Expression/.
--> propertyNameReference := "sin"

4. Let /propertyNameValue/ be GetValue(/propertyNameReference/).
--> propertyNameValue := "sin"

5. Call CheckObjectCoercible(/baseValue/).
--> CheckObjectCoercible(1.234) exits normally

6. Let /propertyNameString/ be ToString(/propertyNameValue/).
--> propertyNameString := "sin"

7. If the syntactic production that is being evaluated is contained
in strict mode code, let /strict/ be *true*, else let /strict/ be
*false*.

We assume no strict mode here (it does not really matter for this
algorithm anyway) --> strict := false

8. Return a value of type Reference whose base value is /baseValue/
and whose referenced name is /propertyNameString/, and whose
strict mode flag is /strict/.
--> return Reference(base=1.234, name="sin", strict=false)

--> ref := Reference(base=1.234, name="sin", strict=false)

(11.2.3)
2. Let /func/ be GetValue(/ref/).
--> func := GetValue(ref)

(8.7.1) GetValue(V)

V := Reference(base=1.234, name="sin", strict=false)

1. If Type(/V/) is not Reference, return /V/.
--> Type(V) is Reference, continue.

2. Let /base/ be the result of calling GetBase(/V/).
--> base := GetBase(V)
--> base := 1.234 (section 8.7)

3. If IsUnresolvableReference(/V/), throw a *ReferenceError*
exception.

V := Reference(base=1.234, name="sin", strict=false)

(8.7)
IsUnresolvableReference(V) := (GetBase(V) == undefined)
--> IsUnresolvableReference(V) == false

--> Continue.

4. If IsPropertyReference(/V/), then

V := Reference(base=1.234, name="sin", strict=false)

(8.7)
IsPropertyReference(V) :=
Type(V) == Object || HasPrimitiveBase(V)

--> Type(V) == Number

HasPrimitiveBase(V) := Type(GetBase(V)) == Boolean
|| Type(GetBase(V)) == String
|| Type(GetBase(V)) == Number
--> HasPrimitiveBase(V) == true

--> IsPropertyReference(V) == true

a. If HasPrimitiveBase(V) is false, then let /get/ be the
[[Get]] internal method of /base/, otherwise let /get/
be the special [[Get]] internal method defined below.
--> HasPrimitiveBase(V) == true
--> get := special [[Get]]

b. Return the result of calling the /get/ internal method
using /base/ as its *this* value, and passing
GetReferencedName(/V/) for the argument.

--> return get(this=1.234, "sin")
--> return special [[Get]](this=1.234, "sin")

1. Let O be ToObject(base).
^^^^^^^^^^^^^^^^^^^^^^^^
--> O := ToObject(1.234)
--> O := new Number(1.234)

2. Let /desc/ be the result of calling the
[[GetProperty]] internal method of /O/
with property name /P/.

desc := (new Number(1.234)).[[GetProperty]]("sin")

(8.12.2) [[GetProperty]] (P)

When the [[GetProperty]] internal method of /O/
is called with property name /P/, the following
steps are taken:

O := new Number(1.234)
P := "sin"

1. Let /prop/ be the result of calling the
[[GetOwnProperty]] internal method of /O/
with property name /P/.

prop :=
(new Number(1.234))[[GetOwnProperty]]("sin")

(8.12.1) [[GetOwnProperty]] (P)

When the [[GetOwnProperty]] internal method of
/O/ is called with property name /P/, the
following steps are taken:

1. If /O/ doesn't have an own property with
name /P/, return *undefined*.

HasOwnProperty(new Number(1.234), "sin")
== false
--> return undefined

--> prop := undefined

2. If /prop/ is not *undefined*, return /prop/.
--> prop == undefined
--> Continue.

3. Let /proto/ be the value of the [[Prototype]]
internal property of /O/.
--> proto := (new Number(1.234))[[Prototype]]
--> proto := Number.prototype

4. If /proto/ is *null*, return *undefined*.
--> proto != null
--> Continue.

5. Return the result of calling the
[[GetProperty]] internal method of /proto/
with argument /P/.
--> return (
Number.prototype.[[GetProperty]]("sin"))

1. prop :=
Number.prototype[[GetOwnProperty]]("sin")

1. HasOwnProperty(Number.prototype, "sin")
== true, continue.
2. D := PropertyDescriptor()
3. X := Property(Number.prototype, "sin")
4. X is a data property (section 8.6), so
a. D.[[Value]] := X.[[Value]]
b. D.[[Writeable]] := X.[[Writeable]]
5. Else ... (skipped accordingly)
6. D.[[Enumerable]] := X.[[Enumerable]]
7. D.[[Configurable]] := X.[[Configurable]]
8. Return D.

--> return PropertyDescriptor(
Number.prototype, "sin", attribs)

--> desc := PropertyDescriptor(
Number.prototype, "sin", attribs)

3. If /desc/ is undefined, return *undefined*.
--> PropertyDescriptor(...) != undefined, continue.

4. If IsDataDescriptor(/desc/) is *true*, return
desc.[[Value]].

--> IsDataDescriptor(PropertyDescriptor(...))
== true:

(8.10.2)
1. PropertyDescriptor(Number.prototype, "sin",
attribs) != undefined, continue.
2. PropertyDescriptor(Number.prototype, "sin",
attribs).[[Value]] present, continue.
3. Return true.

--> Return PropertyDescriptor(...).[[Value]],
--> Return Number.prototype.sin.

--> return Number.prototype.sin

--> func := Number.prototype.sin

(11.2.3)
3. Let /argList/ be the result of evaluating /Arguments/, producing an
internal list of argument values (see 11.2.4).
--> argList := List()

4. If Type(/func/) is not Object, throw a *TypeError* exception.
--> Type(Number.prototype.sin) == Object, continue.

5. If IsCallable(/func/) is *false*, throw a *TypeError* exception.
--> IsCallable(Number.prototype.sin) == true, continue.

6. If Type(/ref/) is Reference, then
--> Type(Reference(base=1.234, name="sin", strict=false))
== Reference, so

a. If IsPropertyReference(/ref/) is true, then
--> IsPropertyReference(
Reference(base=1.234, name="sin", strict=false))
== true, so

i. Let thisValue be GetBase(/ref/).
--> thisValue := GetBase(ref)
--> thisValue := 1.234

b. Else ... (skipped accordingly)

7. Else ... (skipped accordingly)

8. Return the result of calling the [[Call]] internal method on /func/,
providing /thisValue/ as the *this* value and providing the list
/argList/ as the argument values.

--> return (Number.prototype.sin.[[Call]](
this=1.234, arguments=List())

(13.2.1) [[Call]]

When the [[Call]] internal method for a Function object /F/ is
called with a *this* value and a list of arguments, the
following steps are taken:

1. Let /funcCtx/ be the result of establishing a new execution
context for function code using the value of /F/'s
[[FormalParameters]] internal property, the passed arguments
List /args/, and the *this* value as described in 10.4.3.

--> funcCtx := ExecutionContext(
formalParameters=
Number.prototype.sin.[[FormalParameters]],
arguments=List(),
this=1.234)

2. Let result be the result of evaluating the /FunctionBody/
that is the value of /F/'s [[Code]] internal property. If
/F/ does not have a [[Code]] internal property or if its
value is an empty FunctionBody, then result is (normal,
undefined, empty).

--> result := Evaluate(Number.prototype.sin.[[Code]])

3. Exit the execution context /funcCtx/, restoring the previous
execution context.
--> Exit(funcCtx), ...

4. If /result/.type is |throw| then throw /result/.value.
--> result.type == normal, continue.

5. If /result/.type is |return| then return /result/.value.
--> result.type == return
--> return /result/.value

--> return the equivalent of
Number.prototype.sin.apply(1.234, argList)

--> return Math.sin(+1.234)
--> return Math.sin(1.234)


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.)

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¹) --
and facilitate making the difference between data properties and accessor
properties, i.e. normal properties on the one hand, and properties with
getters/setters on the other hand (already implemented there and
elsewhere¹):

var o = {
_bar: "",
get foo() { return +this._bar; },
set foo(x) { this._bar = String(+x + 19); }
};

o.foo = "23";

/* shows 42 as a number */
console.log(o.foo);


PointedEars
___________
¹ <http://www.robertnyman.com/javascript/javascript-getters-setters.html>
--
Anyone who slaps a 'this page is best viewed with Browser X' label on
a Web page appears to be yearning for the bad old days, before the Web,
when you had very little chance of reading a document written on another
computer, another word processor, or another network. -- Tim Berners-Lee
From: Thomas 'PointedEars' Lahn on
Richard Cornford wrote:

> In ES3 the - this - keyword always refers to an object, so you are
> guaranteed that it is never a primitive value. [...]

ISTM that I have just proved this to be a common misconception.


PointedEars
--
var bugRiddenCrashPronePieceOfJunk = (
navigator.userAgent.indexOf('MSIE 5') != -1
&& navigator.userAgent.indexOf('Mac') != -1
) // Plone, register_function.js:16
From: Jorge on
On Feb 17, 11:54 am, Thomas 'PointedEars' Lahn <PointedE...(a)web.de>
wrote:
> Richard Cornford wrote:
> > In ES3 the - this - keyword always refers to an object, so you are
> > guaranteed that it is never a primitive value. [...]
>
> ISTM that I have just proved this to be a common misconception.

He said *In*ES3*:

Number.prototype.test= function () { return typeof this; };
(1).test()
--> "object"
--
Jorge.
From: Thomas 'PointedEars' Lahn on
Thomas 'PointedEars' Lahn wrote:

> Richard Cornford wrote:
>> In ES3 the - this - keyword always refers to an object, so you are
>> guaranteed that it is never a primitive value. [...]
>
> ISTM that I have just proved this to be a common misconception.

Please ignore this. In fact, my research¹ proves that you are correct for
ES3(F), where the Reference value is computed using the conversion result as
base; not so in ES5 (I wonder what the rationale was).


PointedEars
___________
¹ <news:1586094.GhzyNJHpIh(a)PointedEars.de>
--
Use any version of Microsoft Frontpage to create your site.
(This won't prevent people from viewing your source, but no one
will want to steal it.)
-- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)
From: Stefan Weiss on
On 17/02/10 11:52, Thomas 'PointedEars' Lahn wrote:

[snip impressive human-script-engine-mode text]

> 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);

Shouldn't this still show "foo"?

--
stefan