13

I'm trying to gain a deeper understanding of how Javascript works and the following code is bugging me:

function notInVar(a, b) {
    return a + b
}

var inVar = function doesThisWork(a, b) {
    return a + b
}

document.writeln('2 + 2 = ' + notInVar(2, 2));
document.writeln('3 + 3 = ' + inVar(3, 3));
document.writeln('4 + 4 = ' + doesThisWork(4, 4));

In Chrome, the first two document.writelns execute as expected, then I get "Uncaught ReferenceError: doesThisWork is not defined" in Chrome. Why can't I call the second function by the name doesThisWork? For that matter, where is the first function-object notInVar stored?

neo108
  • 5,156
  • 3
  • 27
  • 41
Ian Durkan
  • 1,212
  • 1
  • 12
  • 26
  • 2
    @stackErr: Seems fine. That's a named function expression. – elclanrs Jul 03 '13 at 02:33
  • not sure, but I would say variable scope. – Jonathan de M. Jul 03 '13 at 02:33
  • 1
    [This seems useful](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope#Function_constructor_vs._function_declaration_vs._function_expression). – Marty Jul 03 '13 at 02:36
  • http://www.permadi.com/tutorial/jsFunc/index.html Check this out – Jack Daniel's Jul 03 '13 at 02:36
  • whey was everybody against my answer? I was showing the proper syntax,, anyways I do too want to deeper understand why this doesn't work `var a= var b;` => wrong syntax,, I think the browser is skipping the function's name and makes it anonymous then assign it, but still it returns the name in the definition of the value ! – CME64 Jul 03 '13 at 02:58
  • I didn't ask for proper syntax, I asked for an explanation of the mechanism behind the JS runtime's behavior. – Ian Durkan Jul 03 '13 at 03:04
  • 1
    Incidentally, it "works" in IE 'cos IE is broken. :-) The optional name should only be available inside the function, not outside. BTW, the ECMA reference is for [*FuncionExpression*](http://www.ecma-international.org/ecma-262/5.1/#sec-13), scroll down to the part with the optional identifier and it should become clear. – RobG Jul 03 '13 at 03:09
  • +1 for the interesting question – CME64 Jul 03 '13 at 04:05
  • possible duplicate of [JavaScript: var functionName = function() {} vs function functionName() {}](http://stackoverflow.com/questions/336859/javascript-var-functionname-function-vs-function-functionname) – Alvin Wong Jul 03 '13 at 12:20

6 Answers6

8

The second definition is called a named function expression and due to the nature of that definition, the only way you can call it by name is from inside the function body:

var inVar = function doesThisWork(a, b) {
    return doesThisWork(a + b); // infinite recursion!
}

This can be used to achieve recursion inside an otherwise anonymous function, without having to use something like the Y-combinator.

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
  • How does this relate to the Y-Combinator? This won't let you do infinite recursion like the Y-Combinator will if that's what you're suggesting. –  Jul 03 '13 at 02:55
  • @CrazyTrain The Y-combinator effects anonymous recursion; and infinite recursion is always possible :) – Ja͢ck Jul 03 '13 at 04:45
  • With the Y-Combinator, you can have infinite recursion because the call stack doesn't grow. Giving the function a name doesn't prevent the call stack from growing. And JavaScript functions aren't tail-call optimized. –  Jul 03 '13 at 13:17
  • @CrazyTrain I see what you mean now; it's also somewhat irrelevant to the answer itself, the "infinite recursion!" comment is just there to warn people not to try the code without knowing what they're doing :) of course, I also consider recursion infinite even if the stack breaks heh. – Ja͢ck Jul 03 '13 at 13:23
5

Functions are objects. The variable inVar holds a function object with a name of doesThisWork.

inVar.name //=> "doesThisWork"

If a function has no name it is anonymous.

To call a function stored in a variable you use the variable name (the reference to that object). If you want to call the function inside the same function (for recursion) you can call it by its name, in this case doesThisWork.

elclanrs
  • 92,861
  • 21
  • 134
  • 171
4

For that matter, where is the first function-object notInVar stored?

function notInVar(a, b) {
    return a + b
}

Is equivalent to

var notInVar = function (a, b) {
        return a + b
}

In your case, notInVar is stored in the global scope.

then I get "Uncaught ReferenceError: doesThisWork is not defined" in Chrome

var inVar = function doesThisWork(a, b) {
    return a + b
}

Is similar to

var inVar = function (a, b) {
        return a + b
    }

when it's accessed outside the function

You cannot access the function by doesThisWork, but have to access it by inVar

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • 1
    Not quite... The closest equivalent would be `var foo = function foo(){}`, that way you still have a `name` that can show up in the call stack trace. – elclanrs Jul 03 '13 at 02:49
  • @elclanrs: I think I need to reword that. It's **similar** to when it's accessed **outside** the function. Thanks for pointing out, any way – Khanh TO Jul 03 '13 at 02:51
  • @KhanhTO actually, when you access doesThisWork from inVar you are actually accessing the function not the doesThisWork, the doesThisWork is local to the variable (or object) that contains it and cannot be called from outside. the same goes for a function that is declared inside another. – CME64 Jul 03 '13 at 06:05
1

The way you have it written, doesThisWork is only available inside itself.

go-oleg
  • 19,272
  • 3
  • 43
  • 44
0

Function is a variable and the scope of the variable matters. For the second function, in the global scope, its variable name is inVar. The function name doesThisWork is inside its own scope and is not visible to the global scope. So you can only use inVar, not doesThisWork.

0

well there are multiple things about this:

first: the name of the function will be local, so you can call the same function from within locally only. which might trigger an infinite recursion when used, unless it is filtered like this if(doesThisWork.caller != doesThisWork) return doesThisWork(a,b);.

second is that you are assigning a name for the function (not leaving it as an anonymous function) but local to it's container.

TL;DR => jump to the analysis for a clearer idea.

it is interesting to note the differences between function's declaration methods:

inline declaration :

var x = function fnName(){}; console.log(x.prototype); => fnName {} // but used locally to x
var x = function(){}; console.log(x.prototype); => Object {} // no local name, only global x

at parse-time/run-time declaration:

function fnName(){}; console.log(fnName.prototype); => fnName {} // global* and local name

My analysis: is that the locality here is due to the assignment as when you declare a function within function, that name will be used locally to the containing function and not outside that, the same goes for the variable that contains a function, since it contains it and it's name. still the variable that contains the function can be used in it's scope, as it is local to it's container function which is a different scope.

*global here means global to the function's location not to the document. as it is local to it's container but global to other objects in the same container.

CME64
  • 1,673
  • 13
  • 24