JS中(function(){xxx})(); 这种写法是什么意思?
(function(){
function a(){
   alert("a");
}
})();


自执行匿名函数:


常见格式:(function() { /* code */ })();

解释:包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数。

作用:可以用它创建命名空间,只要把自己所有的代码都写在这个特殊的函数包装内,那么外部就不能访问,除非你允许(变量前加上window,这样该函数或变量就成为全局)。各JavaScript库的代码也基本是这种组织形式。

总结一下,执行函数的作用主要为 匿名 和 自动执行,代码在被解释时就已经在运行了。


其他写法

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

!function () { /* code */ } ();

~function () { /* code */ } ();

-function () { /* code */ } ();

+function () { /* code */ } ();


========================================

========================================

========================================

前言

大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行。

在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法也不一定完全对,主要是看个人如何理解,因为有的人说立即调用,有的人说自动执行,所以你完全可以按照你自己的理解来取一个名字,不过我听很多人都叫它为“自执行”,但作者后面说了很多,来说服大家称呼为“立即调用的函数表达式”。

本文英文原文地址:http://benalman.com/news/2010/11/immediately-invoked-function-expression/

什么是自执行?

在JavaScript里,任何function在执行的时候都会创建一个执行上下文,因为为function声明的变量和function有可能只在该function内部,这个上下文,在调用function的时候,提供了一种简单的方式来创建自由变量或私有子function。

 makeCounter() {
         i = 0;

      () {
        console.log(++i);
    };
}


 counter = makeCounter();
counter(); counter(); 
 counter2 = makeCounter();
counter2(); counter2(); 
alert(i);

很多情况下,我们不需要makeCounter多个实例,甚至某些case下,我们也不需要显示的返回值,OK,往下看。

 

问题的核心

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

 
 foo = (){  }
 
 
(){  }();

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

旁白:函数(function),括弧(paren),语法错误(SyntaxError)

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

 
 foo(){  }();  
 foo(){  }( 1 );
 
 foo(){  }
 
( 1 );

你可以访问ECMA-262-3 in detail. Chapter 5. Functions 获取进一步的信息。

自执行函数表达式

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

( () {  } ()); ( () {  })(); 

 i =  () {  10; } ();
 &&  () {  } ();
0,  () {  } ();


! () {  } ();
~ () {  } ();
- () {  } ();
+ () {  } ();


  () {  }
  () {  } ()

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

用闭包保存状态

和普通function执行的时候传参数一样,自执行的函数表达式也可以这么传参,因为闭包直接可以引用传入的这些参数,利用这些被lock住的传入参数,自执行函数表达式可以有效地保存状态。

 elems = document.getElementsByTagName('a');

 ( i = 0; i < elems.length; i++) {

    elems[i].addEventListener('click',  (e) {
        e.preventDefault();
        alert('I am link #' + i);
    }, 'false');

}


 elems = document.getElementsByTagName('a');

 ( i = 0; i < elems.length; i++) {

    ( (lockedInIndex) {

        elems[i].addEventListener('click',  (e) {
            e.preventDefault();
            alert('I am link #' + lockedInIndex);
        }, 'false');

    })(i);

}


 elems = document.getElementsByTagName('a');

 ( i = 0; i < elems.length; i++) {

    elems[i].addEventListener('click', ( (lockedInIndex) {
          (e) {
            e.preventDefault();
            alert('I am link #' + lockedInIndex);
        };
    })(i), 'false');

}

其实,上面2个例子里的lockedInIndex变量,也可以换成i,因为和外面的i不在一个作用于,所以不会出现问题,这也是匿名函数+闭包的威力。

自执行匿名函数和立即执行的函数表达式区别

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

 foo() { foo(); }

 foo =  () { arguments.callee(); };

 foo =  () { foo(); };

( () {  } ());

( foo() {  } ());

( () { arguments.callee(); } ());
( foo() { foo(); } ());

( foo() { foo(); } ());

希望这里的一些例子,可以让大家明白,什么叫自执行,什么叫立即调用。

注:arguments.callee在ECMAScript 5 strict mode里被废弃了,所以在这个模式下,其实是不能用的。

最后的旁白:Module模式

在讲到这个立即调用的函数表达式的时候,我又想起来了Module模式,如果你还不熟悉这个模式,我们先来看看代码:

 counter = ( () {
     i = 0;

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


counter.get(); counter.set(3);
counter.increment(); counter.increment(); 
counter.i; i;

关于更多Module模式的介绍,请访问我的上一篇帖子:深入理解JavaScript系列(2):全面解析Module模式 。

更多阅读

希望上面的一些例子,能让你对立即调用的函数表达(也就是我们所说的自执行函数)有所了解,如果你想了解更多关于function和Module模式的信息,请继续访问下面列出的网站:

  1. ECMA-262-3 in detail. Chapter 5. Functions. - Dmitry A. Soshnikov

  2. Functions and function scope - Mozilla Developer Network

  3. Named function expressions - Juriy “kangax” Zaytsev

  4. 全面解析Module模式- Ben Cherry(大叔翻译整理)

  5. Closures explained with JavaScript - Nick Morgan

同步与推荐

本文已同步至目录索引:深入理解JavaScript系列

深入理解JavaScript系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。


修改时间 2016-09-04

真诚赞赏,手留余香
赞赏
随机推荐
Mac配置环境变量的地方
MySQL数据库日志处理
学之者生,用之者死——ACE历史与简评
子元素margin-top对父元素的影响
手绘板如何画好线条
PDO使用参数绑定LIKE时,需要用CONCAT拼接
20170118 数位板 进步
vim 教程
JS事件冒泡与捕获、addEventListener--实例演示
Windows 下 Redis 安装配置 PHP 模块