另一個考慮使用函數聲明模式是因爲一個只讀的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.函數提供作用域