【问题标题】:Extra parentheses on function [duplicate]函数上的额外括号[重复]
【发布时间】:2012-08-01 12:19:19
【问题描述】:

可能重复:
What do parentheses surrounding a JavaScript object/function/class declaration mean?
What does this “(function(){});”, a function inside brackets, mean in javascript?
A Javascript function

我遇到了类似这样的标记:

var something = (function(){
    //do stuff
    return stuff;
})()
document.ondblclick = function(e) { alert(something(e)) };

我不明白something 变量中的开头( 和关闭)()。 你能解释一下这样写的区别吗?

var something = function(){
    //do stuff
    return stuff;
};

谢谢!

【问题讨论】:

  • () 调用该函数,其他括号是多余的。所以在第二种情况下,something 将被分配给函数,在第一种情况下,something 将被分配给stuff
  • 去谷歌,输入“JavaScript 闭包”并开始阅读 :)
  • 两者都是函数表达式。第一个看起来像是试图显式地使它成为一个函数表达式。例如,删除var something =,第一个仍然会运行。但是,当删除边缘的括号时,会抛出一个错误:“函数语句需要一个名称”(假设作者已经考虑过这个,当然!)。第一个立即被调用,所以something 引用了stuff 变量的值。后者something对函数的引用。调用时,它引用了stuff 变量当时的值。
  • @EliasVanOotegem:这取决于返回的stuff,参见Is the following JavaScript construct called a Closure?
  • @Esailija @RobW 意味着在第一种情况下,每次我使用 something 变量时都会执行该函数(因此每次都可能更改),但在第二种情况下,该函数只执行在变量声明中,something 总是返回相同的值?

标签: javascript closures


【解决方案1】:

所有答案都很好,但我认为最简单的答案已略过:

var something = (function(){
    //do stuff
    return stuff;
})()

此代码执行后,something 变为 stuff。返回内容的函数在分配内容之前执行。

var something = function(){
    //do stuff
    return stuff;
};

这段代码执行后,something一个返回stuff的函数。返回内容的函数从未执行过。

【讨论】:

  • 那是因为这已经在 cmets 中讨论过了 :P
【解决方案2】:

也请查看 JavaScript 常见问题解答部分:Here are some pretty good explanations and examples

好的,为什么要使用这个:

假设我的脚本正在运行,并且有几件事(例如,我正在循环一个节点列表)我以后可能需要。这就是为什么我可能会选择这样做:

for(var i=0;i<nodesList.lenght;i++)
{
    if (nodesList[i].id==="theOneINeed")
    {
        aClosure = (function(node,indexInNodesList)//assign here
        {
            return function()
            {
                node.style.display = 'none';//usable after the parent function returns
                alert(indexInNodesList+ ' is now invisible');
            }
        })(nodesList[i],i);//pass the element and its index as arguments here
        break;
    }
}
i = 99999;
aClosure();//no arguments, but it'll still work, and say i was 15, even though I've just 
//assigned another value to i, it'll alert '15 is now invisible'

这使我能够做的是防止函数参数被垃圾收集。通常,函数返回后,它的所有 var 和参数都会被 GC 处理。但是在这种情况下,该函数返回了另一个函数,该函数具有指向这些参数的链接(它需要它们),因此只要 aClosure 存在,它们就不会被 GC 处理。

正如我在评论中所说。谷歌闭包,练习一下,你就会明白了……它们真的很强大

【讨论】:

  • 您将闭包和自调用函数的概念混为一谈,这可能会让人很困惑。
  • 您是对的,但我只是想指出您可能想要使用它们的原因。关闭是首先想到的事情。特别是因为在 OP 提供的 sn-ps 中,返回值/对象是一个函数。对我来说看起来像一个关闭
【解决方案3】:

如果你把多余的括号去掉可能更容易理解,因为它们没有用处:

var something = function() {
    return 3;
} // <-- a function. 
(); // now invoke it and the result is 3 (because the return value is 3) assigned to variable called something 

console.log(something) //3 because the function returned 3 

var something = function() {
    return 3;
}; // a function is assigned to a variable called something

console.log(something) //logs the function body because it was assigned to a function
console.log(something()) //invoke the function assigned to something, resulting in 3 being logged to the console because that's what the function returns

