在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 的代码会按以下顺序运行: