函数声明和函数表达式到底有什么区别?
要弄清函数声明和函数表达式的区别,首先要明白在JS中声明和表达式的行为存在十分微妙而又十分重要的差别。
首先,函数声明会在任何表达式被解析和求值之前先行被解析和求值。即使声明位于源代码中的最后一行,它也会先于同一作用域中位于最前面的表达式被求值。其实原理就是靠的作用域与变量提升机制,这块先不细说,后面单独写篇文章吧。
1console.log(fn());
2function fn(){
3 return 'helloNitx';
4}
5//打印:helloNitx
6
7console.log(fn());
8var fn = function(){
9 return 'helloNitx';
10}
11//输出:TypeError: fn is not a function
这里要补充个重要的知识点:由于通过条件语句控制函数声明的行为并未标准化,因此在不同环境下可能会得到不同的结果。所以不要在条件语句中使用函数声明,而可以使用函数表达式。
1//错误示例:不要把函数声明放在条件语句中,有的浏览器会把fn声明为返回1的函数,有的浏览器把fn声明为返回2的函数
2if(true){
3 function fn(){
4 console.log('1');
5 }
6}else {
7 function fn(){
8 console.log('2');
9 }
10}
11fn();
12
13//伪正确示例:
14var foo = null;
15if(true){
16 foo = function(){
17 console.log('3');
18 }
19}else {
20 foo = function(){
21 console.log('4');
22 }
23}
24foo();
注意,这里第二个示例为什么我注释为伪正确示例?看下面这段关于函数声明规则官方摘录:
函数声明只能出现在程度或函数体内。从句法上讲,它们不能出现在块中,比如不能出现在if、while或for语句。因为块只能包含语句,而不能包含函数声明这样的源元素。而唯一可能让表达式出现在块中的情形,就是让它作为表达式语句的一部分。但是规范也明确规定表达式语句不能以
function
开头。而这实际上就是说,函数表达式同样也不能出现在语句或块中。由于存在上述限制,只要函数出现在块中,实际上就可以看作是一个语法错误,而不用管什么函数声明或表达式。
所以较佳实践应是,不要把函数写在语句或块中,不管是声明函数还是表达式函数。
表达式函数里有匿名表达式函数和命名表达式函数。
所谓的命名函数表达式,指的是有名字的函数表达式,这个名字技术上称为标识符。var bar = function foo(){};
实际上就是一个命名函数表达式,这里有个细节需要注意:即这个名字(标识符)只在新定义的函数的作用域中有效,规范要求标识符不能在外围的作用域中有效。
1var f = function foo(){
2 return typeof foo; //foo只在内部作用域中有效
3}
4console.log(typeof foo); //undefined
5console.log(typeof f); //function
这个名字(标识符)的作用在于调试方便,其他与匿名函数表达式如var bar = function(){};
没有区别。另外浏览器的调试器通常功能较简,遇到复杂的程序时,效果不大。
在实际开发中,函数声明和函数表达式可以灵活选择,只是需要理清两点:变量和作用域的提升问题,在函数表达式中注意匿名函数表达式和命名函数表达式的区别。