定义函数的方式有2种:
- 函数声明
- 函数表达式
函数声明的形式
function functionName(arg1,arg2){}
关于函数声明,它有一个重要的特性就是函数声明提升,意思是在执行代码之前会先读取函数声明。
这就意味着可以把函数声明放在调用它的语句后面。
<script>
sayHi();
function sayHi(){
alert("hi,world")
}
console.log(sayHi.name) //输出sayHi,这是sayHi(){} 的name
</script>
这个例子不会抛出错误,因为在代码执行之前会读取函数声明。
第二种创建函数的方式是使用函数表达式。
最常见的创建方式:
var functionName = function(arg1,arg2){}
这种形式看起来好像是常规的变量赋值语句,即创建一个函数并将它赋值给变量functionName
。这种情况下创建的函数是匿名函数,因为function 关键字后面没有标识符。
匿名函数的name属性是空字符串。这也是可以理解的,毕竟是匿名函数嘛。
<script>
sayBye();
var sayBye = function(){
alert("byebye");
}
</script>
报错:sayBye is not a function
理解函数提升的关键,就是理解函数声明与函数表达式之间的区别。
<script>
var flag;
if(flag){
function sayHi(){
alert('hi');
}
}else{
function sayHi(){
alert('love');
}
};
sayHi(); //弹出 love
</script>
表面上看flag的布尔值true就是用一个sayHi()定义,否则就是用另一个定义。
实际上这在ECMAScript中属于无效语法,javascript引擎会尝试修正错误,将其转换为合理的状态。
但是浏览器尝试修正错误的做法并不一致。大多数浏览器会返回第二个声明,忽略condition,但chrome,火狐,opera,会在flag为true时,返回以一个函数声明;因此使用这种方法很危险,不应该出现在你的代码中。不过,如果是使用函数表达式就没有什么问题了。
<script>
var sayBye;
var flag;
if(flag){
sayBye = function(){
alert("bye");
}
}else{
sayBye = function(){
alert("bye lizi");
}
};
sayBye(); //弹出 bye lizi
</script>
也可以这样写:
<script>
function mult(num){
if(num<=0){
return num = 1;
}else{
return num * arguments.callee(num-1);
}
};
alert(mult(3)) ;
</script>
arguments.callee指向一个正在执行的函数的指针,因此可以用它来实现对函数的递归调用
。
通过使用arguments.callee代替函数名,可以确保无论怎样调用函数都不会出问题。因此,在编写地柜函数时,使用arguments.callee总比使用函数名更保险。
但在严格模式下,不能通过脚本方位arguemnts.callee,访问这个属性会导致错误
<script>
'use strict';
function mult(num){
if(num<=0){
return num = 1;
}else{
return num * arguments.callee(num-1);
}
};
alert(mult(3)) ; //'caller', 'callee', and 'arguments' properties may not be accessed on strict mode //functions
</script>
不过,可以使用命名函数表达式来达成相同的结果。
<script>
var interation = (function f(num){
if(num <=1){
return num = 1;
}else{
return num * f(num-1);
}
})
alert(interation(3));
</script>
以上代码创建了一个名为f()的命名函数表达式,然后将它赋值给变量interation.即使把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正常完成。这种方式在严格模式和非严格模式下都行得通。