JavaScript自运行函数(function(){})()的理解
今天打开JQuery源文件(jquery-1.8.3), 看到JQuery的初始化过程是这样的
(function( window, undefined ) {
// ....
})( window );
一开始看不懂这个写法, 经过几番搜索终于明白它的用法以及为什么这样用了, 我们一步步来分析.
1, 首先我们简化这个写法
除去参数, 经过简化后的写法可以写成
(function(){
console.log("Hello World");
})();
后面都使用这个写法作为示例.
2, 函数声明与函数表达式
网上有许多介绍创建JavaScript函数的文章告诉我们创建JavaScript函数有两种方式: 函数声明与函数表达式.
//function definition (also called function declaration)
function func1() {
console.log("Hello World1");
}
func1(); // function expression
var func2 = function (){
console.log("Hello World2");
};
func2();
第一种方式"函数定义"是标准的函数定义方式, 对函数func1的调用可以出现在函数定义之前;
第二种被称为"函数表达式", 与函数定义不同的是对函数func2的调用必须出现在变量func2之后的, 因为变量func2本质上是一个指向函数对象的变量, 这与我们定义普通变量的方式本质上是一样的; 另外一点是通过函数表达式创建的函数名可以不写, 我们称之为匿名函数.比如
var a = 10;
var f = function(){//code...};
f(); // 调用函数f
这里我们将变量f指向赋值运算符右边所创建的匿名函数, 然后就可以通过f()直接去调用这个匿名函数了, 那么, 问题来了, 我们是不是可以直接在创建好匿名函数之后就立即调用而不去多此一举赋值给变量f呢?

我们试试
function(){console.log("Hello World3");}();
// output: Uncaught SyntaxError: Unexpected token (
很遗憾报错, 为什么会这样呢? 这时我们再回过头来看看函数定义与函数表达式的语法区别.
函数定义: function funcName(){//code...} funcName();
函数表达式: function [funcName](){//code...} funcName();
两者的语法差别很小, 因此当JavaScript解释器解释到function关键字是是把这段代码当做函数定义呢还是函数表达式呢? 根据JavaScript语法, 以function开始的语句会被当做函数定义, 而函数定义是必须要有函数名的, 并且通过函数名来执行函数, 但是显然function(){};()是不符合这个语法规范的, 这也就解释了为什么会报错. 所以, 任何可以使得JavaScript解释器把这一语句解释为函数表达式的方法都应该能让这一句代码成功执行. 那么问题又来了: 如何实现这一目的呢?
var a = 1;
这就是一个简单的表达式(expression), 因此我们想到可以在这一语句前面加上一个合适的运算符, 在这里由于运算符右边只能有一个函数对象操作数(JavaScript语句), 所以我们应该用操作数可以为对象的(一元)运算符, 我们来试试各种运算符.
! function(){console.log("Hello World2");}(); //Hello World2
+ function(){console.log("Hello World3");}(); //Hello World3
- function(){console.log("Hello World4");}(); //Hello World4
delete function(){console.log("Hello World5");}(); //Hello World5
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的用法, 是用圆括号作为分组操作符来让该执行函数语句被强制解释为以函数表达式的方式来创建这个匿名函数.
// 第一种写法 ()分组运算符的内部代码只能是表达式, 这里将以函数表达式的方式创建并返回匿名函数
(function(){/* code... */ };)();
// 第二种写法 直接将最顶层的括号内部当做表达式创建并运行该匿名函数
(function(){...}(););
(function(){console.log("Hello World6");})(); //Hello World6
(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(){})()的理解的更多相关文章
- 马上运行函数-$(function(){})篇
QQ:1187362408 欢迎技术交流和学习 马上运行函数-$(function(){})篇(jquery): TODO: 1.jquery:jQuery(function($){ }) 与 $(d ...
- 如何理解Javascript中的函数(Function)
Function类型 首先得知道,每个函数都是Function类型的实例,所以函数本身是对象. 示例1: function sum (num1, num2){ return sum1 + sum2; ...
- JavaScript "自"运行-setInertval()和setTimeout()理解
setInterval()和clearInterval() var result = "Y"; function onOk() { var tid = setInterval(fu ...
- Javascript中的函数(Function)与对象(Object)的关系
今天我们来尝试理解Function和Object.因为这个里面有些人前期可能会搞糊涂.他们之间到底是什么关系.当然也不除外当初的我. 注意:官方定义: 在Javascript中,每一个函数实际上都是一 ...
- Javascript学习之函数(function)
在JS中,Function(函数)类型实际上是对象;每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法.由于函数是对象,因此函数名实际上也是一个指向函数对象的指针. 一 函 ...
- javascript立即执行函数 (function(){})()
看到一段代码: (function(){ var outer = $('#subject'); outer.find('li').on('mouseover', mouseover); })() ( ...
- Js函数function基础理解
正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定 ...
- JavaScript面向对象之函数构造器的理解
1,在使用函数创建类时,函数本身也被称为该类的构造器,该类的构造器方法,该类的构造方法,该类的构造函数等等. 2,注意构造器方法是没有返回值的,当创建该类的实例时,必须调用该类的构造方法. 3,获取构 ...
- javascript中的立即执行函数(function(){…})()
javascript中的立即执行函数(function(){…})() 深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是用(function(){…})()包 ...
随机推荐
- Android 应用程序集成Google 登录及二次封装
谷歌登录API: https://developers.google.com/identity/sign-in/android/ 1.注册并且登录google网站 https://accounts. ...
- [转]PaaS平台分类
本文转自阿朱说 大家发现没,自从我们上升到有规模的互联网架构后,咱们中国的技能能力就跟不上了,只能采取国际业界顶级大公司开源出来的而且已经经受住大规模实际应用考验的组件来搭架构,因而咱们近几年大规模网 ...
- Linux 日志报错 xxx blocked for more than 120 seconds
监控作业发现一台服务器(Red Hat Enterprise Linux Server release 5.7)从凌晨1:32开始,有一小段时间无法响应,数据库也连接不上,后面又正常了.早上检查了监听 ...
- webapi 控制器接收POST参数时必须以对象的方式接收
webapi 控制器接收POST参数时必须以对象的方式接收
- Python基础之反射
python中的反射功能是由以下四个内置函数提供:hasattr.getattr.setattr.delattr,改四个函数分别用于对对象内部执行:检查是否含有某成员.获取成员.设置成员.删除成员. ...
- charles 抓取eclipse中的请求
charles抓取eclipse中的请求 有时候,想要监测eclipse中发送get获取post请求,一样可以使用代理方式: 1.eclipse代码设置 代码中添加,可以就写在主函数中,然后再调用请求 ...
- 一致性哈希算法与Java实现
原文:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...
- [iOS]技巧集锦:UICollectionView内容下沉64像素原因和解决方案
现象 UICollectionView的内容在按Home键再回到APP时,会下沉64像素. 原因 页面有NavigationBar,正好是64像素,Controller勾选了Adjust Scroll ...
- sql中毫秒数与格式化时间的转换
使用MYSQL自带的函数FROM_UNIXTIME(unix_timestamp,format). 如: SELECT FROM_UNIXTIME(1461201575895/1000,"% ...
- Selenium2(WebDriver)_如何判断WebElement元素对象是否存在
1. selenium中如果去寻找元素,而元素不存在的话,通常会抛出NoSuchElementException 导致测试失败,但有时候,我们需要去确保页面元素不存在,才是我们正确的验收条件下面的方法 ...