js閉包的理解

閉包是javascript語言的一個難點,也是它的特點,不少高級應用都要依靠閉包來實現。我的的理解是:函數中嵌套函數。javascript

閉包的定義及其優缺點

閉包是指有權訪問另外一個函數做用域中的變量的函數。建立閉包的常見方式,就是在一個函數內部建立另外一個函數。html

閉包的缺點是常駐內存,會增大內存的使用量,使用不當會形成內存泄漏。java

應用閉包主要是爲了:設計私有變量和方法。node

通常來說,函數執行完畢後,局部活動對象就會被銷燬,內存中僅保存全局做用域,可是閉包的狀況有所不一樣!瀏覽器

理解閉包的前提先理解另外兩個內容:做用域鏈、垃圾回收

做用域鏈:當代碼在執行過程當中,會建立變量對象的一個做用域鏈。做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。
請看下面的一段代碼:閉包

//全局環境中有一個變量color和一個函數changeColor()
 var color = "blue";
        //changeColor()的局部環境中有一個anotherColor變量和swapColors()函數
        function changeColor() {
            var anotherColor = "red";
            //swapColors()環境中只有一個tempColor
            function swapColors() {
                var tempColor = anotherColor;
                anotherColor = color;
                color = tempColor;
            }
            swapColors();
        }

        changeColor();

全局環境只能訪問到變量color
changeColor()局部環境也能夠訪問color
swapColors()能夠訪問其餘兩個環境的全部變量,可是那兩個變量都無權訪問tempColor模塊化

總結:內部環境能夠經過做用域鏈訪問全部的外部環境,但外部環境不能訪問內部環境中的任何變量和函數。每一個環境均可以向上搜索做用域鏈,但任何環境都不能向下搜索做用域鏈而進入另外一個執行環境。函數

垃圾回收原理學習

(1)javascript中若是一個對象再也不被引用,那麼這個對象就會被回收。
(2)若是兩個對象互相引用,而再也不被第3者引用,那麼這兩個互相引用的對象也會被回收。this

嵌套函數的閉包

var f = function () {
            var a = 9999;
            function f1() {
                alert(a);
            }
            f1();
        };
        f();

函數嵌套時候,在f執行完成以後,變量a還要被f1這個內部嵌套的函數繼續使用,所以a不會被釋放。js解析器發現函數中嵌套了函數時,就會把函數中的變量和子函數的變量一塊兒保存,構成了一個「閉包」。這些變量不會被內存回收器回收,只有當內部嵌套的函數不在執行後,纔會被回收。

閉包的特性和使用閉包的好處

閉包有三個特性:
1.函數嵌套函數
2.函數內部能夠引用外部的參數和變量
3.參數和變量不會被垃圾回收機制回收

使用閉包的好處:
1.但願一個變量長期駐紮內存
2.避免全局變量污染
3.私有成員變量的存在

閉包案例

屬性

var person = function () {
            var name = "kimi";
            this.getName = function () {
                return name;
            };

        };
        var p = new person();
        alert(p.getName());

name屬性經過getName方法獲取到。

在循環中直接找到對應元素的索引

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript" charset="UTF-8">
        window.onload = function () {
            var aLi = document.getElementsByTagName('li');
            for (var i = 0; i < aLi.length; i++) {
                aLi[i].onclick = function () {        //當點擊時for循環已經結束
                    alert(i);
                };
            }
        }
    </script>
</head>

<body>
    <ul>
        <li>a</li>
        <li>b</li>
        <li>c</li>
        <li>d</li>
    </ul>
</body>

</html>

執行以上代碼發現點擊任何一個返回的都是4,這是由於賦值的時候,傳的i是對內存地址的引用,循環結束,i指向的就是4.

使用閉包改寫上面的代碼

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>閉包</title>
    <script type="text/javascript" charset="UTF-8">
        window.onload = function () {
            var aLi = document.getElementsByTagName('li');
            for (var i = 0; i < aLi.length; i++) {
                (function (i) {
                    aLi[i].onclick = function () {
                        alert(i);
                    };
                })(i);
            }
        };
    </script>
</head>

<body>
    <ul>
        <li>a</li>
        <li>b</li>
        <li>c</li>
        <li>d</li>
    </ul>
</body>

</html>

每一次循環的時候,都把當前的i經過當即執行函數賦值。

變量的累加

全局變量的累加

<script type="text/javascript" charset="UTF-8">
        var i = 1;
        function text() {
            i++;
            alert(i);
        }
        text();//2
        text();//3
    </script>

局部變量的累加

<script type="text/javascript" charset="UTF-8">
        function text() {
            var i = 1;
            i++;
            alert(i);
        }
        text();//2
        text();//2
    </script>

上述代碼沒有實現累加,改寫代碼以下:

<script type="text/javascript" charset="UTF-8">
        function text() {
            var i = 1;
            return function () {//函數嵌套
                i++;
                alert(i);
            }
        }
        var a = text();//外部函數賦給a
        a();//2
        a();//3
    </script>

模塊化代碼,減小全局變量的污染

<script type="text/javascript" charset="UTF-8">
        var g = (function () {
            var i = 1;
            return function () {
                i++;
                alert(i);
            }
        })();
        g();//2 調用一次g函數,其實調用的是裏面內部函數的返回值
        g();//3
    </script>

this對象

在閉包中使用this對象可能致使一些問題

<script type="text/javascript" charset="UTF-8">
        var name = "The Window";

        var object = {
            name: "My Object",

            getNameFunc: function () {
                return function () {
                    return this.name;
                };

            }

        };

        alert(object.getNameFunc()());//The Window
    </script>

代碼先建立了一個全局變量name,又建立了一個包含name屬性的對象。這個對象還包含一個getNameFunc()方法,返回一個匿名函數,匿名函數又返回一個this.name。調用object.getNameFunc()()返回一個字符串。內部函數搜索的時候只搜索到活動對象。

<script type="text/javascript" charset="UTF-8">
        var name = "The Window";

        var object = {
            name: "My Object",

            getNameFunc: function () {
                var that = this;
                return function () {
                    return that.name;
                };

            }

        };

        alert(object.getNameFunc()());//My Object
    </script>

在定義匿名函數前,把this對象賦值給that變量,閉包也能夠訪問這個變量。即便函數返回,仍然引用着object

學習了閉包也不知道到底哪裏用到,到底有什麼用。回答:(其實你寫的每個js函數都是閉包,一個js函數的頂層做用域就是window對象,js的執行環境自己就是一個scope(瀏覽器的window/node的global),咱們一般稱之爲全局做用域。每一個函數,不論多深,均可以認爲是全局scope的子做用域,能夠理解爲閉包。)

本篇文章是本身學習過程當中的總結,若有錯誤歡迎指正。

相關文章
相關標籤/搜索