另一个考虑使用函数声明模式是因为一个只读的name属性;
重申一遍,这个属性不是标准规定的,但在很多环境中都是可以访问的;
对于函数声明和命名的函数表达式,name属性是被定义的;
未命名的函数表达式,取决了具体的实现,可能是未定义(IE)或者定义为空字符串(Firefox,WebKit)。
function foo() {} // declaration
var bar = function () {}; // expression
var baz = function baz() {}; // named expression
foo.name; // "foo"
bar.name; // ""
baz.name; // "baz"
当你使用Firebug或者其它调试工具调试的代码的时候,name属性是非常有用的,当调试器需要在函数中给你提示一个错误时,它会检查name属性,并作为一个提示,让调试器提示出更准确的信息;
name也用于递归调用,当你想递归调用函数时,函数就必须有个名字;
如果你对这两种情况都不感兴趣,那么使用未命名的函数表达式会更简单,更简洁。
选择函数表达式反对函数声明的原因就是函数表达式更加突出函数是对象,和其它对象差不多,而不是一些特殊的语言结构。
使用一个命名的函数表达式并且将它赋值给另一个不同名字的变量在技术上是可行的;
var foo = function bar() {};
但这种行为在一些浏览器中没有被正确实现(IE),所以不推荐使用这种模式(虽然在上一章最后使用过了,在chrome中测试通过);
函数提升(Function Hoisting)
正如你知道的,所有的变量,无论它们在函数的什么地方声明的,都会在背后提升到函数的顶部;
这同样也适用于函数,因为它们也就是对象,被赋值给了变量;
当使用一个函数声明的时候,函数的定义也会被提升,而不仅仅是函数的声明;
// antipattern
// for illustration only
// global functions
function foo() {
alert('global foo');
}
function bar() {
alert('global bar');
}
function hoistMe() {
console.log(typeof foo); // "function"
console.log(typeof bar); // "undefined"
foo(); // "local foo"
bar(); // TypeError: bar is not a function
// function declaration:
// variable 'foo' and its implementation both get hoisted
function foo() {
alert('local foo');
}
// function expression:
// only variable 'bar' gets hoisted
// not the implementation
var bar = function () {
alert('local bar');
};
}
hoistMe();
在这个例子中,就像其它普通变量一样,foo 和 bar 在函数hoistMe()中被提升到顶部,覆盖了全局的foo 和 bar;
不同的是局部的foo()的定义也被提升到顶部,工作正常,尽管是在后面定义的;
但bar()的定义并没有被提升,提升的只有他的声明,它是undefined并且不能作为函数使用(但仍然防止了全局的bar()在作用域链中的可见性);
既然现在关于基础知识和术语都已经清楚了,接下来我们会看一些关于JavaScript函数的好的模式,从回调模式(callback pattern)开始。
最后,再一次,请记住JavaScript两个非常重要的两个特征:
1.函数是对象
2.函数提供作用域