JavaScript中函数声明与函数表达式的区别详解

在ECMAScript中,有两个最常用的创建函数对象的方法,即使用函数表达式或者使用函数声明。对此,ECMAScript规范明确了一点,即是,即函数声明 必须始终带有一个标识符(Identifier),也就是我们所说的函数名,而函数表达式则可以省略。下面看看这两者的详细区别介绍。

什么是 Function Declaration(函数声明)?

Function Declaration 可以定义命名的函数变量,而无需给变量赋值。Function Declaration 是一种独立的结构,不能嵌套在非功能模块中。可以将它类比为 Variable Declaration(变量声明)。就像 Variable Declaration 必须以“var”开头一样,Function Declaration 必须以“function”开头。

举例来说

function bar() { return 3; }

ECMA 5(13.0)定义语法:

function Identifier ( FormalParameterList[opt] ) { FunctionBody }

函数名在自身作用域和父作用域内是可获取的(否则就取不到函数了)。

function bar() { return 3; } bar() //3 bar //function

什么是 Function Expression(函数表达式)?

Function Expression 将函数定义为表达式语句(通常是变量赋值)的一部分。通过 Function Expression 定义的函数可以是命名的,也可以是匿名的。Function Expression 不能以“function”开头(下面自调用的例子要用括号将其括起来)。

举例来说

//anonymous function expression var a = function() { return 3; } //named function expression var a = function bar() { return 3; } //self invoking function expression (function sayHello() { alert("hello!"); })();

ECMA 5(13.0)定义语法:

function Identifieropt ( FormalParameterList[opt] ) { FunctionBody }

(这个定义感觉并不完整,因为它忽略了一个条件:外围语句是表达式,并且不以“function”开头)

函数名(如果有的话)在作用域外是不可获取的(与 Function Declaration 对比)。

那 Function Statement 是什么?

Function Statement 有时是 Function Declaration 的另一种说法。但是 kangax 指出,在 mozilla 中,Function Statement 是 Function Declaration 的一种拓展,使得 Function Declaration 语句可以在任何允许使用 statement(语句)的地方使用。但是 Function Statement 现在还不是标准,所以不建议应用在产品开发中。

下面我们从一些小测试开始。猜猜以下情况都会弹出什么结果?

题 1:

function foo(){ function bar() { return 3; } return bar(); function bar() { return 8; } } alert(foo());

题 2:

function foo(){ var bar = function() { return 3; }; return bar(); var bar = function() { return 8; }; } alert(foo());

题 3:

alert(foo()); function foo(){ var bar = function() { return 3; }; return bar(); var bar = function() { return 8; }; }

题 4:

function foo(){ return bar(); var bar = function() { return 3; }; var bar = function() { return 8; }; } alert(foo());

如果你的答案不是8、3、3和 [Type Error: bar is not a function] 的话,就继续往下读吧……(即使答对了也要继续读哦)

现在来解释下前面的测试。

Question 1 用了 function declaration,也就是说它们 get hoisted(被提升)了……

等一下,什么是 Hoisting?

这里引用 Ben Cherry的话:“Function declaration和function variable(函数变量)通常会被 JavaScript 解释器移(‘hoisted')到当前作用域顶部”。

function declaration 被提升时,整个函数体都会随之提升,所以 Question 1 的代码经过解释器解释后是像这样运行的:

//**Simulated processing sequence for Question 1** function foo(){ //define bar once function bar() { return 3; } //redefine it function bar() { return 8; } //return its invocation return bar(); //8 } alert(foo());

但是,我们经常被告诉说,return 语句后面的代码是运行不到的啊……

执行 JavaScript 过程中,有 Context(ECMA 5 将之分解为 LexicalEnvironment、VariableEnvironment 和 ThisBinding)和 Process(一系列按序调用的语句)两个概念。当程序进入执行域时,Declaration 会造成 VariableEnvironment。它们不同于 Statement(比如 return),也不遵循 Statement 的运行规则。

Function Expression 会被提升吗?

这取决于表达式。比如 Question 2 中的第一个表达式:

var bar = function() { return 3; };

等号左边的(var bar)是 Variable Declaration。Variable Declaration 会被提升,但是 Assignment Expression(赋值表达式)不会。所以当 bar 提升时,解释器会这样初始化:var bar = undefined。而函数定义本身不会被提升。

(ECMA 5 12.2 带有 initialzier(初始化器)的变量是在 VariableStatement 执行时,由 AssignmentExpression 赋值的,而不是在变量被创建时。)

因此 Question 2 的代码会按以下顺序运行:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wzfppf.html