2

If we define a function f() { ... } in eval(...), why isn't it available for the rest of the code, such as #button3's onclick?

As a comparison, if we define the function as f = () => { ... };, then it is available for #button3's onclick.

Why this difference, which seems a little bit contradictory with var functionName = function() {} vs function functionName() {} (here the idea is function f() { ... } has a wider scope than f = () => { ... };).

Click 1 and 3 (Uncaught ReferenceError: f is not defined), and then 2 and 3 (working):

document.getElementById('button1').onclick = () => eval("function f() { alert('hello'); }");
document.getElementById('button2').onclick = () => eval("f = () => { alert('hello'); };");
<div id="button1">1 Click me to define f as a function</div>
<div id="button2">2 Click me to define f as a variable function</div>
<div id="button3" onclick="f();">3 Click to run f()</div>

6
  • 2 then 3 doesn't work for me - ReferenceError for f Commented Aug 2, 2022 at 21:56
  • @CertainPerformance I updated the snippet, now you can 2 then 3 working. Commented Aug 2, 2022 at 21:58
  • What do you get if you use const like: const f = () => instead of just a Global f = () =>? Commented Aug 2, 2022 at 22:01
  • @RokoC.Buljan I haven't tested yet, but I tested f = () => { }; working, var f = () => { }; not working. Why? Commented Aug 2, 2022 at 22:05
  • @Basj CertainPerformance gave you an indepth answer Commented Aug 2, 2022 at 22:07

1 Answer 1

1

As MDN says on eval:

var-declared variables and function declarations would go into the surrounding scope if the source string is not interpreted in strict mode — for indirect eval, they become global variables. If it's a direct eval in a strict mode context, or if the eval source string itself is in strict mode, then var and function declarations do not "leak" into the surrounding scope.

Here, you're using direct eval (because you're referencing eval) directly, and that "surrounding scope" is the inside of the click handler. To see this working, click, then see how the function f approach defines an f referenceable later in the click handler (but not outside of it):

document.getElementById('button1').onclick = () => {
  eval("function f() { alert('hello'); }");
  f();
}

// scope doesn't extend to out here
setTimeout(() => console.log(typeof f), 3000);
<div id="button1">1 Click me to define f as a function</div>

Doing

f = () => { ... };

is just like doing that in plain code, without eval - without declaring it with const, let, var, or function, you're implicitly assigning to the global object, so

eval("f = () => { alert('hello'); };");

is equivalent to

eval("window.f = () => { alert('hello'); };");

if no f variable exists in scope at that time.

// runs
(() => {
  f = () => console.log('f');
})();
f();


// doesn't run
(() => {
  function g() {
    console.log('g');
  }
})();
g();

Inline handlers may only reference global variables (99% of the time), so for onclick="f();" to work, window.f must exist.

You're using direct eval. Note that if you used indirect eval, the evaled code won't run in the scope of where the eval reference was called, but rather on the top level - and as a result, any variable declaration inside such an eval will be visible everywhere.

// both work
const indirectEval = eval;
document.getElementById('button1').onclick = () => indirectEval("function f() { alert('hello'); }");
document.getElementById('button2').onclick = () => indirectEval("f = () => { alert('hello'); };");
<div id="button1">1 Click me to define f as a function</div>
<div id="button2">2 Click me to define f as a variable function</div>
<div id="button3" onclick="f();">3 Click to run f()</div>

Sign up to request clarification or add additional context in comments.

7 Comments

Oh nice! Is it true in general that f = "anything"; without let, const, var, function is equivalent to window.f = "anything";? Is there a canonical SO question/answer about this?
In sloppy mode, if f hasn't been defined yet, yes - it'll be added as a property to the global object. In strict mode (which is always recommended), trying to assign to a variable that hasn't been declared will instead throw an error. See stackoverflow.com/q/1470488
(...and PS: modules are always treated as Strict Mode.) @Basj
Thanks @CertainPerformance! So if we want a <div onclick="f();" to access a function which is defined later with eval, the only solution is to define f as global? Context: I'm inserting both this <div onclick="f();"> and the <script> block in the DOM from the response of an AJAX call, with container.innerHTML = .... Then I need to eval() the <script> as mentioned in stackoverflow.com/questions/1197575/…. Thus the onclick="f();" has to reference a function which is available here, thus this question. Any idea?
@Basj I'd extract the text content inside the script tag, then insert another script tag
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.