From: williamc on
On 5/8/2010 2:08 PM, Garrett Smith wrote:
> williamc wrote:
>> On 5/7/2010 5:04 PM, Dr J R Stockton wrote:
>>> The expression typeof eval("(function F(){return 3})") gives
>>> "function" in Firefox and Chrome (and probably in Opera and Safari); but
>>> in IE8 it gives "undefined".
>>>
>>> What do I need to do to get the same in IE8 as in the others, i.e. for
>>> eval to return an actual executable function, which would return 3 ?
>>>
>>
>> Question: why does 'function F(){return 3}' need the parentheses around
>> it to eval to 'function' in the non-IE implementations?
>
> It doesn't. In fact, eval('function F(){return 3}') works as specified
> in Spidermonkey and in Webkit's JS engine. The different behavior you
> are seeing in IE is caused by JScript interpreting an expression and
> returning that value.
>
> A FunctionDeclaration is not an Expression; it cannot return a value.

Understood...

> Understanding the behavior you may be seeing requires an understanding
> of how the language is designed to be interpreted and how JScript
> deviates from that.
>
> When the (one) argument to eval is a string value, it is parsed as a
> Program. In ES3, eval uses the calling context's scope. What that means
> is that when eval is called, it can access and can create variables
> using the scope chain from where it was called. An example of that.
>
> var a = 9;
> function testEval(){
> var a;
> // When eval is called, calling context's scope is used.
> eval('a = 11');
> return a;
> }
> alert(testEval(), a)
>
> When `testEval` is called, a VO is created and `a` added to it with
> value `undefined`. When the `eval` function is called, that VO is used
> and so expression 'a = 11' resolves `a` on that VO.
>
> The string starting with "function F(){return 3}" could be parsed only
> as a FunctionDeclaration. This is because an ExpressionStatement cannot
> begin with "function ".
>
> When a FunctionDeclaration is parsed, as is the case with your example
> code, it is added as a property to the VO (Variable Object).
>
> and so the code:
>
> eval("function F(){return 3}");
>
> - should create a property of the calling context's scope.
>
> And indeed it does:
>
> eval("function F(){return 3}");
> F();

Ok. So if I'm understanding correctly, running the eval as above is very
similar to using a function expression like...

F = function() { return 3 } ;
alert(F()); // 3

but I'm still not entirely clear about the effect of the parentheses.
If I have code like the following...

c(); // 'c' - c is available *here* as it was created
// with a function declaration...

try {
d();
} catch (ex) {
alert(ex.name); // 'ReferenceError' - d is not available here...
}

var foo = typeof eval("function d() { alert('d'); }"); // inside eval
var bar = typeof eval("(function e() { alert('e'); })");
// as in the original example, with parends

alert(foo); // 'undefined
alert(bar); // 'function'

c(); // 'c'
d(); // 'd' - function d is now available...

// the eval seemed to act basically the same as a function expression
// would have. Overwrite d with an identical function

d = function() { alert('d'); };
d(); // same result

// but function e is not available...

try {
e();
} catch (ex) {
alert(ex.name); // 'ReferenceError'
}

function c() { alert('c'); } // vanilla function declaration

From: Stefan Weiss on
On 09/05/10 01:06, williamc wrote:
> var foo = typeof eval("function d() { alert('d'); }"); // inside eval
> var bar = typeof eval("(function e() { alert('e'); })");
> // as in the original example, with parends
>
> alert(foo); // 'undefined
> alert(bar); // 'function'
>
> c(); // 'c'
> d(); // 'd' - function d is now available...
....
> // but function e is not available...

The identifier of a named function expression ("e", in your example) is
only available inside the function body. The eval has nothing to do with it:

(function e() { alert(typeof e); });
e(); // ReferenceError

If you try the same thing in IE, on the other hand, e _will_ be defined.
It will even be defined before the function expression is evaluated:

e(); // alerts "function" in IE; should be a ReferenceError
(function e() { alert(typeof e); });

This is a deviation from the specs which has been the source of much
confusion for developers. As a result, named function expressions are
not often used in a cross-browser environment.

BTW, this could does not happen with eval - "e" can only be defined
after the eval (even in IE):

e(); // ReferenceError
eval('(function e() { alert(typeof e); })');


--
stefan
From: Garrett Smith on
williamc wrote:
> On 5/8/2010 2:08 PM, Garrett Smith wrote:
>> williamc wrote:
>>> On 5/7/2010 5:04 PM, Dr J R Stockton wrote:
>>>> The expression typeof eval("(function F(){return 3})") gives
>>>> "function" in Firefox and Chrome (and probably in Opera and Safari); but
>>>> in IE8 it gives "undefined".
>>>>
>>>> What do I need to do to get the same in IE8 as in the others, i.e. for
>>>> eval to return an actual executable function, which would return 3 ?
>>>>
>>> Question: why does 'function F(){return 3}' need the parentheses around
>>> it to eval to 'function' in the non-IE implementations?
>> It doesn't. In fact, eval('function F(){return 3}') works as specified
>> in Spidermonkey and in Webkit's JS engine. The different behavior you
>> are seeing in IE is caused by JScript interpreting an expression and
>> returning that value.
>>
>> A FunctionDeclaration is not an Expression; it cannot return a value.
>
> Understood...
>
>> Understanding the behavior you may be seeing requires an understanding
>> of how the language is designed to be interpreted and how JScript
>> deviates from that.
>>
>> When the (one) argument to eval is a string value, it is parsed as a
>> Program. In ES3, eval uses the calling context's scope. What that means
>> is that when eval is called, it can access and can create variables
>> using the scope chain from where it was called. An example of that.
>>
>> var a = 9;
>> function testEval(){
>> var a;
>> // When eval is called, calling context's scope is used.
>> eval('a = 11');
>> return a;
>> }
>> alert(testEval(), a)
>>
>> When `testEval` is called, a VO is created and `a` added to it with
>> value `undefined`. When the `eval` function is called, that VO is used
>> and so expression 'a = 11' resolves `a` on that VO.
>>
>> The string starting with "function F(){return 3}" could be parsed only
>> as a FunctionDeclaration. This is because an ExpressionStatement cannot
>> begin with "function ".
>>
>> When a FunctionDeclaration is parsed, as is the case with your example
>> code, it is added as a property to the VO (Variable Object).
>>
>> and so the code:
>>
>> eval("function F(){return 3}");
>>
>> - should create a property of the calling context's scope.
>>
>> And indeed it does:
>>
>> eval("function F(){return 3}");
>> F();
>
> Ok. So if I'm understanding correctly, running the eval as above is very
> similar to using a function expression like...
>

