【问题标题】:Why does this function expression behave differently than a function declaration?为什么这个函数表达式的行为与函数声明不同?
【发布时间】:2017-09-27 17:55:48
【问题描述】:

考虑以下 sn-p:

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 

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

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 正在被提升。

【问题讨论】:

标签: javascript


【解决方案1】:

这是因为吊装。在 JavaScript 中,function declarations are hoisted 表示它们被“提升”到顶部并首先声明,并且可以在定义之前使用:

foo(); //logs "in foo"

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

另一方面,函数表达式不像函数声明那样被提升。 var is still hoisted 但它的值(匿名函数)没有赋予变量。为了更清楚地说明这一点,您的第一个 sn-p 基本上看起来像这样(在函数声明和 vars 被提升之后):

var oldFunction;

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

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

//myFunction(1);

oldFunction = myFunction;

myFunction(1);

(旁注:我已注释掉第一个引发错误的调用,因为当您第一次尝试执行 myFunction 时,oldFunction 未定义。上面的 sn-p 重现了您在问题的第一个sn-p)

因此,在您的代码中,第一个 不起作用,因为您有两个同名的函数。因此,第一个 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 这样的名称。因此,在第二个示例中,第一个函数在您第一次调用时 not 被覆盖。然后,当您执行oldFunction = myFunction 时,您为oldFunction 分配了对myFunction 的引用,这是较旧的函数。执行它将执行旧功能。这将记录:

intercept
1

正如预期的那样,不会永远递归。

【讨论】:

    【解决方案2】:

    只有这部分有效:

    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

    【讨论】:

      【解决方案3】:

      第一个例子oldFunction被定义为

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

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-12-31
          • 1970-01-01
          • 2020-01-21
          • 2012-08-21
          • 1970-01-01
          • 2020-01-21
          • 1970-01-01
          相关资源
          最近更新 更多