一.函数声明
函数表达式是JavaScript中的一个既强大有容易令人困惑的特性。定义函数的方式有两种:一是函数声明,二是函数表达式
函数声明如下,这种定义方法会有函数声明提升,也就是函数的调用可以在函数声明前
function sayHello(name){
console.log('hello',name);
}
函数表达式如下,即创建一个函数并将它赋值给变量functionName。这种情况下创建的函数叫做匿名函数,因为function关键字后面没有标识符,匿名函数的name属性是空字符串,在使用前必须先赋值
var sayHello=function(name){
console.log('hello',name);
};
函数有一个非标准的name属性,通过这个属性可以访问到给函数指定的名字。这个属性的值用于等于跟在function关键字后面的标识符
//只在firefox,safari,chrome,opera有效
alert(functionName.name);
二.函数提升
意思是在执行代码之前会先读取函数声明。理解函数提升的关键,就是理解函数声明与函数表达式之间的区别。例如,执行以下代码的结果可能让人意想不到
//不要这样做
if(condition){
function sayHi(){
alert('Hi!');
}
}else{
function sayHi(){
alert('yo');
}
}
表面上看,以上代码表示在condition为true时,使用一个sayHi()的定义;否则,就使用另一个定义。实际上,这在ECMAScript中属于无效语法,JavaScript引擎会尝试修正错误,将其转换为合理的状态。但问题是浏览器尝试修正错误的做法并不一致。大多数浏览器会返回第二个声明,忽略condition;firefox会在condition为true时返回第一个声明。因此这种使用方式很危险,不应该出现在你的代码中,不过,如果是使用函数表达式,那就没有什么问题了。
//可以这么做
var sayHi;
if(condition){
sayHi=function(){
alert('Hi');
};
}else{
sayHi=function(){
alert('yo');
};
}
这个例子不会有什么意外,不同的函数会根据condition被赋值给sayHi。
能够创建函数再赋值给变量,也就能够把函数作为其他函数的值返回。
function createComparisonPunction(propertyName){
return function(object1,object2){
var value1=object1[propertyName];
var value2=object2[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
};
}
createComparisonFunction()就返回了一个匿名函数。返回的函数可能会被赋值给一个变量,或者以其他方式被调用;不过,在createComparison()函数内部,它是匿名的。在把函数当成值来使用的情况下,都可以使用匿名函数。
三.递归
递归函数是在一个函数通过名字调用自身的情况下构成的,如下所示。
function factorial(num){
if(num<=1){
return 1;
}else{
return num *factorial(num-1);
}
}
这是一个经典的递归阶乘函数。虽然这个函数表面看来没什么问题,但下面的代码却可能导致它出错。
var anotherFactorial=factorial;
factorial=null;
alert(anotherFactorial(4)); //出错
以上代码先把factorial函数保存在变量anotherFactorial中,然后将factorial变量设置为null,结果只想原始函数的引用只剩下一个。单在接下来调用anotherFactorial()时,由于必须执行factorial(),而factorial不再是函数,所以就会导致错误,在这种情况下,使用arguments.callee可以解决这个问题。
我们知道,arguments.callee是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用,例如:
function factorial(num){
if(num<=1){
return 1;
}else{
return num *arguments.callee(num-1);
}
}
通过使用argument.callee代替函数名,可以确保无论怎样调用函数都不会出问题。因此。在编写递归函数式,使用arguments.callee总比函数名更保险。
但在严格模式下,不能通过脚本访问arguments.callee,访问这个属性会导致错误,不过,可以使用命名函数表达式来达成相同的结果。例如:
var factorial =(function f(num){
if(num<=1){
return 1;
}else{
return num*f(num-1);
}
});
以上代码创建了一个名为f()的命名函数表达式,然后将它赋值给变量factorial。即便把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和非严格模式下都行得通。
声明:本文章是摘自JavaScript高级程序设计(第3版)第7章。