简体   繁体   中英

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

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.

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 var s 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.

Only this part works:

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

myFunction(1);
//1

as soon as you add the rest ...

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 

it will fail, because to the JS engine it reads like this: (in exactly this order)

var myFunction, oldFunction;

myFunction = function(a){
    console.log(a);
}
//immediately overwriting myFunction by
myFunction = function (b){
    console.log('intercept');
    oldFunction(b);
}

myFunction(1); //this already fails, because `oldFunction` is undefined and therefore can't be called

oldFunction = myFunction;
myFunction(1);    //too many calls, console outputs intercept indefinitely 

Due to hoisting, in your code this function

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

is completely eliminated and immediately replaced by

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

before even a single expression is executed in the code. stuff like var oldFunction = myFunction

At first example oldFunction is defined as

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

at second example oldFunction is defined as

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

In first code sniplet myFunction overridden by new myFunction as well oldFunction references the same so code executes in recursive order. But in second code second myFunction declared as annomous function which is not overriding old myFunction and when oldFunction get called it points to first myFunction only.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM