前言
在 JavaScript 中,函数声明和函数表达式看起来类似,但很容易把二者搞混淆了。本文我们就来分析下函数声明和函数表达式的差异,以及 V8 是如何处理函数声明和函数表达式的。
函数声明与函数表达式的差异
那么它们具体有什么差异呢?我们先来看一段代码:
foo()
function foo() {
console.log('foo')
}
在这段代码中声明了一个 foo 函数,然后在 foo 函数之前调用了 foo 函数,执行这段代码,我们看到 foo 函数被正确执行了。
再来看另外一段代码:
foo()
var foo = function() {
console.log('foo')
}
在这段代码中定义了一个变量 foo,然后将一个函数赋值给了变量 foo,我们也是在 foo 函数的前面调用 foo,执行这段代码,发现报错了,提示的错误信息如下所示:
TypeError: foo is not a function
这是告诉我们,变量 foo 并不是一个函数,所以无法被调用。
同样是在定义的函数之前调用函数,第一段代码就可以正确执行,而第二段代码却报错,这是为什么呢?
其主要原因是这两种定义函数的方式具有不同语义,不同的语义触发了不同的行为。
因为语义不同,所以我们给这两种定义函数的方式使用了不同的名称,第一种称之为 函数声明,第二种称之为 函数表达式。
V8 是如何处理函数声明的?
我们知道,V8 在执行 JavaScript 的过程中,会先对其进行编译,然后再执行,比如下面这段代码:
var x = 5
function foo() {
console.log('Foo')
}
V8 执行这段代码的流程大致如下图所示:
在编译阶段,如果解析到函数声明,那么 V8 会将这个函数声明转换为内存中的函数对象,并将其放到作用域中。如果解析到了某个变量声明,也会将其放到作用域中,但是会将其值设置为 undefined,表示该变量还未被使用。我们把这种在编译阶段,将所有的变量提升到作用域的过程称为 变量提升。
然后在执行阶段,如果使用了某个变量,或者调用了某个函数,那么 V8 便会去作用域查找相关内容。
我们就能解释为什么可以在函数声明之前调用该函数了,这是因为声明的函数在编译阶段就被提升到作用域中,在执行阶段,只要是在作用域中存在的变量或者对象,都是可以使用的。
V8 是如何处理函数表达式的?
了解了函数声明,我们再来看看函数表达式。我们在一个表达式中使用 function 来定义一个函数,那么就把该函数称为函数表达式。
比如 foo = 1 它是一个表达式,这时候我们把右边的数字 1 替换成函数定义,那么这就变成了函数表达式,如下所示:
foo = function() {
console.log('foo')
}
函数表达式主要有以下3个特点:
- 函数表达式是在表达式语句中使用
function的,最典型的表达式是a = b这种形式,因为函数也是一个对象,我们把a = function(){}这种方式称为函数表达式。 - 在函数表达式中,可以省略函数名称,从而创建匿名函数。
- 一个函数表达式可以被用作一个即时调用的函数表达式——IIFE。
了解了函数表达式,我们就来分析这段代码:
foo()
var foo = function() {
console.log('foo')
}
当执行这段代码的时候,V8 在编译阶段会先查找声明语句,你可以把这段代码拆分为下面两行代码:
var foo = undefined
foo = function() {
console.log('foo')
}
第一行是声明语句,V8 在解析阶段,就会在作用域中创建该对象,并将该对象设置为 undefined,第二行是函数表达式,在编译阶段,V8 并不会处理函数表达式,所以也就不会将该函数表达式提升到作用域中了。那么在函数表达式之前调用该函数 foo,此时的 foo 只是指向了 undefined,所以就相当于调用一个 undefined,而 undefined 只是一个原生对象,并不是函数,所以当然会报错了。
立即调用的函数表达式(IIFE)
在编译阶段,V8 并不会处理函数表达式,而 JavaScript 中的立即函数调用表达式正是使用了这个特性来实现了非常广泛的应用,下面我们就来一起看看立即函数调用表达式。
(function () {
//statements
})()
因为函数立即表达式也是一个表达式,所以 V8 在编译阶段,并不会为该表达式创建函数对象。这样的一个好处就是不会污染环境,函数和函数内部的变量都不会被其他部分的代码访问到。
在 ES6 之前,JavaScript 没有私有作用域的概念,如果在多人开发的项目中,你模块中的变量可能覆盖掉别人的变量,所以使用函数立即表达式可以将内部变量封装起来,避免了相互之间的变量污染。