iT邦幫忙

0

為了轉生而點技能-JavaScript,day12(閉包Closure及迴圈的閉包陷阱

閉包Closure

特徵:一個函式內的子函式,運作時會調用上層函式(或是父函式)的變數,避免父函式的變數因為沒有被參照而從記憶體中釋放。

        function init() {
            var name = "Mozilla";      // name 是個由 init 建立的區域變數
            function displayName() {   // displayName() 是內部函式(子函式),一個閉包
                alert(name);           // 使用了父函式宣告的變數:name
            }
            displayName();
        }
        init();

閉包通常都會用回傳 (return) 的方式,取得外部函式的變數 or 參數作運用。

例子1:

        function storeMoney() {
            var money = 1000;
            return function (price) {
                money = money + price;
                return money;
            }
        };
        console.log(storeMoney()); 
        //return 即為表達式,代表function storeMoney會從return回傳一個值出來,因為無參數,所以傳出function (price)。
        
        console.log(storeMoney()(100)); 1100

        var Kingmoney = storeMoney();
        console.log(Kingmoney);          //結果同console.log(storeMoney())
        console.log(Kingmoney(1000));    //2000
        console.log(Kingmoney(1000));    //3000 因為變數money一直被子函式參考,所以不會從記憶體中消除。

        var Queenmoney = storeMoney();
        console.log(Queenmoney(20));    //1020,參數更改
        console.log(Queenmoney(20));    //1040 

例子2(迴圈的閉包陷阱):

        function arrFunction() {
            var arr = [];
            for (var i = 0; i < 3; i++) {
                arr.push(function () {
                    console.log(i);
                });
            }
            console.log(arr);
            console.log('i', i);   // i  3
            return arr;
        }
        var fn = arrFunction();
        fn[0]();
        fn[1]();
        fn[2]();
        var arr = [];   //3  3  3

console.log(arr):

https://ithelp.ithome.com.tw/upload/images/20211201/20143762R1sov5iVzp.jpg


console.log('i', i):

  1. 因為閉包特性,會把 for 迴圈中的i帶入迴圈中的內部函式,而i會等迴圈結束後才會把最後i的值帶入函式,無法累次帶進,原因就是 "var i 的作用域 (scope) 最小單位是 function,不是 for "。
  2. i 的作用域在 arrFunction( ),在 i 進入arr.push(function () {console.log(i);}之前就會先把迴圈跑完,再將最後的值帶入,所以會出現console.log('i', i)為 i 3的結果。

var arr = []:

因為變數i皆會等迴圈結束後才會把最後i的值帶入函式,所以無論參數是多少,子函數都會參考到 for 迴圈中的i。


解迴圈的閉包陷阱:

方法一:立即函式

利用立即函式的特性,每執行一次迴圈,立即執行一次立即函式;本例利用迴圈的變數c填入立即函數的參數(newc)位置。

        function arrFunction() {
            var arr = [];
            for (var c = 0; c < 3; c++) {
                (function (newc) {
                    arr.push(function () {
                        console.log(newc);
                    });
                })(c)
            };

            return arr;
        }
        var fn = arrFunction();
        fn[0]();
        fn[1]();
        fn[2]();
        var arr = [];

方法二:利用let宣告取代迴圈裡的var宣告

        function arrFunction() {
            var arr = [];
            for (let c = 0; c < 3; c++) {

                arr.push(function () {
                    console.log(c);
                });

            }
            console.log(arr);
            // console.log('c', c);

            return arr;
        }
        var fn = arrFunction();
        fn[0]();
        fn[1]();
        fn[2]();
        var arr = [];

補充:參數預設值的寫法

        function carprice(GPS) {
            var car = 1000;
            var sum = 0;
            GPS = GPS || 0;          //當GPS有被填入參數值的時候選擇參數值,如果沒填入則為0。
            return function (GPS) {
                sum = GPS + car;
                return sum;
            };
        };
        var Tom = carprice();
        console.log(Tom(1000));

參考文章:

  1. JS-閉包 (Closure) 觀念整理:https://medium.com/chloelo925/js-%E9%96%89%E5%8C%85-closure-%E8%A7%80%E5%BF%B5%E6%95%B4%E7%90%86-346c32be3e30
  2. 【ES6】let 與 const 用法這些就夠了:https://www.itread01.com/xpyqp.html
  3. 從ES6開始的JavaScript學習生活-Closure 閉包:https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/closure.html

尚未有邦友留言

立即登入留言