0

My question is basically an extension to this one: var functionName = function() {} vs function functionName() {}. I've read through all the answers, but I think I'm still missing the answer to this question:

Why does the type of function declaration matter for jQuery's onComplete event and the native setInterval method? Moreover, why does it matter for these, but not for the normal addEventListener?

Example with onComplete events (assuming blinkOn() is called):

//like this the onComplete event works normally and the functions call eachother
function blinkOn() { $blink.fadeIn( 500, blinkOff ); }
function blinkOff() { $blink.fadeOut( 500, blinkOn ); }

//like this blinkOff won't be called after the fadeIn
function blinkOn() { $blink.fadeIn( 500, blinkOff ); }
var blinkOff = function() { $blink.fadeOut( 500, blinkOn ); };

Example with setInterval:

//works fine
var timer = setInterval( onTimer, 700);
function onTimer() { ... }

//won't work
var timer = setInterval( onTimer, 700);
var onTimer = function() { ... }

The reason I'd like to know is because I want consistency in my code. If the only way to get that is just using function a() { ... }, that's okay. But I think var a = function() { ... } is more clear in defining scope. And I'm just really curious.

Community
  • 1
  • 1
navFooh
  • 153
  • 1
  • 7
  • Have you defined the `var onTimer` function within another function, or is it in the global scope? – SexyBeast Oct 02 '12 at 16:03
  • It has nothing to do with jQuery, native addEventListener or setInterval - all work the same. – Bergi Oct 02 '12 at 16:30

3 Answers3

2

If you were to define your function above where your calls to the function are, you'd have no issues.

var toggleLight = function () { light = !light; };

setInterval(toggleLight, 500);

Your problem is that you're trying to access things which don't exist yet.

It's the exact same thing as trying to do this:

var c = a + b,

    a = 12,
    b = 3;

When the program hits c, a and b don't exist.

Think of this as happening in three steps, for every single function:

var a = 12,
    b = 3,
    c = add(a, b);

function add (a, b) { return a + b; }

STEP 1.
The engine grabs the named function add and works out what it does (defines all named-functions first).

STEP 2.
The engine locates all variables in the function and sets them to undefined:

var a = undefined,
    b = undefined,
    c = undefined;

function add(a, b) { return a + b; }

STEP 3.
The engine initializes all variables in order.

// 3.1
var a = 12,
    b = undefined,
    c = undefined;


// 3.2
var a = 12,
    b = 3,
    c = undefined;

// 3.3
var a = 12,
    b = 3,
    c = add(a, b); // = return a + b = 15

So doing something like this:

callFunction(myFunc);
var myFunc = function () {};

Is equal to this:

var myFunc = undefined;
callFunction(myFunc);
myFunc = function () {};

Solution:

Separate your implementation from your action. Make sure everything is defined before you call anything.

Norguard
  • 24,349
  • 4
  • 38
  • 45
  • Aah thanks I see clearly now! It had me so tangled why a difference in function-declaration could cause this. I just thought the named function was a shorthand (doing exactly the same) as the other. But it makes total sense as you describe it. The cases where it did and did not work in my code all had to do with the order. – navFooh Oct 03 '12 at 22:39
  • It's not a shorthand. You can even name functions which are assigned to vars: `var myFunc = function yourFunc (param) { doStuff(param); };` And that's useful for things like recursion (it can call itself by function-name, instead of variable name, in the function). BUT not even this will get past the `undefined` aspect, and you can't use the function name, either (only declared functions work like that - meaning even if its named, it can't be on the right of an `=`). One thing to keep in mind, though -- if you call a function inside of another function's body, it doesn't need to be defined – Norguard Oct 03 '12 at 22:49
  • until you call it. Here's what I mean: `var func2 = function (data) { func1(data), func1 = function (data) { doStuff(data); };`. In this case `func2` knows about `func1`, and like I said, when `func2` is created, `func1` is still `undefined`. But it doesn't need to be defined until `func2` is called, because that variable resolution happens at the last second, when a function gets called. The difference between this working and the problem you had was that you were passing `undefined` into other functions (like `setTimeout`). You get the idea. – Norguard Oct 03 '12 at 22:54
  • Thanks again! All very useful stuff to know! – navFooh Oct 03 '12 at 22:58
1

Named functions are kinda created in the nowhere, so they become globally available in the scope where they are defined, where as anonymous functions assigned to an identifier are only available once the identifier they are bound to is defined.

So what you are running into there is a simple race condition problem. Your first non-working example should work in case blinkOn() is called after the blikOff variable definition.

Your second non-working example doesn't work because onTimer is not yet defined when setInterval is invoked. Move the variable definition before the setInterval call and it will work.

ndm
  • 54,969
  • 9
  • 66
  • 105
  • Yeah, that was the first non-technical circumscription of what's going on there that came to my mind :) – ndm Oct 02 '12 at 16:34
  • Haha it does capture the spirit. But now I think more of sticking to named functions. It is appealing to separate all logic from the actions.. but I see no harm in functions that are globally available in their own scope? Just being able to call them anywhere in their own scope seems more like an advantage, or not? – navFooh Oct 03 '12 at 22:45
  • Well, i don't know how all the different engines handle that internally, but there's probabyl little to no difference, other than that a named function is being declared as the first thing in the the scope. – ndm Oct 05 '12 at 03:35
0
//like this blinkOff won't be called after the fadeIn
function blinkOn() { $blink.fadeIn( 500, blinkOff ); }
var blinkOff = function() { $blink.fadeOut( 500, blinkOn ); };

It will be called. You just have to make sure that blinkOn is not called (referenced is okay) before (above) the assignment.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164