4

I’m trying to wrap my head around the behavior of reference errors thrown in JavaScript.

In the following example, a ReferenceError is thrown at the second line, and execution breaks:

var obj = {};
obj.func1 = func2;

alert('Completed');

Whereas in this example, the code completes successfully, though obj.func1 remains undefined:

var obj = {};
obj.func1 = func2;

var func2 = function() {
    alert('func2');
};

alert('Completed');

My assumption was that an error would be thrown at the second line just the same, and when that wasn’t the case, I’d have expected obj.func1 to properly reference func2, but I’ve been double blind-sided. So what exactly is going on here?

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
Drazen Bjelovuk
  • 5,201
  • 5
  • 37
  • 64
  • [Function Hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function#Function_declaration_hoisting) and [Variable Hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting) – epascarello Feb 18 '16 at 05:26
  • 1
    @epascarello: There is no function hoisting with function expressions; only function *statements* are hoisted. This is `var` hoisting. – Amadan Feb 18 '16 at 05:26

3 Answers3

4

This is due to Javascript variable declaration "hoisting".

A variable declared with var is visible everywhere in the function, so there's no Reference Error. However, it doesn't actually receive its value until you execute the statement that initializes it. So your second example is equivalent to:

var func2;
var obj = {};
obj.func1 = func2;

func2 = function() {
    alert('func2');
};

alert('Completed');

In this rewrite, you can see that the variable exists when you perform the assignment to obj.func1. But since it doesn't yet have a value, you assign undefined to obj.func1. Assigning to func2 later doesn't change that.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Any reason the initialization of `obj` is coupled with its declaration? I understand since it'd occur immediately after anyway there'd be no practical difference, but is this a literal translation? – Drazen Bjelovuk Feb 18 '16 at 05:47
  • He's not referencing `obj` until after it's initialized, so there's effectively no difference between initializing it during the declaration or afterward. So I didn't bother rewriting that part, since it's not relevant to the question. – Barmar Feb 18 '16 at 05:49
  • I guess I'm just raising concern whether your example obscures the behavior a bit if the idea is that *declarations* exclusively are hoisted. – Drazen Bjelovuk Feb 18 '16 at 05:56
  • The distinction between declaration and initialization only matters if you try to use the variable before the declaration. If all the uses are after, there's no difference, so it's not necessary to show that rewrite. – Barmar Feb 18 '16 at 05:58
3

var is hoisted; the variable exists throughout the current scope. Thus, the second example is equivalent to:

var obj;
var func2;

obj = {};
obj.func1 = func2;
func2 = function() {
    alert('func2');
}

alert('Completed');

Thus, when you do the assignment, The name func2 is known, but undefined. In the first example, it is unknown, which raises ReferenceError.

Amadan
  • 191,408
  • 23
  • 240
  • 301
1

Your func2 variable is not visible. That's why obj.func1 remains undefined.

var obj = {};
var func2 = function() {
    alert('func2');
    return "Test";
};
    
obj.func1 = func2;
   
alert('Completed');
Thusithz
  • 736
  • 1
  • 12
  • 33
  • I'm well aware defining `func2` before assigning `func1` would have resolved the error. This wasn't a real problem, just something I found peculiar during some tinkering. – Drazen Bjelovuk Feb 18 '16 at 05:47