JavaScript函数定义解释

3

有人能为我解释一下JS文件中的以下函数吗?

function Init()
{
  (function set()
     {
       -----code---
       print "hello";
      }
   )();
}

如果我调用Init函数,它会自动运行设置为函数的set()函数吗?还是我需要调用set()来运行它?

是的,因为它是一个IIFE,在您的代码周围定义了作用域。在您的情况下,这可能没有什么区别,因为Init已经创建了作用域。 - Blauharley
4个回答

1

你需要调用 Init 来运行其中的任何代码。现在,里面的代码只是普通的 JavaScript 代码,但是它很有趣。让我们看看为什么。


如何

你的内部函数被称为所谓的IIFE。为了使外部函数(Init)更易读,你可以用IIFE调用的结果替换内部函数。

以下是一些示例:

var x = undefined; // x === undefined
x = (undefined); // again
x = 3;      // x === 3
x = (3);   // again the same
x = 'some string'; // some string
x = ('some string'); // the same

所以,在对象周围放置()是可以的,你会得到相同的对象。对于函数也是一样。

x = function() {};   // x is that function now.
x = (function() {}); // the same.

但如果你说 x = 3;,你能调用 x 吗?

x = 3;
x(); // syntax error
x = (3);
x(); // again error. can't call a number.

但是,如果你的 x 是一个函数,你可以调用它:
x = function() { console.log('Hi'); }; // you can call x now.
x(); // logs 'Hi'!

如果你使用括号做同样的事情,结果是相同的:

x = (function() { console.log('Hi'); }); // again, the same.
x(); // no error, function called again!

所以你可以跳过作业部分:
// instead of x = (...); and the x(), you replace the x part right away.

(function() { console.log('Hi'); })()
// function body       up to here ^^ now the call part.

请注意,如果没有第一对括号,它将无法正常工作:
function() { console.log('Hi'); }(); // syntax error.

所以你将外部括号()放在那个函数表达式周围,只是为了将其转换为一个对象。

为什么

这就是它的工作原理。 但为什么? 因为你想要确保没有其他人调用你的函数!如果你这样做:

var x = function() {};
x();

现在有至少两件事情可能会发生,而你可能不想要:
  1. 可能会出现名称冲突。你可能不知道外部函数被调用时是否已经定义了“x”,例如,你将Init绑定到某个具有作用域x的对象上。
  2. 有人可能“窃取”你的x并重新调用它。不一定是窃取,你只是泄漏了它(一个微妙的错误),一些其他代码调用x,期望找到自己的x,但实际上,现在是你的x,因为它是可达的。而你不希望它变成这样。
所以你巧妙地将它锁定在立即执行的匿名函数中。
希望这能稍微澄清一些事情。

“所以你把外面的括号()放在那里,只是为了将函数定义转换为一个对象。” 实际上,外部括号将可能是函数声明的内容更改为函数表达式。可以使用任何允许在语句开头使用的标点符号,例如+-!,如!function(){}()。;-) - RobG
@RobG 太棒了!今天我学到了这个!function() { console.log('a'); }() https://twitter.com/zladuric/status/823632051572248577 - Zlatko
是的,比 (function(){}()) 少一个字符。;-) - RobG

0

你需要调用 Init(); 来执行该函数。

(function () {console.log('IIFE') }());

你所提到的语法是一个 立即调用函数表达式


还有一个拼写错误。 - Blauharley

0

这是 JavaScript 中的基本函数闭包(一个内部函数在另一个函数内部)。

此外...它是匿名函数...意思是...你只能使用该函数一次(这就是为什么函数调用附加到函数声明的原因)。所以...

function regular()
{
    function regular_closure()
    {

    }

    (function () // this is anonymous
    {

    })(); // call the anonymous function.. which is also a closure

    regular_closure(); // call the function
}

更多关于这个主题的内容:

MDN - JavaScript 闭包深入解析

何时使用 JavaScript 闭包


我认为称之为闭包是具有误导性的。每个函数都有一个闭包,即其外部执行上下文中的变量,而不仅仅是立即调用的函数。该函数在每次调用“regular”时都会执行,因此并不仅仅是使用一次,并且它是一个函数表达式,而不是声明。 - RobG
是的,这是一个闭包,但我认为问题并没有涉及到闭包本身。没有从闭包中访问任何内容,所以它并不是很相关。 - Zlatko
这就是为什么我附上了MDN链接的原因 :) - ymz
这个链接与闭包无关,我认为@RobG会同意。 - Zlatko

0

当你执行

(function set() { /*code here*/ })();

它是一个接近等价于的标签

var set = function () { /*code here*/ };
set();

因此,当您调用Init()函数时,将会调用set函数。


我会说它在技术上并不相等。在第一个示例中,在调用后当前作用域中没有剩余对象。在第二个示例中,你在调用上面和下面的代码中都有一个set变量(而且下面它甚至不是未定义的)。 - Zlatko
@Zlatko “在第一个示例中,当前作用域中没有剩余的对象”这是错误的。在第一个示例中,您可以在函数Init的任何位置调用set(),而不像第二个示例中只能在赋值后调用set()。即使在匿名函数的情况下,在Init作用域内部为其创建了一个内部名称,并且在它开始解释函数Init的第一行之前就已经创建了该名称。 - Dominique Fortin
不好意思,set 函数在外部函数作用域或 set 本身之外的任何地方都不可用。请查看此链接:https://gist.github.com/zladuric/dae9dc89297921f4f4023dee53e667fd或者我是否误解了您的意思? - Zlatko
1
@Zlatko 我错了。我在规范中找到了参考:......与FunctionDeclaration不同,FunctionExpression中的标识符不能从中引用,也不会影响包围FunctionExpression的作用域。 - Dominique Fortin

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