最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does a function redefining itself behave differently in ChromeIE and Firefox? - Stack Overflow

programmeradmin0浏览0评论

Consider the following code:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}

f();

console.log("After a call to f(), f is: \n%s", f);

I expected f to be defined at all times during execution. However, in Chrome and IE, it is undefined when the first console.log is invoked, and in Firefox, it is undefined when the second console.log is invoked.

Why is f not always defined? Why do Chrome/IE and Firefox behave differently?

/

Output on Firefox 26:

Inside a call to f(), f is:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}

After a call to f(), f is:

undefined

Output on Chrome 31 and IE 11:

Inside a call to f(), f is:

undefined

After a call to f(), f is:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}

Consider the following code:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}

f();

console.log("After a call to f(), f is: \n%s", f);

I expected f to be defined at all times during execution. However, in Chrome and IE, it is undefined when the first console.log is invoked, and in Firefox, it is undefined when the second console.log is invoked.

Why is f not always defined? Why do Chrome/IE and Firefox behave differently?

http://jsfiddle/G2Q2g/

Output on Firefox 26:

Inside a call to f(), f is:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}

After a call to f(), f is:

undefined

Output on Chrome 31 and IE 11:

Inside a call to f(), f is:

undefined

After a call to f(), f is:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}
Share Improve this question edited Jan 8, 2014 at 23:04 user247702 24.3k18 gold badges115 silver badges160 bronze badges asked Jan 8, 2014 at 22:55 Dagg NabbitDagg Nabbit 76.8k19 gold badges114 silver badges142 bronze badges 22
  • Suggestions for a better title wele :) – Dagg Nabbit Commented Jan 8, 2014 at 22:56
  • Maybe their consoles are made differently..? – Abraham Hamidi Commented Jan 8, 2014 at 22:57
  • 1 @adeneo this definitely has something to do with the eval because when we do not perform the "" inside the eval - it works. Also, when we use new Function or assign it to itself - it works. – Benjamin Gruenbaum Commented Jan 8, 2014 at 23:09
  • 1 If you do something other than eval'ing, it works as expected -> jsfiddle/G2Q2g/2 – adeneo Commented Jan 8, 2014 at 23:09
  • 1 I asked in es-discuss now esdiscuss/topic/behavior-of-eval-in-non-strict-mode – Benjamin Gruenbaum Commented Jan 9, 2014 at 0:03
 |  Show 17 more ments

3 Answers 3

Reset to default 8

First of all, let's talk about what we would 'expect'.

I would naively expect both cases to return undefined.

  • Just like: eval("function foo(){}") which returns undefined.

  • Just like whenever we have a function declaration - it does not return the function value but sets it.

  • Just like the langue specification says for strict mode.

Update: after digging more through the spec - Firefox is correct here.

Here is what Firefox is doing

Visualized:

  1. f = eval("" + f); // set the left hand side to the function f we're in
  2. f = eval("" + f); // declare a new function f in the scope of this function
  3. f = undefined; // since undefined === eval("function(){}"); *

* since function declarations do not return anything - just like function foo(){} has no return value

Since f was decided in step 1, right now the reference to the function we're in was overwritten with undefined and a local closure declared f was declared with the same code. Now when we do:

console.log("Inside a call to f(), f is: \n%s", f) // f is the local closure variable, it's closest

Suddenly, it's obvious we get the function - it's a member variable.

However, as soon as we escape the function

console.log("After a call to f(), f is: \n%s", f);

Here, f is undefined since we overwrote it in step 1.

Chrome and IE make the mistake of assigning it to the wrong f and evaluating the right hand side before the left hand side of an assignment.

Why it works in strict mode

Note that the next section says in Entering eval code:

Let strictVarEnv be the result of calling NewDeclarativeEnvironment passing the LexicalEnvironment as the argument.

Which explains why it works in strict mode - it's all run in a new context.


Same thing, but in more text and less graphically

  • "Find" f from f = (since the left hand side must be evaluated first. This refers to the local copy of f. That is, evaluate the left hand side first.
  • Perform the eval call which returns undefined but declares a new local function of f.
  • Since f from f = was evaluated before the the function itself, when we assign undefined to it we're actually replacing the global function
  • So when we do console.log inside we're referring to the local copy declared in the eval since it's closer in the scope chain.
  • When we're on the outside and do console.log, we are now referring to the 'global' f which we assigned undefined to.

The trick is, the f we're assigning to and the f that we're logging are two different fs. This is because the left hand side of an assignment is always evaluated first (sec 11.13.1 in the spec).

IE and Chrome make the mistake of assigning to the local f. Which is clearly incorrect since the specification clearly tells us:

  1. Let lref be the result of evaluating LeftHandSideExpression.

  2. Let rref be the result of evaluating AssignmentExpression.

So, as we cal see the lref needs to be evaluated first.

(link to relevant esdiscuss thread)

I'm sorry but I can only answer your first question as of now :-/

I expected f to be defined at all times during execution. Why is f not always defined?

Two things:

  • evaling a function declaration does return undefined. It may be redefined as it is evaluated, but you then assign undefined to f thereafter.
  • f is a local variable in the function f, since named functions are available in their own scopes.

Check this behaviour in http://jsfiddle/G2Q2g/5/.

So now you at least may ask

Why is it undefined when the second console.log is invoked in Firefox, as opposed to the correct behaviour in Opera, Chrome and IE?

Change your code to

function f() {
    f = eval("(" + f + ")");
    console.log("Inside a call to f(), f is: \n%s", f);
};

f();

console.log("After a call to f(), f is: \n%s", f);

and it will work. Note that function source is wrapped into "(" ")" brackets to make it an lvalue expression and not a declaration.

Here it is http://jsfiddle/G2Q2g/8/

UPDATE:

eval(str) parses and executes the string as a Program (ECMAScript term). Function declaration by itself is a declaration, it has no value. But this ( func() ... ) is an expression that has value. And eval returns value of last expression. That' s what you see here.

发布评论

评论列表(0)

  1. 暂无评论