在JavaScript中閉包的做用和簡單的用法

JavaScript中閉包的做用和簡單的用法

 

1、閉包的簡介

     做用域鏈:js中只有函數有做用域的概念,因爲函數內能訪問函數外部的數據,而函數外部不能訪問函數內部的數據,由上述造成一種做用域訪問的鏈式結構叫作做用域鏈。javascript

 

     閉包:經過某種方式實現的一個封閉的、包裹的對外不公開的結構|空間,有人稱之爲封閉空間,以及匿名函數自調。閉包能夠使函數外部訪問函數內部的數據html

 

、閉包的基本寫法

     1、經過return來訪問函數內部的變量java

(function(){var a = 10; return 變量 | 函數 });git

 

      2經過當即執行函數訪問函數內部的變量github

         01(function(){})();  // 經常使用數組

        02(function(){}());  // 經常使用安全

        03;function(){}();閉包

        04+function(){}();函數

        05-function(){}();spa

 

、閉包的做用

      1、閉包的做用

          01、提供一種間接訪問函數內部變量的方法

         02、延長函數內部的局部變量的生命週期(慎用!局部變量過多會佔大量內存)

         03、全局變量私有化,減小全局變量污染

         04、函數局部變量在函數執行完後釋放內存(函數執行完畢,其內部的變量當即銷燬)

         05、更新複雜變量(能夠對變量進行校驗和判斷,保證安全性和穩定性)

 

      2、代碼示例

        01、提供一種間接訪問函數內部變量的方法

 function A() {
    var num = 10;// 局部變量
    return num;// 訪問內部數據
}
var B1 = A();
console.log(B1);// 10   說明局部變量沒有銷燬   延長函數內部的局部變量的生命週期

 

        上述代碼能夠在外部訪問函數A內部的局部變量,並延長局部變量num的生命週期,但也有缺陷,每次訪問都是一次性的,沒法修改保存其變量以下代碼所示:

            function foo() {
    var obj = {
        name:"張三",
        age:28
    };
    return obj;
}
var obj1 = foo();
var obj2 = foo();
console.log(obj1 == obj2);  //false

 

上述代碼所示兩次訪問的對象不是同一個`console.log(obj1 == obj2); // false`

 

        03、全局變量私有化,減小全局變量污染

(function () {
    window.onload = function () {// 全局變量私有化
        
    };
})();

       

(function () {
    var d = document;
    var btn1 = d.getElementById('btn');
    var btn2 = d.getElementById('btn');
    var btn3 = d.getElementById('btn');
})();

 

        05、更新複雜變量(能夠對變量進行校驗和判斷,保證安全性和穩定性)

 

001、設置數據

function test() {
    var str1 = '字符串1';
    return function (str) {
        str1 = str; // 設置數據
        return str1;
    }
}
var test1 = test();
console.log(test1('hello')); // hello
console.log(test1()); // undefined

 

上述代碼是設置數據,但並無判斷數據,因爲函數調用時沒有傳參,因此結果是`console.log(test1()); // undefined`,咱們還能夠在其內部添加校驗和判斷,代碼以下:

function test() {
    var str1 = '字符串1';
    return function (str) {
        if(typeof str == 'string'){// 判斷傳參是否是string類型
            str1 = str; // 設置數據
        }
        return str1;
    }
}
var test1 = test();
console.log(test1('hello')); // hello
console.log(test1()); // undefined

 

 

 002、返回多個數據

以數組的形式返回

 

function person() {
    var name = "黃忠";
    var age = 45;
    return [function () {//以數組的形式返回多個數據
        return name;
    },
        function () {
            return age;
        }];
}
var p1 = person();
console.log(p1[0]()); // 黃忠
console.log(p1[1]()); // 45

 

通常不怎麼以數組的形式返回數據!

 

以對象的形式返回

 

function person() {
    var name = "黃忠";
    var age = 45;
    return {
        getName:function () {//以對象的形式返回多個數據
            return name;
        },
        getAge:function () {
            return age;
        }
    }
}
var p1 = person();
console.log(p1.getName()); // 黃忠
console.log(p1.getAge()); // 45

 

