在ECMAjavascript中,建立函數最經常使用的兩個方法是函數表達式和函數聲明,這二者的區別並非很明顯。由於ECMA規範只明確了一點:函數聲明必須帶有標識符(identifier,也就是常說的函數名稱),而函數表達式能夠省略標識符。javascript
//函數聲明 function 函數名稱:必須 (函數參數:可選){}; //函數表達式 function 函數名稱:可選(函數參數:可選){}
也就是說,若是沒有函數名稱,那麼確定是函數表達式,經常使用於:java
var foo = function(){};
那麼若是有函數名稱的時候,就要根據上下文來判斷了。
若是是做爲賦值表達式的一部分的話,那就是表達式。
若是是被包含在一個函數體內,或者位於程序最頂部,那麼就是聲明。json
var foo = function(){};// 函數表達式 function foo (){}; // 函數聲明 new function(){}; // 匿名類表達式 (function(){ function foo(){}; // 聲明 由於位於函數體內 })();
還有一種不太常見的表達式寫法,就是被括號括住的(function(){}),這是由於在括號是一個分組操做符,在它內部只能是表達式。瀏覽器
function foo(){};//函數聲明 (function foo(){}) //函數表達式,由於位於分組操做符內部 try{ (var temp = 3); } catch(error){ alert(error); } //這裏會拋出異常,由於在()中只能放入表達式,而var是聲明。
一樣的,在使用eval方法,將string類型的json字符串轉換爲對象的時候,使用eval('('+str +')')也是這個緣由。把str放在括號內,就可使編譯器把"{}"解析成表達式而不是代碼塊。ide
表達式和聲明還有一個很大的差別就是:聲明會在同一個做用域內,最先的表達式執行前執行解析。即便函數聲明在最後一行,也會優先解析。函數
alert(foo()); function foo(){ return "abc"; } //這也就是爲何結果是"abc"的緣由,聲明雖然寫在下面,可是在alert表達式以前已經被執行了。 // 可是若是foo不是以聲明的方式定義,而是以表達式的方式定義的話,就會出現問題了: alert(foo()); var foo = function(){ return "abc"; }; //Uncaught TypeError: foo is not a function 會出現這個錯誤,由於在alert調用foo的時候,foo並無被解析。
另外,雖然在條件語句內部可使用函數聲明,可是並無人測試過兼容性,所以,若是須要「重載」函數,最好仍是使用表達式:測試
if(true){ function foo(){ } } else{ function foo(){ } } //這種寫法存在必定風險,最好使用如下表達式的寫法 var foo; if(true){ foo = function(){}; } else{ }
上面說了一堆函數聲明和函數表達式的區別,如今終於言歸正傳說到正題了。能夠看到,上面函數表達式的例子裏面,都是沒有名字的。調試
//常規寫法: var foo = function(){}; //命名函數表達式: var foo = function f(){}; f就是這個表達式的名字,可是,它的做用域僅僅在於函數內部 var foo = function f(){ alert(typeof f); }; foo(); alert(f);//Uncaught ReferenceError: f is not defined
既然如此,命名還有什麼用呢?
答案就是在調試的時候,能夠很爽。。。。code
var f1 = function (){ return "abc"; }; var f2 = function(){ return f1() + "edf"; }; var f3 = function(){ return f2(); }; alert(f3());
能夠看到,當使用命名錶達式時,Call Stack會使用該名稱。不然會自動起一個名字。能夠確定的是,當狀況複雜的時候,解析器並不會返回你指望的那個名字,這也就是爲何要使用命名錶達式的緣由。對象
var foo = function f(){}; alert(foo === f);// 結果是false,固然這個只有在低版本的ie瀏覽器下才會看到,正常狀況下f應該是undefined
瞭解到這些bug之後,最好的解決方案就是:當標識符不存在。
由於自己就是方便調試的,那麼代碼裏面直接無視就是最好的辦法。
若是不想使用「命名」的方式的話,能夠還有一種簡單的方式來替代,就是在函數表達式中,定義一個函數聲明,而後將這個函數聲明返回。
var hasClassName = (function(){ var cache = {}; var _className = '(?:^|\\s+)' + className + '(?:\\s+|$)'; var re = cache[_className] || (cache[_className] = new RegExp(_className)); return re.test(element.className); } return hasClassName; })();