本文深入解析JavaScript中立即执行匿名函数(IIFE)的语法机制。通过对比函数声明与函数表达式的差异,阐明括号、运算符如何消除解析歧义并触发立即调用。同时介绍利用IIFE创建私有作用域以隔离全局变量、避免命名冲突的最佳实践,结合jQuery源码案例说明其实际应用场景。
立即执行匿名函数概述
本文详细介绍JavaScript中立即执行匿名函数的具体用法,供开发者参考。
;(function() {})();
这种模式被称为立即运行的匿名函数,也可称为立即调用函数表达式(IIFE)。
当匿名函数被包裹在括号内,并在其后紧跟一对括号时,该函数便会立即执行。
要使用函数,首先需声明其存在。最常用的方式是通过function语句定义函数。
Function对象与创建方式
Function对象
Function对象是JavaScript内置的固有对象,所有函数本质上都是Function对象的实例。
我们可以尝试通过构造函数直接创建新函数,答案是可行的。
var abc = new Function("x","y","return x*y;");
alert(abc(2,3)); // "6"
匿名函数的调用方法
匿名函数即没有名称的函数,这引出了如何调用它们的问题。
匿名函数的调用方式①
var abc=function(x,y){
return x+y;
}
alert(abc(2,3)); // "5"
上述操作实质上是另一种定义函数的形式,这种用法在开发中十分常见。
例如,在设定DOM元素的事件处理函数时,通常不为其命名,而是将事件引用赋予一个匿名函数。
匿名函数的调用方式②
使用括号将匿名函数括起来,随后在末尾添加一对包含参数列表的小括号。
alert((new Function("x","y","return x*y;"))(2,3));// "6"
小括号在表达式中的作用
小括号的作用
小括号能将表达式组合分块,每一对括号都会产生一个返回值,即括号内表达式的结果。
因此,当用一对小括号将匿名函数括起来时,小括号返回的是该匿名函数的Function对象引用。
这使得带有小括号的匿名函数如同有名字的函数一样,可以获取其引用。若在此引用变量后加上参数列表,即可实现普通函数的调用形式。
函数声明与表达式的差异
函数声明、函数表达式与匿名函数的区别
函数声明:function fnName () {…}; 使用function关键字声明函数并指定函数名,称为函数声明。
函数表达式:var fnName = function () {…}; 使用function关键字声明函数但未命名,最后将匿名函数赋予一个变量,称为函数表达式,这是最常见的形式。
匿名函数:function () {}; 使用function关键字声明但未命名,属于函数表达式。匿名函数赋予变量则创建函数,赋予事件则成为事件处理程序或创建闭包等。
函数声明与函数表达式的不同之处
一、JavaScript引擎在解析代码时会‘提升'(Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到引擎执行到所在行时,才从上而下逐行解析。
二、函数表达式后面可以加括号立即调用该函数,而函数声明不可以,只能以fnName()形式调用。
示例①
fnName();
function fnName(){
...
}
//正常,因为‘提升'了函数声明,函数调用可在函数声明之前
fnName();
var fnName=function(){
...
}
//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后
示例②
var fnName=function(){
alert('Hello World');
}();
//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数
function fnName(){
alert('Hello World');
}();
//不会报错,但是javascript引擎只解析函数声明,忽略后面的括号,函数声明不会被调用
function(){
console.log('Hello World');
}();
//语法错误,虽然匿名函数属于函数表达式,但是未进行赋值操作,
//所以javascript引擎将开头的function关键字当做函数声明,报错:要求需要一个函数名
要在函数体后加括号立即调用,该函数必须是函数表达式,不能是函数声明。
示例③
(function(a){
console.log(a); //firebug输出123,使用()运算符
})(123);
(function(a){
console.log(a); //firebug输出1234,使用()运算符
}(1234));
!function(a){
console.log(a); //firebug输出12345,使用!运算符
}(12345);
+function(a){
console.log(a); //firebug输出123456,使用+运算符
}(123456);
-function(a){
console.log(a); //firebug输出1234567,使用-运算符
}(1234567);
var fn=function(a){
console.log(a); //firebug输出12345678,使用=运算符
}(12345678)
从输出结果可见,在function前添加!、+、-甚至逗号等运算符,均可实现函数定义后立即执行的效果。括号、!、+、-、=等运算符将函数声明转换为函数表达式,消除了JavaScript引擎识别函数表达式和函数声明的歧义,告知引擎这是一个函数表达式而非声明,从而允许在后方加括号并立即执行。
添加括号是最安全的做法,因为!、+、-等运算符还会与函数返回值进行运算,有时会造成不必要的麻烦。
IIFE的实际应用:私有作用域
然而,这种写法有何实际用途?
JavaScript中原本没有私有作用域的概念。在多人开发项目中,若在全局或局部作用域声明变量,可能被他人用同名变量覆盖。利用JavaScript函数作用域链特性,可使用此技术模仿私有作用域。用匿名函数作为“容器”,内部可访问外部变量,而外部无法访问内部变量,因此( function(){…} )()内部定义的变量不会与外部变量冲突,俗称“匿名包裹器”或“命名空间”。
jQuery便采用此方法,将代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域调用jQuery代码时,可保护jQuery内部变量。
感兴趣的朋友可使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。