Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this function expression behave differently than a function declaration?

Tags:

javascript

Consider the following snippet:

function myFunction(a){
    console.log(a);
}

myFunction(1);
//1

var oldFunction = myFunction;

function myFunction(b){
    console.log('intercept');
    oldFunction(b);
}
myFunction(1);
//too many calls, console outputs intercept indefinitely 

The code doesn't work properly and outputs "intercept" indefinitely. Then I searched Stack Overflow and I found out that the following modified code worked as expected:

function myFunction(a){
     console.log(a);
}

myFunction(1);//1

var oldFunction = myFunction;

var myFunction = function(b){
    console.log('intercept');
    oldFunction(b);
}
myFunction(1);
//intercept 
//1

My question is: why doesn't first code work as expected? I know there are differences between function declarations and function expressions (primarily due the hoisting behavior), but I'm still confused.

EDIT: Indeed is a hoisting behavior misunderstood. I realize that the first code works if I execute it as the following:

eval(`function myFunction(a){
    console.log(a);
}

myFunction(1);
//1

var oldFunction = myFunction;`);

eval(`function myFunction(b){
    console.log('intercept');
    oldFunction(b);
}
myFunction(1);`);
//1
//intercept
//1

This closes the question. When I execute the code separately becomes clear that the function myFunction was being hoisting.

like image 804
mesompi Avatar asked Apr 29 '26 12:04

mesompi


1 Answers

This is because of hoisting. In JavaScript, function declarations are hoisted meaning they are "hoisted" up to the top and declared first, and can be used before they are defined:

foo(); //logs "in foo"

function foo() {
  console.log("in foo");
}

On the other hand, function expressions are not hoisted like function declarations are. var is still hoisted but it's value (the anonymous function) isn't given to the variable. For the purpose of making this more clear, your first snippet essentially looks like this (after function declarations and vars are hoisted):

var oldFunction;

function myFunction(a) {
  console.log(a);
}

function myFunction(b) {
  console.log('intercept');
  oldFunction(b);
}

//myFunction(1);

oldFunction = myFunction;

myFunction(1);

(Side note: I've commented out the first call which throws an error because oldFunction is undefined when you try to execute myFunction the first time. The snippet above reproduces what you described in the first snippet of the question)

Thus, in your code, the first does not work because you have two functions of the same name. Thus the first myFunction is overridden and now it refers to the newer function. This will cause the code to recurse as the function is calling itself, making it run indefinitely.

In your second example, it's essentially this:

var oldFunction;
var myFunction;

function myFunction(a) {
  console.log(a);
}

//myFunction(1);

oldFunction = myFunction;

myFunction = function(b) {
  console.log('intercept');
  oldFunction(b);
}
myFunction(1);

In function hoisting, the whole declaration is hoisted, not just the name like with var. So in the second example, the first function is not overridden when you call it the first time. Then, when you do oldFunction = myFunction you assign oldFunction a reference to myFunction, which is the older function. Executing it will execute the older function. This will log:

intercept
1

As expected and will not recurse forever.

like image 198
Andrew Li Avatar answered May 02 '26 00:05

Andrew Li