为什么在JavaScript中总是要声明函数?

5

我听说在JavaScript中,必须要声明函数。这是真的吗?这样做有什么好处呢?

通常我会像这样将函数赋值给一个变量:

var foo = function() {};

据说这是错误的,可能与堆栈跟踪有关。可以有人解释一下吗? 我被告知要执行以下操作:
var foo = function fooBar() {};

这句话的意思是:如果把它赋给一个对象,这样做是否有意义?
var Foo = {};
Foo.Bar = function Bar() {};

请注意,这些函数不在全局范围内,也不是自我执行的,也不会被多次使用。

4
可能是重复的问题:var functionName = function() {} vs function functionName() {} - Mike Robinson
1
似乎你的问题是关于给函数命名。是的,这有助于堆栈跟踪,这样你就不会仅仅看到一堆“匿名函数”条目了。 - JAL
@MikeRobinson,那个问题更多地涉及到提升。我知道这两个函数之间的区别。请重新阅读问题。 - DevNoob
@JAL,你能否详细说明一下吗? - DevNoob
1
请注意:如果您需要支持<= IE8,命名函数表达式可能会引起很多麻烦。这已经有很好的文档记录了,但这是一个很好的总结:http://kangax.github.io/nfe/#jscript-bugs - Jesse Kernaghan
显示剩余2条评论
1个回答

3

首先,有一点需要注意的是:你所拥有的不是一个函数声明。下面是一个函数声明的例子:

function fooBar() {
}

它创建了一个名为 fooBar 的函数,并通过变量 fooBar 进行访问。这是一个使用命名函数表达式进行的赋值操作。
var foo = function fooBar() {
};

该函数的名称仍然是fooBar,但该函数仅在函数本身内部绑定到fooBar变量,而不是外部。虽然它确实使函数在其作用域内可访问,而无需引用外部作用域中的变量,但这意味着有两个原因需要命名它:

  • For the ability to refer to the function inside itself regardless of code in the outer function!

    This can return whatever the outer function wants:

    function fooBar() {
        return fooBar.toString();
    }
    
    var baz = fooBar;
    fooBar = 5;
    baz(); // "5"
    

    This is always consistent:

    var fooBar = function fooBar() {
        return fooBar.toString();
    };
    
    var baz = fooBar;
    fooBar = 5;
    baz(); // "function fooBar() { …"
    
  • And yes, for a more detailed stack trace:

    function trace(func) {
        try {
            func();
        } catch (error) {
            console.log(error.stack);
        }
    }
    
    trace(function () {
        throw new Error("Bad thing");
    });
    /*
    Error: Bad thing
        at /home/ryan/test.js:10:18
        at trace (/home/ryan/test.js:3:16)
        at Object.<anonymous> (/home/ryan/test.js:9:8)
        at Module._compile (module.js:410:26)
        at Object.Module._extensions..js (module.js:428:10)
        at Module.load (module.js:335:32)
        at Function.Module._load (module.js:290:12)
        at Function.Module.runMain (module.js:451:10)
        at startup (node.js:123:18)
        at node.js:866:3
    */
    
    trace(function descriptiveName() {
        throw new Error("Bad thing");
    });
    /*
    Error: Bad thing
        at descriptiveName (/home/ryan/test.js:14:18)
        at trace (/home/ryan/test.js:3:16)
        at Object.<anonymous> (/home/ryan/test.js:13:8)
        at Module._compile (module.js:410:26)
        at Object.Module._extensions..js (module.js:428:10)
        at Module.load (module.js:335:32)
        at Function.Module._load (module.js:290:12)
        at Function.Module.runMain (module.js:451:10)
        at startup (node.js:123:18)
        at node.js:866:3
    */
    

    (Node.js pictured here.) Note the descriptiveName at the top of the second stack trace. This is especially convenient when you have slightly complicated systems of asynchronous callbacks, events, methods on objects that are passed around, and so on.


你可以使用IIFE来描述代码块的作用。例如:(function applyFixIfNeeded() { //fix in here }());,而不是像下面的代码那样使用注释//This applies a fix。我个人更喜欢在底部声明函数并在顶部调用它们,但我过去也使用过这种形式。 - plalx

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