一、函数声明、函数表达式、匿名函数
1、函数声明
语法格式:function fnName () {…};
定义:使用function关键字声明一个函数,再指定一个函数名。
2、函数表达式
语法格式:var fnName = function () {…};
定义:使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量。
3、匿名函数
语法格式:function () {…}; 定义:使用function关键字声明一个函数,但未给函数命名,匿名函数属于函数表达式。
作用:赋予一个变量则创建函数;赋予一个事件则成为事件处理程序;赋予一个事件则创建闭包。
二、加括号后
//函数声明后面加括号,不会报错,但是javascript引擎只解析函数声明,会忽略后面的括号,函数声明不会被调用。 function fnName(){ alert('Hello World'); }();
//函数表达式后面加括号,当javascript引擎解析到此处时,能立即调用该函数。 var fnName=function(){ alert('Hello World'); }();
//匿名函数后面加括号,显示语法报错:要求需要一个函数名。虽然匿名函数属于函数表达式,但是未进行赋值操作,所以javascript引擎将开头的function关键字当做函数声明。 function(){ console.log('Hello World'); }();
三、函数的闭包、命名空间
函数的闭包(closure)可以让匿名函数立即被执行,这种闭包的优势是:
1、减少了全局变量的个数,可以有效减少命名冲突。原因是包在里面的变量对于外面来说是不可见的,他们的作用域局限在匿名函数的函数体内。
2、这种方式可以保存闭包外面的变量的状态。
多人合作一个项目时,你在全局或局部作用域中声明了一些变量,这些变量可能被其他人的同名变量覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术模仿一个私有作用域,用匿名函数作为一个“容器”,“容器内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
Jquery使用的就是这种方法,将Jquery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用Jquery代码时,可以保护Jquery内部变量。
四、javascript立即执行函数有哪些方法?
要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。
1、(function(){…})()
相当于先定义 function xx(){},后调用 xx()。是利用匿名函数和闭包来执行…里面的代码,同时所有的定义比如变量的作用域都在闭包里,不会污染到外部命名空间。
function(){…}是一个匿名函数,包裹着function(){…}的()表示拥有最高级的优先执行权。后面的()表示执行完function(){…}后,立即调用function(){…}。
(function(a){ console.log(a); //firebug输出123,使用()运算符 })(123);
2、(function (){…} ())
(function(a){ console.log(a); //firebug输出1234,使用()运算符 }(1234));
3、其他方法
!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)
()、!、+、-、=等运算符告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。加括号是最安全的做法,因为!、+、-、=等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。
五、实例应用
function fn() { for(var i=0 ; i<2; i++) { var backup = i; setTimeout(function() { alert(backup); }, 2000); } } fn();
上面代码的for循环一共两次,我们本来的目的是每隔两秒钟把每一次循环的索引i输出,我们期待它应该输出0和1,但是实际输出结果两次都是1。
使用了闭包之后,我们可以把循环中每一次i值的状态都保存下来。
function fn() { for(var i=0 ; i<2; i++) { (function(){ var backup = i; setTimeout(function() { alert(backup); }, 2000); })(); } } fn();
这次我们得到了正确的结果0和1。