今天打开JQuery源文件(jquery-1.8.3), 看到JQuery的初始化过程是这样的

  1. (function( window, undefined ) {
  2. // ....
  3. })( window );

一开始看不懂这个写法, 经过几番搜索终于明白它的用法以及为什么这样用了, 我们一步步来分析.

1, 首先我们简化这个写法

除去参数, 经过简化后的写法可以写成

  1. (function(){
  2. console.log("Hello World");
  3. })();

后面都使用这个写法作为示例.

2, 函数声明与函数表达式

网上有许多介绍创建JavaScript函数的文章告诉我们创建JavaScript函数有两种方式: 函数声明与函数表达式.

  1. //function definition (also called function declaration)
  2. function func1() {
  3. console.log("Hello World1");
  4. }
  5. func1();
  6.  
  7. // function expression
  8. var func2 = function (){
  9. console.log("Hello World2");
  10. };
  11. func2();

第一种方式"函数定义"是标准的函数定义方式, 对函数func1的调用可以出现在函数定义之前;

第二种被称为"函数表达式", 与函数定义不同的是对函数func2的调用必须出现在变量func2之后的, 因为变量func2本质上是一个指向函数对象的变量, 这与我们定义普通变量的方式本质上是一样的; 另外一点是通过函数表达式创建的函数名可以不写, 我们称之为匿名函数.比如

  1. var a = 10;
  2. var f = function(){//code...};
    f(); // 调用函数f

这里我们将变量f指向赋值运算符右边所创建的匿名函数, 然后就可以通过f()直接去调用这个匿名函数了, 那么, 问题来了, 我们是不是可以直接在创建好匿名函数之后就立即调用而不去多此一举赋值给变量f呢?

我们试试

  1. function(){console.log("Hello World3");}();
  2. // output: Uncaught SyntaxError: Unexpected token (

很遗憾报错, 为什么会这样呢? 这时我们再回过头来看看函数定义与函数表达式的语法区别.

函数定义:     function funcName(){//code...} funcName();

函数表达式:  function [funcName](){//code...} funcName();

两者的语法差别很小, 因此当JavaScript解释器解释到function关键字是是把这段代码当做函数定义呢还是函数表达式呢? 根据JavaScript语法, 以function开始的语句会被当做函数定义, 而函数定义是必须要有函数名的, 并且通过函数名来执行函数, 但是显然function(){};()是不符合这个语法规范的, 这也就解释了为什么会报错. 所以, 任何可以使得JavaScript解释器把这一语句解释为函数表达式的方法都应该能让这一句代码成功执行. 那么问题又来了: 如何实现这一目的呢?

var a = 1;

这就是一个简单的表达式(expression), 因此我们想到可以在这一语句前面加上一个合适的运算符, 在这里由于运算符右边只能有一个函数对象操作数(JavaScript语句), 所以我们应该用操作数可以为对象的(一元)运算符, 我们来试试各种运算符.

  1. ! function(){console.log("Hello World2");}(); //Hello World2
  2. + function(){console.log("Hello World3");}(); //Hello World3
  3. - function(){console.log("Hello World4");}(); //Hello World4
  4. delete function(){console.log("Hello World5");}(); //Hello World5
  5. void function(){console.log("Hello World6");}(); //Hello World6

这些运算符都能实现我们的目的, 即让JavaScript解释器以创建函数表达式的方式创建这个函数. 至于具体使用哪一个运算符可以自己决定, 不过很明显我们希望用最简洁的方式. 在实践中一些大牛倾向于使用"!", 这一点在stackoverflow中有非常多的讨论.  http://stackoverflow.com/questions/3755606/what-does-the-exclamation-mark-do-before-the-function

3, 圆括号Parenthesis

另外一种更常用的写法, 也就是JQuery的用法, 是用圆括号作为分组操作符来让该执行函数语句被强制解释为以函数表达式的方式来创建这个匿名函数.

  1. // 第一种写法 ()分组运算符的内部代码只能是表达式, 这里将以函数表达式的方式创建并返回匿名函数
  2. (function(){/* code... */ };)();
  3. // 第二种写法 直接将最顶层的括号内部当做表达式创建并运行该匿名函数
  4. (function(){...}(););
  1. (function(){console.log("Hello World6");})(); //Hello World6
  1. (function(){console.log("Hello World6");}()); //Hello World6

4, 结论

分析下来, 其实这样写的目的很简单: 就是定义一个匿名函数并执行. 至于为什么这样写, 这是利用闭包closure的特性来初始化全局变量, 将这些全局变量的scope控制在匿名函数内部. 至于闭包, 下次再扯吧, 先下班了.

参考资料

1, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions

2, http://www.zhihu.com/question/20292224

3, http://www.zhcexo.com/round-brackets-in-javascript/

4, http://swordair.com/function-and-exclamation-mark/

JavaScript自运行函数(function(){})()的理解的更多相关文章

  1. 马上运行函数-$(function(){})篇

    QQ:1187362408 欢迎技术交流和学习 马上运行函数-$(function(){})篇(jquery): TODO: 1.jquery:jQuery(function($){ }) 与 $(d ...

  2. 如何理解Javascript中的函数(Function)

    Function类型 首先得知道,每个函数都是Function类型的实例,所以函数本身是对象. 示例1: function sum (num1, num2){ return sum1 + sum2; ...

  3. JavaScript "自"运行-setInertval()和setTimeout()理解

    setInterval()和clearInterval() var result = "Y"; function onOk() { var tid = setInterval(fu ...

  4. Javascript中的函数(Function)与对象(Object)的关系

    今天我们来尝试理解Function和Object.因为这个里面有些人前期可能会搞糊涂.他们之间到底是什么关系.当然也不除外当初的我. 注意:官方定义: 在Javascript中,每一个函数实际上都是一 ...

  5. Javascript学习之函数(function)

    在JS中,Function(函数)类型实际上是对象;每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法.由于函数是对象,因此函数名实际上也是一个指向函数对象的指针. 一 函 ...

  6. javascript立即执行函数 (function(){})()

    看到一段代码: (function(){ var outer = $('#subject'); outer.find('li').on('mouseover', mouseover); })() ( ...

  7. Js函数function基础理解

    正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定 ...

  8. JavaScript面向对象之函数构造器的理解

    1,在使用函数创建类时,函数本身也被称为该类的构造器,该类的构造器方法,该类的构造方法,该类的构造函数等等. 2,注意构造器方法是没有返回值的,当创建该类的实例时,必须调用该类的构造方法. 3,获取构 ...

  9. javascript中的立即执行函数(function(){…})()

    javascript中的立即执行函数(function(){…})() 深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是用(function(){…})()包 ...

随机推荐

  1. android 自定义view中findViewById为空的解决办法

    网上说的都是在super(context, attrs);构造函数这里少加了一个字段, 其实根本不只这一个原因,属于view生命周期的应该知道,如果你在 自定义view的构造函数里面调用findVie ...

  2. 【代码笔记】iOS-获得徐家汇的天气预报

    一,代码. //获得徐家汇的天气预报 -(void)getWeatherInfo{ NSError *error; NSURLRequest *request = [NSURLRequest requ ...

  3. iOS多线程实现4-NSOperation

    原文链接:http://www.cnblogs.com/mddblog/p/4816875.html 一.介绍 NSOperation是一个抽象类,我们可以使用系统提供的子类或者自己实现它的子类,具有 ...

  4. MPMoviePlayerController属性,方法,通知整理

    属性 说明 @property (nonatomic, copy) NSURL *contentURL 播放媒体URL,这个URL可以是本地路径,也可以是网络路径 @property (nonatom ...

  5. iOS开发之开源项目链接

    1. Coding iOS 客户端 Coding官方客户端. 笔者强烈推荐的值得学习的完整APP.GitHub - Coding/Coding-iOS: Coding iOS 客户端源代码 2. OS ...

  6. ORACLE 移动数据文件 控制文件 重做日志文件

    ORACLE数据库有时候需要对存储进行调整,增加分区.IO调优等等,此时需要移动数据文件.重做日志文件.控制文件等等,下文结合例子总结一下这方面的知识点. 进行数据文件.重做日志文件.控制文件的迁移前 ...

  7. SQL SERVER中什么情况会导致索引查找变成索引扫描

    SQL Server 中什么情况会导致其执行计划从索引查找(Index Seek)变成索引扫描(Index Scan)呢? 下面从几个方面结合上下文具体场景做了下测试.总结.归纳. 1:隐式转换会导致 ...

  8. 服务器重启后SQL Server Agent由于"The EventLog service has not been started" 启动失败

    案例环境: 操作系统   : Microsoft Windows Server 2003 Standard Edtion SP2 数据库版本 : SQL Server 2005 Standard Ed ...

  9. InfluxDB学习之InfluxDB数据保留策略(Retention Policies)

    InfluxDB每秒可以处理成千上万条数据,要将这些数据全部保存下来会占用大量的存储空间,有时我们可能并不需要将所有历史数据进行存储,因此,InfluxDB推出了数据保留策略(Retention Po ...

  10. logback配置详解1

    一:根节点<configuration>包含的属性: scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true. scanPeriod: 设置监测配置文 ...