No. When eval is called with a string argument, an execution context is
entered using the what is known as the "calling context's" scope and
`this` value. If eval was called in global context, then the `this`
value and variable object is the global object.

I suggest reading "entering an exectution context" and "Variable
Instantiation" from ECMA-262.

> F = function() { return 3 } ;
> alert(F()); // 3
>
> but I'm still not entirely clear about the effect of the parentheses.
> If I have code like the following...
>
> c(); // 'c' - c is available *here* as it was created
> // with a function declaration...
>

Yes. The function declaration was interpreted when the execution context
was entered.

> try {
> d();
> } catch (ex) {
> alert(ex.name); // 'ReferenceError' - d is not available here...
> }
>

Identifier `d` is not available yet. It gets created in a call to eval,
and the call to `eval` has not been reached.

> var foo = typeof eval("function d() { alert('d'); }"); // inside eval
[^^^^|^^^^^] [^^^^^^^^^eval context^^^^^^^^]
calling ctx

when the interpreter first enters the execution context, it sees "var
foo" and adds that to the VO.

After all variable declarations and function declarations have been
processed, statements are evaluated, as they appear in source order.

Inside this call to eval, the string value is found and parsed as a
Program, and so a new execution context is entered then. The scope and
`this` value of the calling context is passed in. The string argument is
parsed, a FunctionDeclaration is found from that and added to the scope,
the call completes normally and returns undefined.

> var bar = typeof eval("(function e() { alert('e'); })");
> // as in the original example, with parends
>

> alert(foo); // 'undefined

As to be expected and explained by Stephen Weiss.

> alert(bar); // 'function'
>

Yes, because in the second call to eval, an ExpressionStatement was
found, evaluated, and resulting value returned to the calling context,
where it is used in a typeof operation.

> c(); // 'c'
> d(); // 'd' - function d is now available...
>
> // the eval seemed to act basically the same as a function expression
> // would have. Overwrite d with an identical function
>

In a way, yes. The call to eval code is an expression; the code *inside*
that call may be a FunctionDeclaration.

A call to eval cannot modify the calling context before it has been called.

function a(){
var a = 10;
eval("a = 11");
function b(){}
}

The interpreter does two passses. In the first pass, `a` and `b` are
added to the VO. `a` gets value undefined, `b` the value of the
function. IN the second pass, `a` is assigned value 10, then eval is
called, then in that eval call, a new execution context is entered, but
using the calling context's scope (and `this` value). In that eval
context, `a` is resolved and assigned the value `10`.

> d = function() { alert('d'); };
> d(); // same result
>
> // but function e is not available...
>

Nor should it be. I explained this previously where I wrote:

| There is one more significant difference here: The optional identifier
| in a FunctionExpression does not affect the enclosing scope. This
| means that with:
|
| eval("(function F(){return 3})");
|
| The identifier `F` is not added to enclosing scope.

> try {
> e();
> } catch (ex) {
> alert(ex.name); // 'ReferenceError'
> }
>

That is the expected outcome.
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: Garrett Smith on
Garrett Smith wrote:
> williamc wrote:
>> On 5/8/2010 2:08 PM, Garrett Smith wrote:
>>> williamc wrote:
>>>> On 5/7/2010 5:04 PM, Dr J R Stockton wrote:
>>>>> The expression typeof eval("(function F(){return 3})") gives
>>>>> "function" in Firefox and Chrome (and probably in Opera and
>>>>> Safari); but
>>>>> in IE8 it gives "undefined".

[...]

> The interpreter does two passses. In the first pass, `a` and `b` are
> added to the VO. `a` gets value undefined, `b` the value of the
> function. IN the second pass, `a` is assigned value 10, then eval is
> called, then in that eval call, a new execution context is entered, but
> using the calling context's scope (and `this` value). In that eval
> context, `a` is resolved and assigned the value `10`.
Correction:
" In that eval context, `a` is resolved and assigned the value `11`."
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: Ry Nohryb on
On May 9, 12:31 am, Dr J R Stockton <reply1...(a)merlyn.demon.co.uk>
wrote:
> In comp.lang.javascript message <z5edIBuhBI5LF...(a)invalid.uk.co.demon.me
> rlyn.invalid>, Fri, 7 May 2010 22:04:33, Dr J R Stockton
> <reply1...(a)merlyn.demon.co.uk> posted:
>
> >The expression   typeof eval("(function F(){return 3})")    gives
> >"function" in Firefox and Chrome (and probably in Opera and Safari); but
> >in IE8 it gives "undefined".
>
> >What do I need to do to get the same in IE8 as in the others, i.e. for
> >eval to return an actual executable function, which would return 3 ?
>
> typeof eval("T=function F(){return 3}")

Well. But avoid using named function expressions in IEs:

typeof eval("var T= function () { return 3 }")
--
Jorge.