var functionName = function() {} 与 function functionName() {} 有什么区别?

7613

最近我开始维护别人的JavaScript代码。我正在修复错误,添加功能,并尝试整理代码并使其更加一致。

之前的开发人员使用了两种声明函数的方式,我无法确定是否有什么原因。

这两种方式是:

var functionOne = function() {
    // Some code
};

而且,

function functionTwo() {
    // Some code
}

为什么要使用这两种不同的方法,各自有哪些优缺点?是否有一种方法可以做到另一种方法无法实现的事情?

41个回答

20

第一个函数(function doSomething(x))应该是对象表示法的一部分。

第二个函数(var doSomething = function(x){ alert(x);})只是创建了一个匿名函数并将其赋值给变量doSomething。所以调用doSomething()将调用该函数。

您可能想知道什么是函数声明函数表达式

函数声明定义了一个命名的函数变量,而不需要变量赋值。函数声明作为独立的构造出现,不能嵌套在非函数块内。

function foo() {
    return 3;
}

ECMA 5(13.0)将语法定义为
function Identifier( FormalParameterListopt ){ FunctionBody }

在上述条件下,函数名在其范围以及其父级的范围内可见(否则它将无法访问)。

而在函数表达式中

函数表达式将函数定义为较大表达式语法的一部分(通常是变量赋值)。通过函数表达式定义的函数可以是具有名称或匿名的。函数表达式不应以“function”开头。

// Anonymous function expression
var a = function() {
    return 3;
}

// Named function expression
var a = function foo() {
    return 3;
}

// Self-invoking function expression
(function foo() {
    alert("hello!");
})();

ECMA 5(13.0)将语法定义为
function Identifieropt ( FormalParameterListopt ) { FunctionBody }


19

下面是它们的区别:

  1. 函数声明可以放在代码的任何位置。即使在调用函数之前,函数定义出现在代码中,函数声明也会在页面上的任何其他代码开始执行之前被提交到内存中或以某种方式提升,从而被执行。

    请看下面的函数:

function outerFunction() {
    function foo() {
       return 1;
    }
    return foo();
    function foo() {
       return 2;
    }
}
alert(outerFunction()); // Displays 2

这是因为在执行期间,它看起来像:

function foo() {  // The first function declaration is moved to top
    return 1;
}
function foo() {  // The second function declaration is moved to top
    return 2;
}
function outerFunction() {
    return foo();
}
alert(outerFunction()); //So executing from top to bottom,
                        //the last foo() returns 2 which gets displayed

如果在调用函数表达式之前未定义它,将导致错误。此外,在函数表达式中,函数定义本身不会像函数声明那样被移动到顶部或者提交到内存中。但是,我们分配给函数的变量会被提升,而且被赋值为undefined

使用函数表达式的相同函数:

function outerFunction() {
    var foo = function() {
       return 1;
    }
    return foo();
    var foo = function() {
       return 2;
    }
}
alert(outerFunction()); // Displays 1

这是因为在执行过程中,它看起来像:

