首先,我们来完成一些小测试:
test1:
js javascript">js-function">js-keyword">function js-title">foojs-params">(){
js-function">js-keyword">function js-title">barjs-params">() {
js-keyword">return js-number">3;
}
js-keyword">return bar();
js-function">js-keyword">function js-title">barjs-params">() {
js-keyword">return js-number">8;
}
}
alert(foo());
test2:
js javascript">js-function">js-keyword">function js-title">foojs-params">(){
js-keyword">var bar = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">3;
};
js-keyword">return bar();
js-keyword">var bar = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">8;
};
}
alert(foo());
test3:
js javascript">alert(foo());
js-function">js-keyword">function js-title">foojs-params">(){
js-keyword">var bar = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">3;
};
js-keyword">return bar();
js-keyword">var bar = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">8;
};
}
test4:
js javascript">js-function">js-keyword">function js-title">foojs-params">(){
js-keyword">return bar();
js-keyword">var bar = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">3;
};
js-keyword">var bar = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">8;
};
}
alert(foo());
答案:8、3、3、[Type Error: bar is not a function]
函数声明">什么是函数声明?
Function Declaration 可以定义命名的函数变量,而无需给变量赋值。Function Declaration 必须以“function”开头。
js javascript">js-function">js-keyword">function js-title">barjs-params">() {
js-keyword">return js-number">3;
}
函数名在自身作用域和父作用域内是可获取的(否则就取不到函数了)。
js lua"> js-function">js-keyword">function js-title">barjs-params">() {
js-keyword">return js-number">3;
}
alert(bar()); //js-number">3
alert(bar) //js-function">js-keyword">function js-title">barjs-params">(){js-keyword">return js-number">3;}
函数表达式">什么是函数表达式?
Function Expression 将函数定义为表达式语句(通常是变量赋值)的一部分。通过 Function Expression 定义的函数可以是命名的,也可以是匿名的。Function Expression 不能以“function”开头。
js javascript">js-comment">//anonymous function expression
js-keyword">var a = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">3;
}
js-comment">//named function expression
js-keyword">var a = js-function">js-keyword">function js-title">barjs-params">() {
js-keyword">return js-number">3;
}
js-comment">//self invoking function expression
(js-function">js-keyword">function js-title">sayHellojs-params">() {
alert(js-string">"hello!");
})();
函数名(如果有的话)在作用域外是不可获取的(与 Function Declaration 对比)。
那么它们有什么区别呢?
实际上,解析器在向执行环境加载数据是,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到执行到它所在的代码行,才会真正被解释执行。
现在来解释下前面的测试。
test1 用了 function declaration,也就是说它们 get hoisted(被提升了)
什么是 Hoisting?
这里引用 Ben Cherry的话:“Function declaration和function variable(函数变量)通常会被 JavaScript 解释器移(‘hoisted’)到当前作用域顶部”。
function declaration 被提升时,整个函数体都会随之提升,所以 test1 的代码经过解释器解释后是像这样运行的:
js javascript">js-comment">//**Simulated processing sequence for Question 1**
js-function">js-keyword">function js-title">foojs-params">(){
js-comment">//define bar once
js-function">js-keyword">function js-title">barjs-params">() {
js-keyword">return js-number">3;
}
js-comment">//redefine it
js-function">js-keyword">function js-title">barjs-params">() {
js-keyword">return js-number">8;
}
js-comment">//return its invocation
js-keyword">return bar(); js-comment">//8
}
alert(foo());
Function Expression 会被提升吗?
这取决于表达式。比如 test2 中的第一个表达式:
js javascript">js-keyword">var bar = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">3;
};
等号左边的(var bar)是 Variable Declaration。Variable Declaration 会被提升,但是 Assignment Expression(赋值表达式)不会。所以当 bar 提升时,解释器会这样初始化:var bar = undefined。而函数定义本身不会被提升。所以 test2 的代码经过解释器解释后是像这样运行的:
js javascript">js-comment">//**Simulated processing sequence for Question 2**
js-function">js-keyword">function js-title">foojs-params">(){
js-comment">//a declaration for each function expression
js-keyword">var bar = js-literal">undefined;
js-keyword">var bar = js-literal">undefined;
js-comment">//first Function Expression is executed
bar = js-function">js-keyword">functionjs-params">() {
js-keyword">return js-number">3;
};
js-comment">// Function created by first Function Expression is invoked
js-keyword">return bar();
js-comment">// second Function Expression unreachable
}
alert(foo()); js-comment">//3
test 3 和 test 1 的逻辑相似。这次是 foo 函数被提升了。
test 4 就很简单了,根本就没有函数提升……
js javascript">js-comment">//**Simulated processing sequence for Question 4**
js-function">js-keyword">function js-title">foojs-params">(){
js-comment">//a declaration for each function expression
js-keyword">var bar = js-literal">undefined;
js-keyword">var bar = js-literal">undefined;
js-keyword">return bar(); js-comment">//TypeError: "bar not defined"
js-comment">//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 一样)。
js javascript">js-function">js-keyword">function js-title">foojs-params">() {
js-keyword">if(js-literal">false) {
js-function">js-keyword">function js-title">xjs-params">() {};
}
js-keyword">return x;
}
alert(foo());