学习js函数--自执行函数

Posted 铸剑师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习js函数--自执行函数相关的知识,希望对你有一定的参考价值。

我在写代码时候经常会在tpl的<script>里写类似的代码:

$(function(){
    alert("我好饿");
});

刚开始的时候只知道写了它不需要调用,直接执行,就这样依葫芦画瓢,我写了很多代码。说道这,还要说说这货的加载顺序,如果把代码直接写到script标签里,当页面加载完这个script标签就会执行里边的代码了。如果在这代码里用到了未加载的dom或者调用了未加载的方法,是会报错的。言归正传,这个函数其实就是自执行函数,很多人会比较专业地称为“立即调用的函数表达式”。

在这里,我引用下limlimlim的博客,感觉比较专业...原址http://blog.csdn.net/limlimlim/article/details/9198111

当你声明类似function foo(){}或var foo = function(){}函数的时候,通过在后面加个括弧就可以实现自执行,例如foo(),看代码:

// 因为想下面第一个声明的function可以在后面加一个括弧()就可以自己执行了,比如foo(),
// 因为foo仅仅是function() { /* code */ }这个表达式的一个引用
 
var foo = function(){ /* code */ }
 
// ...是不是意味着后面加个括弧都可以自动执行?
 
function(){ /* code */ }(); // SyntaxError: Unexpected token (
//

上述代码,如果甚至运行,第2个代码会出错,因为在解析器解析全局的function或者function内部function关键字的时候,默认是认为function声明,而不是function表达式,如果你不显示告诉编译器,它默认会声明成一个缺少名字的function,并且抛出一个语法错误信息,因为function声明需要一个名字。

有趣的是,即便你为上面那个错误的代码加上一个名字,他也会提示语法错误,只不过和上面的原因不一样。在一个表达式后面加上括号(),该表达式会立即执行,但是在一个语句后面加上括号(),是完全不一样的意思,他的只是分组操作符。

// 下面这个function在语法上是没问题的,但是依然只是一个语句
// 加上括号()以后依然会报错,因为分组操作符需要包含表达式
 
function foo(){ /* code */ }(); // SyntaxError: Unexpected token )
 
// 但是如果你在括弧()里传入一个表达式,将不会有异常抛出
// 但是foo函数依然不会执行
function foo(){ /* code */ }( 1 );
 
// 因为它完全等价于下面这个代码,一个function声明后面,又声明了一个毫无关系的表达式: 
function foo(){ /* code */ }
 
( 1 );

要解决上述问题,非常简单,我们只需要用大括弧将代码的代码全部括住就行了,因为javascript里括弧()里面不能包含语句,所以在这一点上,解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明。

// 下面2个括弧()都会立即执行

(function () { /* code */ } ()); // 推荐使用这个
(function () { /* code */ })(); // 但是这个也是可以用的

// 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的
// 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了
// 不过,请注意下一章节的内容解释

var i = function () { return 10; } ();
true && function () { /* code */ } ();
0, function () { /* code */ } ();

// 如果你不在意返回值,或者不怕难以阅读
// 你甚至可以在function前面加一元操作符号

!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();

// 还有一个情况,使用new关键字,也可以用,但我不确定它的效率
// http://twitter.com/kuvos/status/18209252090847232

new function () { /* code */ }
new function () { /* code */ } () // 如果需要传递参数,只需要加上括弧()

上面所说的括弧是消除歧义的,其实压根就没必要,因为括弧本来内部本来期望的就是函数表达式,但是我们依然用它,主要是为了方便开发人员阅读,当你让这些已经自动执行的表达式赋值给一个变量的时候,我们看到开头有括弧(,很快就能明白,而不需要将代码拉到最后看看到底有没有加括弧。

 

在这篇帖子里,我们一直叫自执行函数,确切的说是自执行匿名函数(Self-executing anonymous function),但英文原文作者一直倡议使用立即调用的函数表达式(Immediately-Invoked Function Expression)这一名称,作者又举了一堆例子来解释,好吧,我们来看看:

// 这是一个自执行的函数,函数内部执行自身,递归
function foo() { foo(); }

// 这是一个自执行的匿名函数,因为没有标示名称
// 必须使用arguments.callee属性来执行自己
var foo = function () { arguments.callee(); };

// 这可能也是一个自执行的匿名函数,仅仅是foo标示名称引用它自身
// 如果你将foo改变成其它的,你将得到一个used-to-self-execute匿名函数
var foo = function () { foo(); };

// 有些人叫这个是自执行的匿名函数(即便它不是),因为它没有调用自身,它只是立即执行而已。
(function () { /* code */ } ());

// 为函数表达式添加一个标示名称,可以方便Debug
// 但一定命名了,这个函数就不再是匿名的了
(function foo() { /* code */ } ());

// 立即调用的函数表达式(IIFE)也可以自执行,不过可能不常用罢了
(function () { arguments.callee(); } ());
(function foo() { foo(); } ());

// 另外,下面的代码在黑莓5里执行会出错,因为在一个命名的函数表达式里,他的名称是undefined
// 呵呵,奇怪
(function foo() { foo(); } ());

希望这里的一些例子,可以让大家明白,什么叫自执行,什么叫立即调用。(我到这也才理解了一点,理论万岁)

下面 limlimlim还讲了Module模式,这是我之后要学习的重要东西,当然在这之前还有闭包(头大)

给各位看看例子吧

// 创建一个立即调用的匿名函数表达式
// return一个变量,其中这个变量里包含你要暴露的东西
// 返回的这个变量将赋值给counter,而不是外面声明的function自身

var counter = (function () {
    var i = 0;

    return {
        get: function () {
            return i;
        },
        set: function (val) {
            i = val;
        },
        increment: function () {
            return ++i;
        }
    };
} ());

// counter是一个带有多个属性的对象,上面的代码对于属性的体现其实是方法

counter.get(); // 0
counter.set(3);
counter.increment(); // 4
counter.increment(); // 5

counter.i; // undefined 因为i不是返回对象的属性
i; // 引用错误: i 没有定义(因为i只存在于闭包)

 

以上是关于学习js函数--自执行函数的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——声明函数

VSCode自定义代码片段8——声明函数

js自执行函数&扩展方法

JS 函数学习

JavaScript自执行函数和jquery扩展方法

js 自执行匿名函数(转载)