javascript:理解函数声明与函数表达式优先级问题

此文讨论一下函数声明和函数表达式的一些不同之处,以及最蛋疼的优先问题,已经明白的请无视。

之所以写本文是因为在网上看到一篇与此相关的文章,里面给出了结果但没有分析原因,而我自己也对此有些不肯定,所以我决定自己尝试一下,并记录下来,以便以后再也不用思考这类问题。

函数声明即function name () {}这样的代码。首先,我们要确定以下代码是完全正确的:

1
2
3
4
t1();
function t1(){
console.log("t1");
}

即函数可以提前调用然后再声明。

为什么可以这样?是因为javascript代码是一段一段预载的(即一组script标签),在一段代码预载完成后,会把函数声明提前到代码段的前面执行,以便在代码段的任何地方调用,所以前面的代码无错——不知道真实原理是不是如此,类似吧。

而函数表达式,就是把一个匿名函数附给一个变量,以后想要调用这个函数时,就直接调用这个变量即可。如:

1
2
3
4
var t1 = function(){
    console.log("new new t1");
}
t1();

函数表达式就没有直接函数声明的优点,即他不能先调用再来声明。

为什么呢?因为函数表达式是把函数附给一个变量做为他的值,而变量在没有声明之前就调用是要出错的——这个原理好懂吧?

其实我把原理一讲,好像下面的题目就变得毫无难度了,不过我还是写出来给大家讲一下吧。题目1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function t1(){
    console.log("t1");
}
t1();

function t1(){
    console.log("new t1");
}
t1();

var t1 = function(){
    console.log("new new t1");
}
t1();

var t1 = function (){
    console.log("new new new t1");
}
t1();

请判断以上代码的输出结果。当然,肯定不会是以下结果:

t1,
new t1,
new new t1,
new new new t1

这就涉及到前面讲的“函数声明会提前”的问题,上面的代码其实和下面的代码是一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function t1(){
    console.log("t1");
}
function t1(){
    console.log("new t1");
}
t1();
t1();

var t1 = function(){
    console.log("new new t1");
}
t1();

var t1 = function (){
    console.log("new new new t1");
}
t1();

这样一排相信大部分人一下就说出正确结果了,就是:

new t1
new t1
new new t1
new new new t1

因为上面的代码有两个同名函数声明,而很明显后面的同名函数会覆盖之前的函数,所以只有”new t1″是生效的——这一点,没有疑问吧?

而后面的两个var t1,则不存在提前的问题,但他们依然会覆盖同名元素,导致之前的同名元素(即t1函数声明)被替换了。

下面把题目改一下,变成题目2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function t1(){
    console.log("t1");
}
t1();

function t1(){
    console.log("new t1");
}
t1();

var t1 = function(){
    console.log("new new t1");
}
t1();
//注意下面这最后一个
function t1(){
    console.log("new new new t1");
}
t1();

这段代码要注意的是最后一个地方,即把之前的var t1的函数表达式改成了function t1()这种函数声明。这道题目结果如何呢?

还是用前面的“提前原则”整理一下代码,整理后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function t1(){
    console.log("t1");
}
function t1(){
    console.log("new t1");
}
function t1(){
    console.log("new new new t1");
}
t1();

t1();

var t1 = function(){
    console.log("new new t1");
}
t1();
t1();

整理之后又发现,代码变得毫无难度了:前面3个同名函数声明,同样只有最后一个生效,所以紧随其后的两次t1()调用都输出new new new t1,而后面的var t1又覆盖了之前的函数声明,所以其后的两次t1()调用会输出覆盖后的结果,即new new t1

new new new t1
new new new t1
new new t1
new new t1

题目2与题目1的代码就差别在最后一个函数的声明方式,但输出结果却面目全非。

写成之后发现我写了一篇如此简单的文章,呃。总之你记住,直接函数声明会提前,函数表达式则不会,就行了。

发表评论

电子邮件地址不会被公开。