一、创建函数的几种方法
1. 函数声明
function fnName(){…} 使用关键字function声明一个函数,再指定一个函数名,叫函数声明。
2.函数表达式
var fn = function(){…} 使用function声明一个函数,但并未指定函数名,最后将函数赋值给一个变量,叫函数表达式
3.匿名函数
function() {…} 使用function声明一个函数,并不给函数命名,叫匿名函数 。匿名函数有多种用途:赋值给一个变量则创建函数,赋值给一个事件则成为事件处理程序,创建闭包。
4.Function构造函数
var fn = new Function(“a”,”b”,”return a+b”);
相当于var fn=function(a,b){
return a+b;
}
二、函数声明和函数表达式的区别
- javascript引擎在解析javascript代码时会“函数提升”当前执行环境中的函数声明,而函数表达式要等javascript引擎执行到它所在行时才一步一步得解析函数表达式。
- 函数表达式可以在后面加()立即调用,函数声明必须使用fnName()的方式调用。
举例说明,代码1:
f1();
function f1(){
alert(1);
}
//正常,因为函数声明被提升了,所以f1()调用是可以的
f1();
var f1=function(){
alert(1);
}
//报错:f1 is not a function,f1还为保存对函数的引用,函数调用要在函数表达式之后。
var f1=function f2(i){
if(i==1) return 1;
else return i*f2(i-1);
}
f1(4);
//这种写法的函数名,只有在函数体内可见,可用于实现递归函数。在函数体外不能访问f2,这也是除了用arguments.callee来解决递归陷阱的另一种方法。
var f1=function(){
alert(1);
}();
//javascript引擎只解析函数声明,忽略后面的括号,函数声明不会执行
function(){
alert(1);
}();
//报错,匿名函数未进行赋值,无法直接调用
这样我们就好理解立即执行函数的写法了,因为要在函数体外面加括号直接调用的话,函数必须是函数表达式,不能是函数声明或匿名函数。
(function(){
alert(1);
})()
//一开始我以为是一个括号包裹一个匿名函数,后面再用()来调用函数。其实不然,第一个括号其实是一个()运算符,它将匿名函数变成了一个函数表达式。
(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并没有私有作用域,javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
JQuery使用的就是这种方法,将JQuery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。
递归陷阱
function f1(n){
if(n==1) return 1;
else return n*f1(n-1);
}//这是一个递归函数求n!的值。
var ftemp = f1;
f1=null;
ftemp(5);
//则直接报错,因为f1已经不是函数了,在ftemp函数内调用f1会导致出错。
解决办法:
function f1(n){
if(n==1) return 1;
else return n*arguments.callee(n-1); //arguments.callee指向正在执行的函数
}
另外一种就是上面提过的:
var f1 = function ftemp(){
if(n==1) return 1;
else return n*ftemp(n-1);
}//ftemp外部不可见
//但这也有弊端,f1调用必须在函数表达式之后。不如callee方法好