當javaScript從入門到提高前需要註意的細節:函數部分 – Javascript教程_JS教程_技術文章 – 程式設計聯盟

在javaScript中,函數是一個很基礎的對象,同樣也是非常隨意,定義起來很隨意,用起來那是更加隨意。以下說明在javaScript中聲明一個函數那是多麼的隨意

[html]
function fun1() {//聲明一個函數 
 

 
function() { //聲明一個匿名函數 
 

 
var fun2 = function() {//聲明一個變量指向一個匿名的函數表達式 
 

 
var fun3 = function fun4() {//聲明一個變量指向一個非匿名的函數表達式 
 

 
function fun5() { 
    return function() { //返回一個匿名函數 
 
    } 

那function和var的區別有哪些?我們不討論深奧的,我們看下哪些運行結果
[html]
alert(typeof fun1); //function 
alert(typeof fun2); //undefined 
alert(typeof fun3); //undefined 
alert(typeof fun4); //function 
alert(typeof fun5); //function 
 
 
function fun1() {//聲明一個函數 
 

 
function() { //聲明一個匿名函數 
 

 
var fun2 = function() {//聲明一個變量指向一個匿名的函數表達式 
 

 
var fun3 = function fun4() {//聲明一個變量指向一個非匿名的函數表達式 
 

 
function fun5() { 
    return function() { //返回一個匿名函數 
 
    } 

我們可以看到function比var還要優先。
我們之前說變量的時候已經說過,一個變量可以被反復賦值,對於函數來說,這可以嗎?
對於習慣在靜態語言下寫代碼的人,看到如下代碼估計要氣憤瞭,這個是啥破代碼?
[html]
function fun1() { 
    alert("Y"); 

 
function fun1() { 
    alert("N"); 

但事實上,這個代碼是可以執行的,執行的結果暗示我們函數的名稱會被自動的作為對象的一個屬性,函數體就是屬性的值瞭
[html]
fun1(); // n 
 
this["fun1"](); // n 
this.fun1(); // n 
window["fun1"](); // n 
window.fun1(); // n 

現在我們把兩個因素合起來看看,function在所有執行代碼前先編譯,且後面的function會覆蓋前面的function定義,那麼,如下的代碼執行的是?
[html]
var input = 5; 
switch (input) { 
    case 10: 
        function fun1() { 
            alert(10); 
        } 
        break; 
    case 5: 
        function fun1() { 
            alert(5); 
        } 
        break; 
    default: 
        function fun1() { 
            alert("default"); 
        } 
        break; 

令人惱怒的是,在chrome和IE下執行的是我們前面的推斷,function在所有執行代碼前先編譯,且後面的function會覆蓋前面的function定義結果是
[html]
fun1(); //default 

而FireFox把if中的語句作為執行快,運行時才編譯,所以他的結果是
[html]
fun1(); //5 
為瞭解決這個問題,你需要吧動態分配的函數以函數的表達式來執行,避免編譯器對函數還沒有執行到就已經分配瞭,比如

[html]
var input = 5; 
switch (input) { 
    case 10: 
        fun1 = function() { 
            alert(10); 
        } 
        break; 
    case 5: 
        fun1 = function() { 
            alert(5); 
        } 
        break; 
    default: 
        fun1 = function() { 
            alert("default"); 
        } 
        break; 

說到函數的表達式,那什麼是函數的表達式呢?簡單的說,在右邊的都是函數的表達式
[html]
var fun1 = function() {//函數表達式 
 

 
var fun1 = function fun2() {//函數表達式 
 

 
var obj = { 
    Do: function() {//函數表達式 
 
    } 

 
new function() {//函數表達式 
 

 
function fun3() { 
    return function() { //函數表達式 
 
    } 

還有兩種就是在()內和【】內,原諒我為瞭看的清楚瞭些,用瞭中文的【】,具體看下代碼就ok瞭

[html]
(function() { }); 
[function() { } ]; 

函數表達式和一般的表達式的值一樣,都是執行到的時候才編譯,不過有一個例外,就是非匿名的函數表達式
[html]
var fun3 = function fun4() {//聲明一個變量指向一個非匿名的函數表達式 
 

上面的代碼如果你沒有特定的含義,求你最好不要這麼寫,因為會出現你想不到的情況
[html]
alert(fun3 === fun4); 

上面的結果是無論如何都超過瞭初學者的想法:在IE中式false,在Chrome或FireFox中是錯誤
對於Chrome或FireFox原因比較明顯,也就是說,fun4被賦值後就立即拋棄瞭

[html]
alert(typeof fun3); //function 
alert(typeof fun4); //undefined 

在IE中
[html]
alert(typeof fun3); //function 
alert(typeof fun4); //function 
這一點是IE的錯誤,但是從結果可以得知:命名函數表達式的名字在外部作用域是無效的。哪怕如下,你執行下fun3還是不行的www.aiwalls.com
[html]
var fun3 = function fun4() {//聲明一個變量指向一個非匿名的函數表達式 
 

 
fun3(); 
 
alert(typeof fun3); //function 
alert(typeof fun4); //undefined 

雖然IE是有錯誤,但是IE透露瞭一個信息,非匿名的函數表達式由於函數有名稱,被優先的分配瞭。看看我們最上面的函數定義聲明的案例中的fun4
這麼一來,如果你一不小心的話

[html]
function fun1() { 
    alert("A"); 

 
var count = 2; 
 
var input = 10; 
switch (input) { 
    case 5: 
        function fun1() { 
            alert(5); 
        } 
        break; 
    case 10: 
        if (count % 2 == 0) { 
            function fun1() { 
                alert("odd"); 
            } 
        } 
        else { 
            function fun1() { 
                alert("even"); 
            } 
        } 

 
fun1(); 

這些代碼的執行會不會出乎你的意料呢?註意上面的代碼在不同的瀏覽器中得到的是不同的。再看看下面的代碼
[html]
var fun1 = function(x) { 
    if (x < 10) { 
        return function fun2() { 
            alert("min"); 
        } 
    } 
    return function fun2() { 
        alert("max"); 
    } 

 
fun1(4)(); 
fun1(10)(); 

你是不是認為執行的結果又min也有max在不同的瀏覽器中,錯瞭,這次他們都很正常的預期。說明return將後面的非匿名函數表達式有效的讓編譯器作為表達式而不是聲明來看待瞭。
那麼非匿名的函數表達式有什麼問題嘛?
我們來看一個demo
[html]
var fun1 = function fun2() { 
    alert("OK"); 

 
fun1(); // OK 
fun2(); //在IE中彈出OK,在Chrome和FireFox中錯誤 
既然隻有IE支持,那我們在IE上繼續點實現,學習一個種語言,千萬不要被書上的條條框框限制,最好是多多多多的天馬行空的亂想點不正經的代碼出來,有靜態語言經驗的人都會被下面的代碼的執行結果嚇死
[html]
var fun1 = function fun2() { 
    alert("OK"); 

 
fun1 = function() { 
    alert("fun1"); 

 
fun1(); //fun1 
fun2(); //OK 

fun1和fun2竟然不是指向同一個對象,或者說內存地址。所以,如果說這個是IE的bug,那麼我們幸好Chrome和FireFox不支持非匿名的函數表達式具有實在的意義,上帝保佑,這樣我們盡可能的不要寫出非匿名的函數表達式就可以避免很多問題瞭。不過也有說法是說,非匿名的函數表達式在遞歸的時候有用,以下代碼告訴我們匿名函數表達式也一樣可以遞歸的哦,親。

一個匿名函數使用遞歸的階乘demo

[html]
alert((function(x) { 
    if (x > 1) { 
        return x * arguments.callee(–x); 
    } 
    else { 
        return 1; 
    } 
} (10)));// 3628800 

 

以上說瞭一大堆,就是為瞭要告訴你一個關鍵事實:函數表達式隻能在代碼執行階段創建而且不存在於變量對象中,換個更通俗的說法是:函數表達式隻有當執行到的時候,其才存在,否則是當他不存在的。

我們用匿名函數除瞭是return和內部賦值外,還常常用來做一次性執行的自執行函數。以下就匿名函數自執行的demo
[html]
(function() { 
    alert("OK"); 
})(); 
 
 
(function() { 
    alert("OK"); 
} ()); 

上面的兩種寫法在所有瀏覽器中都可以執行,隨你喜歡什麼方式,隻要固定就好,有些書推薦使用方式2,我不清楚為什麼,反正對我來說很隨意的使用瞭。
匿名函數也可以傳參
[html]
(function(x) { 
    x–; 
    alert(x); 
} (10)); 

匿名函數往往非常好用,我們可以把匿名函數作為一個參數來傳遞,有時候,我們傳遞的的確是函數的本身,有時候我們傳遞的是函數的結果,如果是函數的結果,那麼函數的自調用就非常cool瞭

先看看傳遞函數,以下的代碼會生成一組li元素,並且背景色是依據前一個li的背景色加一點點,這樣看上去就是一個漸變區域。當然,真的畫漸變區域不必這樣處理。

[html]
$( 
function() { 
    $(":button").click( 
    function() { 
        for (var i = 0; i < 10; i++) { 
            var ol = $("<li>").css({ width: 30, height: 30 }). 
            addClass("left"). 
            appendTo("ol"); 
            ol.css("backgroundColor", 
                function(index, value) { 
                    index = $("ol>li").index(this); 
                    var r, g, b = 0, colorValue = ""; 
                    if (index == 0) { 
                        r = parseInt(Math.random() * 256); 
                        g = parseInt(Math.random() * 256); 
                        b = parseInt(Math.random() * 256); 
                    } 
                    else { 
                        colorValue = $("ol>li").eq(index – 1).css("backgroundColor"). 
                                    toString().replace("rgb(", "").replace(")", "") 
                        r = parseInt(colorValue.split(",")[0]); 
                        g = parseInt(colorValue.split(",")[1]); 
                        b = parseInt(colorValue.split(",")[2]) + 5; 
                    } 
                    return "rgb(" + r + "," + g + "," + b + ")"; 
                } 
                ); 
        } 
    } 
    ); 

); 

我們再看一個例子,匿名函數的自調用在傳參的使用的用處。我們點擊一個p的時候,我創建瞭一個新的p,這個p的樣式完全的復制瞭被點擊的p的樣式,註意我的css函數中傳遞瞭一個匿名的自調用函數,這個函數返回瞭一個對象
[javascript]
$( 
function() { 
    $("p").click( 
    function() { 
        $("<p>").appendTo("body").css( 
        (function(e) { 
            var styleObj = {}; 
            for (var item in e.style) { 
                if (e.style[item] != "") { 
                    styleObj[item] = e.style[item]; 
                } 
            } 
            return styleObj; 
        } (this)) 
        ); 
    } 
    ); 

); 

摘自 shyleoking的專欄

發佈留言