(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/

时间: 2024-08-10 17:19:15

(function(){})()的解释的相关文章

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

自执行匿名函数: 常见格式:(function() { /* code */ })(); 解释:包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数. 作用:可以用它创建命名空间,只要把自己所有的代码都写在这个特殊的函数包装内,那么外部就不能访问,除非你允许(变量前加上window,这样该函数或变量就成为全局).各JavaScript库的代码也基本是这种组织形式. 总结一下,执行函数的作用主要为 匿名 和 自动执行

Function Declaration(函数声明)和函数表达式的区别

前言 在ECMAScript中,有两个最常用的创建函数对象的方法,即使用函数表达式或者使用函数声明.对此,ECMAScript规范明确了一点,即是,即函数声明 必须始终带有一个标识符(Identifier),也就是我们所说的函数名,而函数表达式则可以省略.下面看看这两者的详细区别介绍. 什么是 Function Declaration(函数声明)? Function Declaration 可以定义命名的函数变量,而无需给变量赋值.Function Declaration 是一种独立的结构,不能

JS中(function(){xxx})(); 这种写法的意思

自执行匿名函数: 常见格式:(function() { /* code */ })(); 解释:包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数. 作用:可以用它创建命名空间,只要把自己所有的代码都写在这个特殊的函数包装内,那么外部就不能访问,除非你允许(变量前加上window,这样该函数或变量就成为全局).各JavaScript库的代码也基本是这种组织形式. 总结一下,执行函数的作用主要为 匿名 和 自动执行

吃透【预解释】,从此再也不用担心!

Author:李金涛 Form:光环国际 Time:2017-12-31 23:49(跨年夜的最后一刻,我在辛勤耕耘我的"预解释",收获满满,甚喜!) 定义:预解释(变量提升):js在运行前,先把所有带var和function关键字的提前声明或定义.且预解释是发生当前作用域下的. 1,全局预解释阶段: (1)全局作用域与全局变量:当浏览器加载HTML页面的时候,首先会提供一个供全局JavaScript代码执行的环境,称之为全局作用域(global/ window).在window全局作

JQuery Autocomplete实战

废话不多说,先看效果!~ 需要引入的资源如下 <link rel="stylesheet" href="/css/jquery.autocomplete.css" type="text/css"> <script type="text/javascript" src="/lib/jquery.min.js"></script><!--1.8.3--> <

函数原型prototype以及对象的隐式原型__prot0__的基本了解

prototype原型: 一. 函数与对象的关系    1. 函数是对象的一种(函数是对象类型)        例: function fn1(){.........}           console.log(fn1 instanceof Object);           返回true,说明函数(fn1)是对象类型. 2. 对象是由函数创建的       例: var obj = new Object();           var arr = new Array(3);       

C++11function

C++11中增加了一些新的模板,今天我们简单介绍function模板. 头文件:<functional> function是一个模板,当创建一个具体的function类型时,我们必须像其他类模板一样提供额外的信息.所谓的额外信息是指function类型能够表示的对象的调用形式.例如vector,我们在使用vector的时候必须要指定 vector中存放数据的类型,是int型.string型.还是double.我们必须明确指定. function与函数指针比较相似,优点在于它允许用户在目标的实现

avascript中的自执行匿名函数

Javascript中的自执行匿名函数 格式: (function(){ //代码 })(); 解释:这是相当优雅的代码(如果你首次看见可能会一头雾水:)),包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数. 来个带参数的例子: (function(arg){ alert(arg+100); })(20); // 这个例子返回120. 重要用途:可以用它创建命名空间,只要把自己所有的代码都写在这个特殊的函数包装

JQUERY的基本概念

JQuery 语法: * 1.使用JQuery必须先导入一个JQuery.x.x.x.js文件: * * 2.JQuery中的选择器: * $("选择器").函数(); * ① $是JQuery的缩写,即可以使用jQuery("选择器").函数(); * ② 选择器,可以是任何设为CSS文件支持的选择符: * 3.文档就绪函数,防止在文档未完全加载完成之前,运行JQuery代码: * $(document).ready(function(){ * * //JS代码