if (true) {
function foo(){ return 1; }
}
else {
function foo(){ return 2; }
}
foo(); // 1
// 注:其它客户端会将foo解析成函数声明
// 因此,第二个foo会覆盖第一个,结果返回2,而不是1
3.函数语句不是在变量初始化期间声明的,而是在运行时声明的——与函数表达式一样。不过,函数语句的标识符一旦声明能在函数的整个作用域生效了。标识符有效性正是导致函数语句与函数表达式不同的关键所在(下一小节我们将会展示命名函数表达式的具体行为)。
复制代码 代码如下:
// 此刻,foo还没用声明
typeof foo; // "undefined"
if (true) {
// 进入这里以后,foo就被声明在整个作用域内了
function foo(){ return 1; }
}
else {
// 从来不会走到这里,所以这里的foo也不会被声明
function foo(){ return 2; }
}
typeof foo; // "function"
不过,我们可以使用下面这样的符合标准的代码来模式上面例子中的函数语句:
复制代码 代码如下:
var foo;
if (true) {
foo = function foo(){ return 1; };
}
else {
foo = function foo() { return 2; };
}
4.函数语句和函数声明(或命名函数表达式)的字符串表示类似,也包括标识符:
复制代码 代码如下:
if (true) {
function foo(){ return 1; }
}
String(foo); // function foo() { return 1; }
5.另外一个,早期基于Gecko的实现(Firefox 3及以前版本)中存在一个bug,即函数语句覆盖函数声明的方式不正确。在这些早期的实现中,函数语句不知何故不能覆盖函数声明:
复制代码 代码如下:
// 函数声明
function foo(){ return 1; }
if (true) {
// 用函数语句重写
function foo(){ return 2; }
}
foo(); // FF3以下返回1,FF3.5以上返回2
// 不过,如果前面是函数表达式,则没用问题
var foo = function(){ return 1; };
if (true) {
function foo(){ return 2; }
}
foo(); // 所有版本都返回2
再次强调一点,上面这些例子只是在某些浏览器支持,所以推荐大家不要使用这些,除非你就在特性的浏览器上做开发。
命名函数表达式
函数表达式在实际应用中还是很常见的,在web开发中友个常用的模式是基于对某种特性的测试来伪装函数定义,从而达到性能优化的目的,但由于这种方式都是在同一作用域内,所以基本上一定要用函数表达式:
复制代码 代码如下:
// 该代码来自Garrett Smith的APE Javascript library库()
var contains = (function() {
var docEl = document.documentElement;
if (typeof docEl.compareDocumentPosition != 'undefined') {
return function(el, b) {
return (el.compareDocumentPosition(b) & 16) !== 0;
};
}
else if (typeof docEl.contains != 'undefined') {
return function(el, b) {
return el !== b && el.contains(b);
};
}
return function(el, b) {
if (el === b) return false;
while (el != b && (b = b.parentNode) != null);
return el === b;
};
})();
提到命名函数表达式,理所当然,就是它得有名字,前面的例子var bar = function foo(){};就是一个有效的命名函数表达式,但有一点需要记住:这个名字只在新定义的函数作用域内有效,因为规范规定了标示符不能在外围的作用域内有效:
复制代码 代码如下:
var f = function foo(){
return typeof foo; // foo是在内部作用域内有效
};
// foo在外部用于是不可见的
typeof foo; // "undefined"
f(); // "function"
既然,这么要求,那命名函数表达式到底有啥用啊?为啥要取名?
正如我们开头所说:给它一个名字就是可以让调试过程更方便,因为在调试的时候,如果在调用栈中的每个项都有自己的名字来描述,那么调试过程就太爽了,感受不一样嘛。
调试器中的函数名
如果一个函数有名字,那调试器在调试的时候会将它的名字显示在调用的栈上。有些调试器(Firebug)有时候还会为你们函数取名并显示,让他们和那些应用该函数的便利具有相同的角色,可是通常情况下,这些调试器只安装简单的规则来取名,所以说没有太大价格,我们来看一个例子:
复制代码 代码如下: