定义函数的方式有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仍然有效,所以递归调用照样能正常完成。这种方式在严格模式和非严格模式下都行得通。
Live and learn ;)