[转] 函数声明和函数表达式——函数声明的声明提前
定义函数的方法
定义函数的方法主要有三种:
- 函数声明(Function Declaration)
- 函数表达式Function Expression)
- new Function构造函数
其中,经常使用的是函数声明和函数表达式的函数定义方法,这两种方法有着很微妙的区别和联系,而且这两种方法的使用也容易混淆,所以这篇文章主要总结下这两种函数定义方法的相关知识点,当然本文的主题依然是关于函数提前的。
函数声明的典型格式:
function functionName(arg1, arg2, ...){
<!-- function body -->
}
函数表达式
-
函数表达式的典型格式:
var variable=function(arg1, arg2, ...){ <!-- function body --> }
-
包含名称(括弧,函数名)的函数表达式:
var variable=function functionName(arg1, arg2, ...){ <!-- function body --> }
像上面的带有名称的函数表达式可以用来递归:
var variable=function functionName(x){ if(x<=1) return 1; else return x*functionName(x); }
声明提前
var
声明提前
小伙伴们应该都听说过声明提前的说法,我想在此再次重申一遍,因为声明提前是函数声明和函数表达式的一个重要区别,对于我们进一步理解这两种函数定义方法有着重要的意义。
但是再说函数声明提前之前呢,有必要说一下var
声明提前。
先给出var
声明提前的结论:
变量在声明它们的脚本或函数中都是有定义的,变量声明语句会被提前到脚本或函数的顶部。但是,变量初始化的操作还是在原来var语句的位置执行,在声明语句之前变量的值是undefined。
上面的结论中可以总结出三个简单的点:
- 变量声明会提前到函数的顶部;
- 只是声明被提前,初始化不提前,初始化还在原来初始化的位置进行初始化;
- 在声明之前变量的值是undefined。
还是来例子实在:
var handsome='handsome';
function handsomeToUgly(){
alert(handsome);
var handsome='ugly';
alert(handsome);
}
handsomeToUgly();
正确的输出结果是:
先输出undefined
,然后输出ugly
。
错误的输出结果是:
先输出handsome
,然后输出ugly
。
这里正是变量声明提前起到的作用。该handsome
局部变量在整个函数体内都是有定义的,在函数体内的handsome
变量压住了,哦不对,是覆盖住了同名的handsome
全局变量,因为变量声明提前,即var handsome
被提前至函数的顶部,就是这个样子:
var handsome='handsome';
function handsomeToUgly(){
var handsome;
alert(handsome);
var handsome='ugly';
alert(handsome);
}
handsomeToUgly();
所以说在alert(handsome)
之前,已经有了var handsome
声明,由上面提到的
在声明之前变量的值是undefined
所以第一个输出undefined
。
又因为上面提到的:
只是声明被提前,初始化不提前,初始化还在原来初始化的位置进行初始化
所以第二个输出ugly
。
函数声明提前
接下俩我们结合var
声明提前开始聊函数声明的声明提前。
函数声明的声明提前小伙伴们应该很熟悉,举个再熟悉不过的例子。
sayTruth();<!-- 函数声明 -->
function sayTruth(){
alert('myvin is handsome.');
}
sayTruth();<!-- 函数表达式 -->
var sayTruth=function(){
alert('myvin is handsome.');
}
小伙伴们都知道,对于函数声明的函数定义方法,即上面的第一种函数调用方法是正确的,可以输出myvin is handsome.
的真理,因为函数调用语句可以放在函数声明之后。而对于函数表达式的函数定义方法,即上面的第二种函数调用的方法是不能输出myvin is handsome.
的正确结果的。
结合上面的myvin is handsome.
例子,函数声明提前的结论似乎很好理解,不就是在使用函数声明的函数定义方法的时候,函数调用可以放在任意位置嘛。对啊,你说的很对啊,小伙伴,我都不知道怎么反驳你了。那就容我再扯几句。
从小伙伴所说的
不就是在使用函数声明的函数定义方法的时候,函数调用可以放在任意位置嘛
可以引出一点:
函数声明提前的时候,函数声明和函数体均提前了。
而且:
函数声明是在预执行期执行的,就是说函数声明是在浏览器准备执行代码的时候执行的。因为函数声明在预执行期被执行,所以到了执行期,函数声明就不再执行(人家都执行过了自然就不再执行了)。
上面是一点。
函数表达式为什么不能声明提前
我们再说一点:为什么函数表达式不能像函数声明那样进行函数声明提前呢?
辛亏我知道一点儿,否则真不知道我该怎么回答呢?
咳咳,按照我的理解给小伙伴们解释一下下:
我们上面说了var
的声明提前,注意我上面提过的:
只是声明被提前,初始化不提前,初始化还在原来初始化的位置进行初始化
Ok,我们把函数表达式摆在这看看:
var variable=function(arg1, arg2, ...){
<!-- function body -->
}
函数表达式就是把函数定义的方式写成表达式的方式(貌似是白说,但是这对于解释和理解为毛函数表达式不能函数声明提前具有良好的疗效),就是把一个函数对象赋值给一个变量,所以我们把函数表达式写成这个样子:
var varible=5
看到这,也许小伙伴们会明白了,一个是把一个值赋值给一个变量,一个是把函数对象赋值给一个变量,所以对于函数表达式,变量赋值是不会提前的,即function(arg1, arg2, ...){<!-- function body -->}
是不会提前的,所以函数定义并没有被执行,所以函数表达式不能像函数声明那样进行函数声明提前。
函数声明提前的实例分析
还是那句话,还是例子来的实在:
sayTruth();
if(1){
function sayTruth(){alert('myvin is handsome')};
}
else{
function sayTruth(){alert('myvin is ugly')};
}
在浏览器不抛出错误的情况下(请自行测试相应的浏览器是否有抛出错误的情况,为啥我不测试?我能说我懒么。。。),浏览器的输出结果是输出myvin is ugly
(我不愿承认,但是事实就是这样啊啊啊啊,难道道出了人丑就该多读书??????)。
为什么呢?当然是声明提前了。因为函数声明提前,所以函数声明会在代码执行前进行解析,执行顺序是这样的,先解析function sayTruth(){alert('myvin is handsome')}
,在解析function sayTruth(){alert('myvin is ugly')}
,覆盖了前面的函数声明,当我们调用sayTruth()
函数的时候,也就是到了代码执行期间,声明会被忽略,所以自然会输出myvin is ugly
(好残酷的现实。。。)。忘了的可以看上面说过的:
函数声明是在预执行期执行的,就是说函数声明是在浏览器准备执行代码的时候执行的。因为函数声明在预执行期被执行,所以到了执行期,函数声明就不再执行了(人家都执行过了自然就不再执行了)。
小了个结
关于函数声明的函数提前(提升)就聊到这里先,希望我的理解和扯淡能够对有需要的小伙伴有所帮助。
当然,实践出真知。对事物的了解、认知和运用还是在于多看多用多总结,记得有句名言,是讲声明和实践的:“动起来,为新的声明喝彩。”。