咱們通常以這種形式返回多個數據!

 

003、以對象形式返回多個數據並設置多個數據

function person() {
    var name = "黃忠";
    var age = 45;
    return {
        getName:function () {//以對象的形式返回多個數據
            return name;
        },
        getAge:function () {
            return age;
        },
        setName:function (value) {
            //容錯性處理 + 邏輯校驗
            //判斷
            if (typeof value != 'string')
            {
                throw "該函數只接受字符串類型的參數";
            }
            name = value;
        },
        setAge:function (num) {
            //容錯性處理 + 邏輯校驗
            //判斷
            //console.log(num == NaN);// false
            var str = String(num);// 把獲取的實參轉爲字符串
            var str1 = String(NaN);// NaN轉爲字符串
            if ((str == str1) || typeof num != 'number' )
            {//判斷輸入的是否爲數字類型而且不爲NaN
                throw "該函數只接受數字類型的參數而且不爲NaN";
            }
            age = num;
        }
    }
}
var p1 = person();
console.log(p1.getName()); // 黃忠
console.log(p1.getAge()); // 45
p1.setName('郭嘉');// 修更名字
p1.setAge(28);// 修改年齡
console.log(p1.getName()); // 郭嘉
console.log(p1.getAge()); // 28

 

上述代碼在返回的對象中多添加了setName方法和setAge方法,並在方法內部作了容錯處理和校驗判斷,這樣使代碼的安全性和穩定性更好!

 

## 4、閉包的應用場景

      1for循環中

For循環中,for循環執行的速率很是快,有時候,咱們但願保存變量i,用於其餘地方。咱們就能夠用到閉包!代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<button>第一個</button>
<button>第二個</button>
<button>第三個</button>
<button>第四個</button>
<button>第五個</button>
<script>
    var btns = document.getElementsByTagName('button');
    //遍歷按鈕
    for(var i= 0;i < btns.length;i++){
        (function (a) {// 保存在形參中
            btns[a].onclick = function () {// 按鈕點擊事件  每點擊對應的按鈕彈出對應的數字
                alert(a+1);
            }
        })(i) ;// i看成實參傳進去
    }
    console.log(i);// 5 //js中只有函數有做用域的概念
</script>
</body>
</html>

 

但上述代碼中變量i是全局變量會影響其餘的for循環,咱們也能夠用閉包把for循環包起來,防止全局變量污染!代碼以下:

 

var btns = document.getElementsByTagName('button');
//遍歷按鈕
(function () {
    for(var i= 0;i < btns.length;i++){
        (function (a) {// 保存在形參中
            btns[a].onclick = function () {// 按鈕點擊事件  每點擊對應的按鈕彈出對應的數字
                alert(a+1);
            }
        })(i) ;// i看成實參傳進去
    }
})();
console.log(i);// Uncaught ReferenceError: i is not defined // i 未定義

 

上述代碼把for循環包起來,就把全局變量私有化了,外面就沒法訪問了,因此報錯未定義。

2、在for循環中處理事件機制時咱們常常用到閉包!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<button>第一個</button>
<button>第二個</button>
<button>第三個</button>
<button>第四個</button>
<button>第五個</button>
<script>
    var btns = document.getElementsByTagName('button');
    //遍歷按鈕
    for(var i= 0;i < btns.length;i++){
        btns[i].onclick = (function (a) {
            return function () {
                alert(a+1);
            }
        })(i);
    }
</script>
</body>
</html>

 

3、在定時器中使用閉包

for (var i = 0; i < 10; i++) {
    setTimeout((function (j) {
        return function () {
            console.log(j);
        }
    })(i),1000);
}

 

 

結論

閉包的原理:變量的訪問原則(即上一級的做用域沒法訪問下一級的做用域),其實函數自己就是閉包

 

6、源碼連接

 

https://github.com/350469960/OS/blob/master/javascript/closure.md

相關文章
相關標籤/搜索