为什么这个函数表达式的行为与函数声明不同?

3

考虑以下代码片段:

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 

代码不能正常工作,无限输出“intercept”。然后我搜索了Stack Overflow,发现以下修改过的代码可以按预期工作:

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

我的问题是:为什么第一段代码不能按预期工作?我知道函数声明和函数表达式之间存在差异(主要是由于提升行为),但我仍然感到困惑。
编辑:确实是一个被误解的提升行为。我意识到如果我按照以下方式执行第一段代码,它可以正常工作:
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

这个问题已经得到解决。当我单独执行代码时,很明显函数myFunction被提升了。


1
可能是Javascript函数作用域和提升的重复问题。 - pishpish
4个回答

3
这是因为变量提升(hoisting)。在JavaScript中,函数声明会被提升,意味着它们会被“提升”到顶部并首先声明,可以在定义之前使用:

foo(); //logs "in foo"

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

另一方面,函数表达式不像函数声明那样被提前到作用域顶部。虽然 var 会提前声明,但它的值(即匿名函数)并未赋给变量。为了更清楚地说明这一点,您的第一个代码段在函数声明和 var 提前之后实际上看起来像这样:

var oldFunction;

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

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

//myFunction(1);

oldFunction = myFunction;

myFunction(1);

(顺便说一句:我已经注释掉了第一个调用,因为在执行myFunction的第一次尝试时oldFunction未定义而导致错误。上面的片段复制了您在问题的第一个片段中所描述的内容)

因此,在您的代码中,第一个函数无法正常工作,因为您有两个同名函数。因此,第一个myFunction被覆盖,现在它指的是新函数。这将导致函数递归调用自身,进而无限运行。

在您的第二个示例中,实际上是这样的:

var oldFunction;
var myFunction;

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

//myFunction(1);

oldFunction = myFunction;

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

在函数提升中,整个声明都被提升,而不仅仅是像var一样的名称。因此,在第二个例子中,第一个函数在第一次调用时不会被覆盖。然后,当你执行oldFunction = myFunction时,你将oldFunction赋值为对myFunction的引用,即旧函数。执行它将执行旧函数。这将记录:

intercept
1

正如预期的那样,并不会无限递归。


1
只有这部分起作用:
function myFunction(a){
    console.log(a);
}

myFunction(1);
//1

一旦你添加了剩下的内容...

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 

它将失败,因为对于JS引擎来说,它的读法如下:(按照这个顺序)

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 

由于变量提升,在您的代码中,此函数
function myFunction(a){
    console.log(a);
}

被完全消除并立即替换为

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

在代码中执行任何表达式之前,就会出现这样的情况,例如var oldFunction = myFunction


0
在第一个代码片段中,new myFunction覆盖了myFunction,并且oldFunction引用了相同的函数,因此代码以递归顺序执行。但是在第二个代码中,第二个myFunction被声明为匿名函数,它不会覆盖旧的myFunction,当调用oldFunction时,它仅指向第一个myFunction。

0

首先,示例中定义了oldFunction函数

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

在第二个例子中,oldFunction 被定义为

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接