function outerFunction() {
   var foo = undefined;
   var foo = undefined;

   foo = function() {
      return 1;
   };
   return foo ();
   foo = function() {   // This function expression is not reachable
      return 2;
   };
}
alert(outerFunction()); // Displays 1
  • 在非函数块(如 if)中编写函数声明是不安全的,因为它们将无法访问。

  • if (test) {
        function x() { doSomething(); }
    }
    
  • 像下面的命名函数表达式,在Internet Explorer9之前的版本中可能无法正常工作。

  • var today = function today() {return new Date()}
    

    15

    关于性能:

    V8 的新版本引入了一些底层优化,SpiderMonkey 也是如此。

    现在表达式和声明之间几乎没有区别。
    函数表达式 现在似乎更快

    Chrome 62.0.3202 Chrome test

    FireFox 55 Firefox test

    Chrome Canary 63.0.3225 Chrome Canary test


    匿名 函数表达式 似乎具有更好的性能 相对于 命名 函数表达式。


    Firefox Firefox named_anonymous Chrome Canary Chrome canary named_anonymous Chrome Chrome named_anonymous


    1
    结果差异太小,无法被视为差异。如果您运行测试100次,您将获得100个结果。 - Ronny Sherer
    @RonnySherer,你熟悉jsperf吗? 测试是在运行了超过1000万次之后进行的! - Panos Kalatzantonakis
    1
    每个测量都存在干扰。计算机不处于相同的状态,这也不是计算机上运行的唯一进程。 当差异很小时,意味着您不能依赖它,而且实际上是相同的。 尝试连续运行相同的测试10次,您会发现数字是不同的。非常接近,但不是相同的。 - Ronny Sherer
    @RonnySherer js perf 创建了一个虚拟环境,特别考虑到那些微小差异的进程。它不在我的电脑上运行,只运行那个。当某些东西如此微小时,也许有人不应该在意。但是,我仍然正确计算并报告它。如果有人想在数十亿次迭代的循环中使用它,则应选择性能最佳的函数。 - Panos Kalatzantonakis
    1
    虚拟环境在服务器上,可能会执行其他任务。我做了一些测试,结果从未完全相同。 - Ronny Sherer

    14

    如果您使用这些函数来创建对象,则会得到:

    var objectOne = new functionOne();
    console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function
    
    var objectTwo = new functionTwo();
    console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function
    

    14

    命名函数与匿名函数

    第一个函数语法是匿名函数表达式

    var functionOne = function() {
      // do something...
    };
    

    第二种是函数声明

    function functionTwo () {
      // do something...
    }
    

    两者之间的主要区别在于函数名称,因为匿名函数没有名称可供调用。

    匿名函数声明快捷方便,许多库和工具倾向于鼓励这种惯用代码风格。然而,匿名函数也有一些缺点:

    • 可读性: 匿名函数省略了一个名称,可能导致代码难以阅读。

    • 调试: 栈跟踪中没有匿名函数的名称,这可能会使调试更加困难。

    • 自我引用: 如果函数需要引用自身,例如递归,该怎么办。

    命名函数表达式

    为您的函数表达式提供名称可以很好地解决所有这些缺点,并且没有任何实质性的不利影响。最佳实践是始终为您的函数表达式命名:

    setTimeout(function timeHandler() { // <-- look, a name here!
      console.log("I've waited 1 second");
    }, 1000);
    

    IIFE(立即调用函数表达式)的命名

    (function IIFE(str) { // <-- look, always name IIFEs!
      console.log(str); // "Hello!"
    })('Hello!');
    

    对于分配给变量的函数,命名函数在这种情况下不太常见,可能会导致混淆,在这种情况下,箭头函数可能是更好的选择。


    13
    在JavaScript中,有两种方法可以创建函数:
    1. Function declaration:

      function fn(){
        console.log("Hello");
      }
      fn();
      

      This is very basic, self-explanatory, used in many languages and standard across C family of languages. We declared a function defined it and executed it by calling it.

      What you should be knowing is that functions are actually objects in JavaScript; internally we have created an object for above function and given it a name called fn or the reference to the object is stored in fn. Functions are objects in JavaScript; an instance of function is actually an object instance.

    2. Function expression:

      var fn=function(){
        console.log("Hello");
      }
      fn();
      

      JavaScript has first-class functions, that is, create a function and assign it to a variable just like you create a string or number and assign it to a variable. Here, the fn variable is assigned to a function. The reason for this concept is functions are objects in JavaScript; fn is pointing to the object instance of the above function. We have initialized a function and assigned it to a variable. It's not executing the function and assigning the result.

    参考资料:JavaScript函数声明语法:var fn = function() {} vs function fn() {}


    12

    考虑到“堆栈跟踪中显示命名函数”的论点,现代JavaScript引擎实际上能够很好地表示匿名函数。

    截至本文撰写时,V8、SpiderMonkey、Chakra和Nitro总是按其名称引用命名函数。如果匿名函数有标识符,则它们几乎总是按其标识符引用。

    SpiderMonkey可以找出从其他函数返回的匿名函数的名称。而其他引擎则无法做到这一点。

    如果您真的非常希望您的迭代器和成功回调在跟踪中显示,您也可以给它们取个名字...

    [].forEach(function iterator() {});
    

    但大部分情况下,这并不值得太过紧张。

    利用 (演示)

    'use strict';
    
    var a = function () {
        throw new Error();
    },
        b = function b() {
            throw new Error();
        },
        c = function d() {
            throw new Error();
        },
        e = {
            f: a,
            g: b,
            h: c,
            i: function () {
                throw new Error();
            },
            j: function j() {
                throw new Error();
            },
            k: function l() {
                throw new Error();
            }
        },
        m = (function () {
            return function () {
                throw new Error();
            };
        }()),
        n = (function () {
            return function n() {
                throw new Error();
            };
        }()),
        o = (function () {
            return function p() {
                throw new Error();
            };
        }());
    
    console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
        return values.concat(e[key]);
    }, [])).concat([m, n, o]).reduce(function (logs, func) {
    
        try {
            func();
        } catch (error) {
            return logs.concat('func.name: ' + func.name + '\n' +
                               'Trace:\n' +
                               error.stack);
            // Need to manually log the error object in Nitro.
        }
    
    }, []).join('\n\n'));
    

    V8

    func.name: 
    Trace:
    Error
        at a (http://localhost:8000/test.js:4:11)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: b
    Trace:
    Error
        at b (http://localhost:8000/test.js:7:15)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: d
    Trace:
    Error
        at d (http://localhost:8000/test.js:10:15)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: 
    Trace:
    Error
        at a (http://localhost:8000/test.js:4:11)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: b
    Trace:
    Error
        at b (http://localhost:8000/test.js:7:15)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: d
    Trace:
    Error
        at d (http://localhost:8000/test.js:10:15)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: 
    Trace:
    Error
        at e.i (http://localhost:8000/test.js:17:19)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: j
    Trace:
    Error
        at j (http://localhost:8000/test.js:20:19)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: l
    Trace:
    Error
        at l (http://localhost:8000/test.js:23:19)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: 
    Trace:
    Error
        at http://localhost:8000/test.js:28:19
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: n
    Trace:
    Error
        at n (http://localhost:8000/test.js:33:19)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27
    
    func.name: p
    Trace:
    Error
        at p (http://localhost:8000/test.js:38:19)
        at http://localhost:8000/test.js:47:9
        at Array.reduce (native)
        at http://localhost:8000/test.js:44:27 test.js:42
    

    SpiderMonkey


    蜘蛛猴(SpiderMonkey)
    func.name: 
    Trace:
    a@http://localhost:8000/test.js:4:5
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: b
    Trace:
    b@http://localhost:8000/test.js:7:9
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: d
    Trace:
    d@http://localhost:8000/test.js:10:9
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: 
    Trace:
    a@http://localhost:8000/test.js:4:5
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: b
    Trace:
    b@http://localhost:8000/test.js:7:9
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: d
    Trace:
    d@http://localhost:8000/test.js:10:9
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: 
    Trace:
    e.i@http://localhost:8000/test.js:17:13
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: j
    Trace:
    j@http://localhost:8000/test.js:20:13
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: l
    Trace:
    l@http://localhost:8000/test.js:23:13
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: 
    Trace:
    m</<@http://localhost:8000/test.js:28:13
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: n
    Trace:
    n@http://localhost:8000/test.js:33:13
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    
    
    func.name: p
    Trace:
    p@http://localhost:8000/test.js:38:13
    @http://localhost:8000/test.js:47:9
    @http://localhost:8000/test.js:54:1
    

    Chakra

    func.name: undefined
    Trace:
    Error
       at a (http://localhost:8000/test.js:4:5)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at b (http://localhost:8000/test.js:7:9)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at d (http://localhost:8000/test.js:10:9)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at a (http://localhost:8000/test.js:4:5)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at b (http://localhost:8000/test.js:7:9)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at d (http://localhost:8000/test.js:10:9)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at e.i (http://localhost:8000/test.js:17:13)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at j (http://localhost:8000/test.js:20:13)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at l (http://localhost:8000/test.js:23:13)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at Anonymous function (http://localhost:8000/test.js:28:13)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at n (http://localhost:8000/test.js:33:13)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    
    
    func.name: undefined
    Trace:
    Error
       at p (http://localhost:8000/test.js:38:13)
       at Anonymous function (http://localhost:8000/test.js:47:9)
       at Global code (http://localhost:8000/test.js:42:1)
    

    Nitro

    func.name: 
    Trace:
    a@http://localhost:8000/test.js:4:22
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: b
    Trace:
    b@http://localhost:8000/test.js:7:26
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: d
    Trace:
    d@http://localhost:8000/test.js:10:26
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: 
    Trace:
    a@http://localhost:8000/test.js:4:22
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: b
    Trace:
    b@http://localhost:8000/test.js:7:26
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: d
    Trace:
    d@http://localhost:8000/test.js:10:26
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: 
    Trace:
    i@http://localhost:8000/test.js:17:30
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: j
    Trace:
    j@http://localhost:8000/test.js:20:30
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: l
    Trace:
    l@http://localhost:8000/test.js:23:30
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: 
    Trace:
    http://localhost:8000/test.js:28:30
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: n
    Trace:
    n@http://localhost:8000/test.js:33:30
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    
    func.name: p
    Trace:
    p@http://localhost:8000/test.js:38:30
    http://localhost:8000/test.js:47:13
    reduce@[native code]
    global code@http://localhost:8000/test.js:44:33
    

    12

    两者都是定义函数的不同方式。不同之处在于浏览器如何解释它们并将它们加载到执行上下文中。

    第一种情况是函数表达式,它只有在解释器达到那行代码时才会加载。所以如果你像下面这样做,你会得到一个错误,显示functionOne不是一个函数。

    functionOne();
    var functionOne = function() {
        // Some code
    };
    

    原因是在第一行没有给functionOne分配任何值,因此它是未定义的。我们试图将其作为函数调用,因此出现了错误。

    在第二行,我们将匿名函数的引用赋给functionOne。

    第二种情况是函数声明,在执行任何代码之前加载。因此,如果您像以下方式那样操作,您将不会收到任何错误,因为声明在代码执行之前加载。

    functionOne();
    function functionOne() {
       // Some code
    }
    

    11
    They are pretty similar with some small differences, first one is a variable which assigned to an anonymous function (Function Declaration) and second one is the normal way to create a function in JavaScript(Anonymous function Declaration), both has usage, cons and pros:
    1. Function Expression
    var functionOne = function() {
        // Some code
    };
    

    函数表达式定义了一个函数作为更大表达式语法的一部分(通常是变量赋值)。通过函数表达式定义的函数可以具有名称或匿名。函数表达式不能以“function”开头(因此在下面的自调用示例周围使用括号)。

    将一个变量赋值给一个函数意味着没有提升,我们知道 JavaScript 中的函数可以提升,也就是说它们可以在声明之前被调用,而变量需要在访问它们之前声明,因此在这种情况下我们无法在其声明之前访问该函数,同时这可能是你编写函数的一种方式,对于返回另一个函数的函数,这种声明方式可能是有意义的,在 ECMA6 及以上版本中,您可以将 this 分配给箭头函数,这可以用于调用匿名函数,此声明方式也是创建 JavaScript 构造函数的更好方式。

    2. 函数声明

    function functionTwo() {
        // Some code
    }
    

    一个函数声明定义了一个命名的函数变量,而不需要变量赋值。函数声明作为独立的结构出现,不能嵌套在非函数块内部。它们与变量声明类似,可以将它们视为变量声明的兄弟。就像变量声明必须以“var”开头一样,函数声明必须以“function”开头。
    这是在JavaScript中调用函数的常规方式,即使在声明之前也可以调用此函数,因为在JavaScript中所有函数都会被提升,但如果使用'use strict',则无法预期提升,这是调用所有普通函数的好方法,这些函数行数不多,也不是构造函数。
    另外,如果您需要更多关于JavaScript hoisting如何工作的信息,请访问下面的链接:

    https://developer.mozilla.org/en-US/docs/Glossary/Hoisting


    8

    这只是声明函数的两种可能方式,而在第二种方式中,您可以在声明之前使用该函数。


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