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

时间:2017-04-29 20:18:32

标签: javascript

请考虑以下代码段:

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

4 个答案:

答案 0 :(得分:3)

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

foo(); //logs "in foo"

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

另一方面,函数表达式不像函数声明那样被提升。 var is still hoisted但它的值(匿名函数)没有给变量。为了使其更清晰,您的第一个片段基本上看起来像这样(在函数声明和var之后被提升):

var oldFunction;

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

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

//myFunction(1);

oldFunction = myFunction;

myFunction(1);

(旁注:我已经注释掉了第一个引发错误的调用,因为当你第一次尝试执行oldFunctionmyFunction未定义。上面的代码片段重现了你的内容在问题的第一个片段中描述

因此,在您的代码中,第一个不起作用因为您有两个相同名称的函数。因此,第一个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

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

答案 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

这样的东西

答案 2 :(得分:0)

首先,示例oldFunction定义为

function myFunction(b){
    console.log('intercept');
    oldFunction(b); // calls `oldFunction` again
}
第二个示例oldFunction

定义为

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

答案 3 :(得分:0)

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