一步步學習javascript基礎篇(6):函數表達式之【閉包】


 

回顧前面介紹過的三種定義函數方式

1. function sum (num1, num2) { return num1 + num2; }  //函數聲明語法定義

2. var sum = function(num1, num2){ return num1 + num2; }; //函數表達式定義 

3. var sum = new Function("num1", "num2", "return num1 + num2"); //Function 構造函數 

 

在分析閉包之前我們先來看看,定義和調用函數容易犯的錯誤。

例1:

sayHi(); //錯誤:函數還不存在
var sayHi = function () {
    alert("test");
};

例2:

if (true) {
    function sayHi() {
        alert("1");
    }
} else {
    function sayHi() {
        alert("2");
    }
}
sayHi();//打印結果並不是我們想要的

例3:

var fun1 = function fun2() {
    alert("test");
}
fun2();//錯誤:函數還不存在

在例1中,我們不能在使用函數聲明式語法定義之前調用函數。解決方案:

1.如果使用函數表達式定義函數的話,需要在表達式定義后調用。

var sayHi = function () {
    alert("test");
};
sayHi()

2.使用函數聲明式。(這里瀏覽器引擎會函數聲明提升,在所有代碼執行之前先讀取函數聲明)

sayHi(); 
function sayHi () {
    alert("test");
};

在例2中,我們預期的結果應該是打印1,實際結果是打印2。

為什么會這樣?正因為函數聲明提升,所以瀏覽器在預解析的時候不會判斷if條件,直接解析第二個函數定義的時候覆蓋了第一個。

解決方案:

在例3中,發現只能只用fun1()調用,而不能使用fun2()調用。

我自己的理解,真正原因不知道。沒找到資料。 

因為1: function fun3() { }; 等效與 var fun3 = function fun3() { }; 如圖:

所以只能只用fun1()調用,而不能使用fun2()調用。

其實這里我還是有疑問的?哪位大神知道,望告知。

既然,fun2在外面不能調用為什么在函數內部能調用?雖然在debugger還是得不到fun1。

 

好了,通過上面的三道題目熱身。我們繼續今天的主題“閉包”。

1.什么是閉包?

定義:就是有權訪問另一個函數作用域的變量的函數

我們先從一個示例函數開始:

例1:

function fun() {
    var a = "張三";
}
fun();//在我們執行完后,變量a就被標記為銷毀了

例2:

function fun() {
    var a = "張三";
    return function () {
        alert("test");
    }
}
var f = fun();//同樣,在我們執行完后,變量a就被標記為銷毀了

例3:

function fun() {
    var a = "張三";
    return function () {
        alert(a);
    }
}
var f = fun();//【現在情況發生變化了,如果a被銷毀,顯然f被調用的話就不能訪問到變量a的值了】
f();//【然后變量a的值正常的被訪問到了】
//這就是閉包,當函數A 返回的函數B 里面使用到了函數A的變量,那么函數B就使用了閉包。

示例:

顯然,濫用閉包會增大內存的使用。所以非特殊情況盡量不要使用閉包。如果用到了,記得手動設置空引用,內存才能被回收 f = null; 

圖解:(不了解作用域鏈的同學請先看前面的文章作用域和作用域鏈

補充:例4:(閉包的實例應用)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <style type="text/css">
        .tempBut {
            display: none;
        }
    </style>
</head>
<body>
    <div class="mydiv">
        <input value="but" type="button" class="tempBut" />
    </div>
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script type="text/javascript">
      
        for (var i = 0; i < 10; i++) {
            var tempHtml = $(".tempBut").clone().removeClass("tempBut"); 
            //tempHtml.click(function (i) {
            //    alert(i);//怎樣打印 0 到 9
            //});
            (function (num) {//使用閉包傳值
                tempHtml.click(function () {
                    alert(num);
                });
            })(i);
            $(".mydiv").append(tempHtml);
        }
    </script>
</body>
</html>

 

 

2.什么是匿名函數?(僅僅只是解釋這個概念)

如:(即,沒有名字的函數)

 

 

關於對象中函數的返回值是匿名函數時,this的怪異現象 

講解之前,先清醒下頭腦,不要越看越迷糊了。如果迷糊了,那就直接忽略下面的。

var name1 = "張三";
var obj = {
    name1: "李四",            
    fun2: function () {
        alert(this.name1);
    },
    fun3: function () {
        return function () {
            alert(this.name1);
        }
    }
}
obj.fun2();//打印結果"李四"意料之中的。
obj.fun3()();//因為這里返回的是一個函數,所以要再加一對()來調用。打印結果是"張三",意料之外。
//真是百事不得其解啊,什么this指向了全局?

我們前面講過“哪個對象點出來的方法,this就是哪個對象”,那我們的 obj.fun3()() 打印的是“張三”也就是說this執行了全局作用域。

我們看看下面的示例也許就知道為什么了。

var name1 = "張三";
var obj = {
    name1: "李四",            
    fun2: function () {
        alert(this.name1);
    },
    fun3: function () {
        return function () {
            alert(this.name1);
        }
    }
}       
//obj.fun3()();
var obj2 = {};
obj2.name1 = "test";
obj2.fun = obj.fun3();
obj2.fun();//打印結果"test",再次證明了“哪個對象點出來的方法,this就是哪個對象”.

我們來分解下 obj.fun3()() 先是 obj.fun3() 返回一個匿名函數到了window作用域,然后接着調用this就指向了window了。(感覺解釋有點勉強,也不知道對不,暫時自己先是這么理解的) 

 

 

這是學習記錄,不是教程。文中錯誤難免,您可以指出錯誤,但請不要言辭刻薄。

原文鏈接:http://haojima.net/zhaopei/519.html

本文已同步至目錄索引:一步步學習javascript

歡迎對個人博客感興趣的道友加入群:【嗨-博客】469075305 入群須知

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM