首先,我们来完成一些小测试:
test1:
function foo(){
function bar() {
return 3;
}
return bar();
function bar() {
return 8;
}
}
alert(foo());
test2:
function foo(){
var bar = function() {
return 3;
};
return bar();
var bar = function() {
return 8;
};
}
alert(foo());
test3:
alert(foo());
function foo(){
var bar = function() {
return 3;
};
return bar();
var bar = function() {
return 8;
};
}
test4:
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]
什么是函数声明?
Function Declaration 可以定义命名的函数变量,而无需给变量赋值。Function Declaration 必须以“function”开头。
function bar() {
return 3;
}
函数名在自身作用域和父作用域内是可获取的(否则就取不到函数了)。
function bar() {
return 3;
}
alert(bar()); //3
alert(bar) //function bar(){return 3;}
什么是函数表达式?
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!");
})();
函数名(如果有的话)在作用域外是不可获取的(与 Function Declaration 对比)。
那么它们有什么区别呢?
实际上,解析器在向执行环境加载数据是,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到执行到它所在的代码行,才会真正被解释执行。
现在来解释下前面的测试。
test1 用了 function declaration,也就是说它们 get hoisted(被提升了)
什么是 Hoisting?
这里引用 Ben Cherry的话:“Function declaration和function variable(函数变量)通常会被 JavaScript 解释器移(‘hoisted’)到当前作用域顶部”。
function declaration 被提升时,整个函数体都会随之提升,所以 test1 的代码经过解释器解释后是像这样运行的:
//**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());
Function Expression 会被提升吗?
这取决于表达式。比如 test2 中的第一个表达式:
var bar = function() {
return 3;
};
等号左边的(var bar)是 Variable Declaration。Variable Declaration 会被提升,但是 Assignment Expression(赋值表达式)不会。所以当 bar 提升时,解释器会这样初始化:var bar = undefined。而函数定义本身不会被提升。所以 test2 的代码经过解释器解释后是像这样运行的:
//**Simulated processing sequence for Question 2**
function foo(){
//a declaration for each function expression
var bar = undefined;
var bar = undefined;
//first Function Expression is executed
bar = function() {
return 3;
};
// Function created by first Function Expression is invoked
return bar();
// second Function Expression unreachable
}
alert(foo()); //3
test 3 和 test 1 的逻辑相似。这次是 foo 函数被提升了。
test 4 就很简单了,根本就没有函数提升……
//**Simulated processing sequence for Question 4**
function foo(){
//a declaration for each function expression
var bar = undefined;
var bar = undefined;
return bar(); //TypeError: "bar not defined"
//neither Function Expression is reached
}
alert(foo());
还应该注意什么?
官方是禁止在非功能模块(比如 if)中使用 Function Declaration 的。但是所有浏览器都支持,但是各自的解释方式不同。
例如下面的代码段在 Firefox 3.6 中会抛错,因为它将 Function Declaration 解释成了 Function Statement(见上文),所以 x 没有定义。但是在 IE8、Chrome 5 和 Safari 5 中,会返回函数 x(和标准的 Function Declaration 一样)。
function foo() {
if(false) {
function x() {};
}
return x;
}
alert(foo());