【讨论】:

  • 接受了这个答案,因为它对我来说是最简单和最清楚的,但实际上所有答案都是正确的并且非常有用!谢谢!!
  • @Sean 非常感谢,我正要删除它,因为它没有得到支持:P
  • 我宁愿添加那些多余的括号。 JSLint 说:“将立即函数调用用括号括起来,以帮助读者理解表达式是函数的结果,而不是函数本身。”
  • @VainFellowman 是的,我担心同意克罗克福德的人会来这里发表评论。我不同意,因为如果结果只是函数本身,我只会做一个函数声明(function something(){})。声明更容易编写,您也可以免费获得一个命名函数。但这只是 JSLint 所说的一种观点。
  • @Esailija 好点。无论如何,拥有一个命名函数是一件好事,例如在调试中。我认为添加这些括号是一个好习惯,它不会花费额外的时间并且更容易理解。
【解决方案4】:

(function(){ ... })(anonymous) function expression,例如,您可以将该值分配给变量。

它后面的括号会立即执行函数表达式,产生函数的返回值(这里是:stuff)。该构造称为IIFE

stuff 是一个函数时(我假设这是因为您稍后调用something),这称为closure - 返回的函数(stuff,分配给something)仍然可以访问该匿名函数的执行上下文中的变量。

【讨论】:

    【解决方案5】:

    关于它的作用,请阅读所有 cmets 和其他答案。他们是绝对正确的。

    你为什么要使用它?你在使用闭包时经常会发现这种模式。以下代码 sn-p 的目的是为 10 个不同的 DOM 元素添加一个事件处理程序,每个元素都应该提醒它的 ID 属性(例如“你点击了 3”)。您应该知道,如果这是您的实际意图,那么有一种更简单的方法可以做到这一点,但出于学术原因,让我们坚持这种实现方式。

    var unorderedList = $( "ul" );
    for (var i = 0; i < 10; i++) {
        $("<li />", {
            id: i,
            text: "Link " + i,
            click: function() {
                console.log("You've clicked " + i);
            }
        }).appendTo( unorderedList );
    }
    

    上述代码的输出可能不是你最初期望的。每个点击处理程序的结果都是“你点击了 9”,因为在事件处理程序被触发时 i 的值是“9”。开发人员真正想要的是在定义事件处理程序的时间点显示 i 的值。

    为了修复上述错误,我们可以引入闭包。

    var unorderedList = $( "ul" ), i;
    
    for (i = 0; i < 10; i++) {
        $("<li />", {
            id: i,
            text: "Link " + i,
            click: function(index) {
                return function() {
                    console.log("You've clicked " + index);
                }
            }(i)
        }).appendTo( unorderedList );
    }
    

    您可以从 jsFiddle 执行和修改上述代码。

    修复上述代码的一种方法是利用自执行匿名函数。这是一个花哨的术语,意味着我们将创建一个无名函数,然后立即调用它。这种技术的价值在于变量的范围保持在函数内。因此,首先我们将事件处理程序内容包围在一个函数中,然后立即调用该函数并传入 i 的值。通过这样做,当事件处理程序被触发时,它将包含定义事件处理程序时存在的 i 值。

    关于闭包的进一步阅读:Use Cases for JavaScript Closures

    【讨论】:

    • 一句话简单的说。你真的需要加入不相关的 jQuery 语法来混淆 OP 吗?
    • 您在 cmets 中的回答当然是完全正确的(+1 顺便说一句)。这是一个简单用例的示例。而且我猜 jQuery 是广为人知的,所以在一个可能遇到的例子中使用它是有意义的。
    • 你说“为了修复上面的bug,我们可以引入一个闭包。”,但是你在第一个例子中分配给click的函数是a 已经关闭(它关闭了i)。使用闭包不是解决该问题的方法,进行函数调用并创建新范围是解决方案。这就是你在第二个例子中所做的,但你没有引入一个新的闭包(至少你没有像这样使用它)。
    • @Felix Kling 是的,你是对的。这也是一个封闭。正如您在评论中所说,通过使用自执行匿名函数,我们创建了一个新范围。
    猜你喜欢
    • 1970-01-01
    • 2015-04-05
    • 1970-01-01
    • 2019-02-13
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多