首先,我们来完成一些小测试:
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());