贾宝祺 發表於 2025-7-1 08:06:00

javascript中IIFE(立即执行函数表达式)到底是咋来的?

<h2 id="介绍">介绍</h2>
<p>IIFE(Immediately Invoked Function Expression),中文名称:立即执行函数表达式,其实IIFE最早并不叫这个名字,而是叫做<code>Self-Executing Anonymous Function</code>,即自执行匿名函数。根据MDN的资料,IIFE这个说法最早由Ben Alman于2010年提出,下面我们一起来看看这个名字的来龙去脉。</p>
<p>2010年11月5日,Ben Alman写下来他的著名文章:Immediately-Invoked Function Expression (IIFE),标志着IIFE这个名字的诞生。</p>
<p>在文章中,Ben Alman称他是一个对待术语非常严谨的人,之前他多次看到<code>Self-Executing Anonymous Function</code>这个说法,觉得不是很恰当,于是他提出了<code>Immediately-Invoked Function Expression</code>这个说法。</p>
<h2 id="iife到底是咋来的">IIFE到底是咋来的?</h2>
<p>当我们定义一个函数或者一个函数表达式时,你得到的是一个名字,通过这个名字,你就可以调用这个函数。</p>
<p>下面这两段代码,第一个定义了一个普通函数,第二个定义了一个函数表达式,这两种形式,我们都可以通过标识符<code>foo</code>来调用它们。</p>
<pre><code class="language-javascript">// 普通函数
function foo() {
console.log('I am a function');
}

// 函数表达式
const foo = function() {
console.log('I am a function expression');
};
</code></pre>
<p>也就是说,当<code>javascript</code>解释器遇到全局<code>function</code>关键字,或者一个函数内部的<code>function</code>关键字时,会将其解释为一个函数声明。</p>
<p>然而函数声明是无法直接调用的,所以下面的写法会导致错误:</p>
<pre><code class="language-javascript">function foo() {
console.log('I am a function'); // Uncaught SyntaxError: Unexpected token ')'
}();
</code></pre>
<p>我们来分析一下,上面这段代码,javascript解释器会将其解释为一个函数声明,和一个分组操作符(<code>()</code>), 分组操作符是用来改变运算符优先级的,里面必须有表达式才行,所以javascript解释器会报错。</p>
<p>那我们就给它一个表达式:</p>
<pre><code class="language-javascript">function foo() {
console.log('I am a function'); // Uncaught SyntaxError: Unexpected token ')'
}(1);
</code></pre>
<p>这回代码不报错了,但是这段代码毫无意义,这个函数并没有执行,实际上这段代码与下面的代码等价:</p>
<pre><code class="language-javascript">function foo() {
console.log('I am a function');
}

(1);
</code></pre>
<p>它的返回值就是1,这不是我们想要的结果,我们需要函数定义后能立即被执行,那就需要我们告诉javascript解释器,这个函数是一个表达式,而不是一个声明,因为表达式可以立即执行,但是声明不能。</p>
<p>而在javascript中,生成表达式最简单的方式就是用<code>()</code>包裹起来,于是有了下面的代码</p>
<pre><code class="language-javascript">(function foo() {
console.log('I am a function');
});
</code></pre>
<p>这样函数声明就变成了一个函数表达式,但是这个表达式没有名字,我们没法调用它,我们先给它一个名字,然后通过名字调用它。</p>
<pre><code class="language-javascript">const bar = (function foo() {
console.log('I am a function');
});

bar(); // I am a function
</code></pre>
<p>这样完全没有问题,但是这里的<code>bar</code>实在有点多余,实际上<code>bar</code>和</p>
<pre><code class="language-javascript">(function foo() {
console.log('I am a function');
});
</code></pre>
<p>是等价的,既然<code>bar()</code>可以调用函数,那么我们直接在函数表达式末尾加上<code>()</code>,也可以调用这个函数,于是就有了下面的代码,这就是IIFE的由来。</p>
<pre><code class="language-javascript">(function foo() {
console.log('I am a function');
})();
</code></pre>
<p>将<code>()</code>写在外层的括号内也一样,这种方式颇得javascript专家Douglas Crockford的青睐。我本人更喜欢第一种。</p>
<pre><code class="language-javascript">(function() {
console.log('I am a function');
}());
</code></pre>
<h2 id="iife的变种">IIFE的变种</h2>
<p>由上面介绍可知,生成IIFE的精髓就是<code>将函数声明变成函数表达式</code>,而在javascript中,生成表达式可不止使用<code>()</code>包裹起来这一种方式,还有很多其他的方式可以实现。于是IIFE也就是产生了诸多变种。</p>
<p>这个变种利用赋值运算符<code>=</code>来实现,赋值运算符是将右侧表达式的值赋值给左侧变量的,所以它右侧的部分被解析成了函数表达式及其调用。</p>
<pre><code class="language-javascript">const i = function() {
console.log('I am an IIFE');
}();
</code></pre>
<p>下面的表中使用逻辑运算符来生成表达式。</p>
<pre><code class="language-javascript">true &amp; (function() {
console.log('I am an IIFE');
}());
</code></pre>
<p>还有下面这些,都是利用一元运算符来生成函数表达式。</p>
<pre><code class="language-javascript">!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();
</code></pre>
<p>最后来一个不为人知的,<code>void</code>运算符会对其右侧的表达是求值然后返回<code>undefined</code>。(<code>void expression</code> - 先对<code>expression</code>求值,然后返回<code>undefined</code>)。</p>
<pre><code class="language-javascript">void function() {
console.log('I am an IIFE');
}();
</code></pre>
<p>还有使用<code>new</code>运算符来生成IIFE,这种方式比较少见,因为它会创建一个新的对象。</p>
<pre><code class="language-javascript">new function() {
console.log('I am an IIFE');
}();
</code></pre>
<p>这些方式都比较偏门了,不建议使用,只是用来帮助我们理解IIFE的。</p>
<h2 id="为什么self-executing-anonymous-function这个名字不好">为什么Self-Executing Anonymous Function这个名字不好?</h2>
<p><code>Ben Alman</code>认为这个名字有两个问题:</p>
<p><strong>Self-Executing</strong>:这个名字暗示函数会调用自己,但是实际上函数是立即被执行的,而不是调用它自身。<br>
比如下面的几段代码都会调用自己,但是这并不是IIFE的语义。</p>
<pre><code class="language-javascript">// 递归调用自身
function foo() { foo();

// 使用arguments.callee调用自身
const foo = function() { arguments.callee(); };
</code></pre>
<p><strong>Anonymous</strong>:这个名字暗示函数是匿名的,但实际上函数可以有名字,也可以没有名字,比如下面的例子:</p>
<pre><code class="language-javascript">// 有名字的IIFE
(function foo() {
console.log('I am an IIFE');
})();
</code></pre>
<h2 id="参考">参考</h2>
<p>https://web.archive.org/web/20171201033208/http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife</p>
<p>大家有时间可以去拜读<code>Ben Alman</code>的原文,大佬写的东西就是不一样,通俗易懂,是我辈楷模!</p>


</div>
<div id="MySignature" role="contentinfo">
    <div id="ZddSignature">
<div>作者:zdd</div>
<div>出处:http://www.cnblogs.com/graphics/
<div>
<div>本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.</div>
</div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/graphics/p/18959048
頁: [1]
查看完整版本: javascript中IIFE(立即执行函数表达式)到底是咋来的?