JavaScript高級教程(23)——高階函數、函數的閉包、遞歸

1.高階函數

高階函數是對其他函數進行操作的函數,它接收函數作爲參數或將函數作爲返回值輸出。
在這裏插入圖片描述
在這裏插入圖片描述
此時fn就是一個高階函數。
函數也是一種數據類型,同時可以作爲參數,傳遞給另外一個參數使用,最典型的就是作爲回調函數

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div></div>
    <script>
        //高階函數  函數可以作爲參數傳遞
        function fn(a, b, callback) {
            console.log(a + b);
            callback && callback();

        }
        fn(1, 2, function() {
            console.log('我是最後調用的');

        });
    </script>
</body>

</html>

2.閉包

2.1變量作用域

變量根據作用域的不同可分爲兩種:全局變量和局部變量

1.函數內部可以使用全局變量
2.函數外部不可以使用局部變量
3.當函數執行完畢,本作用域內的局部變量會銷燬

2.2什麼是閉包

閉包(closure)指有權限訪問另一個函數作用域中變量的函數。

簡單理解就是,一個作用域可以訪問另外一個函數內部的局部變量

2.4閉包的作用

延伸了變量的使用範圍

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //閉包(closure)指有權限訪問另一個函數作用域中變量的函數
        //閉包:我們fun這個函數作用域  訪問了另外一個函數fn裏面的局部變量  num
        //我們fn 外面的作用域可以訪問fn  內部的局部變量
        //閉包的主要作用:延伸了變量的作用範圍
        function fn() {
            var num = 10;

            // function fun() {
            //     console.log(num);

            // }
            // fun();
            return function() {
                console.log(num);

            };
        }
        var f = fn();
        f();
        //類似於
        // var f = function fun() {
        //     console.log(num);

        // }
    </script>
</body>

</html>

閉包的案例一:點擊li,輸出當前li索引號

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul class="nav">
        <li>巧克力</li>
        <li>棒棒糖</li>
        <li>牛奶</li>
        <li>蛋糕</li>
    </ul>
    <script>
        //1.我們可以利用動態添加屬性的方式
        var lis = document.querySelector('.nav').querySelectorAll('li');

        // for (var i = 0; i < lis.length; i++) {
        //     lis[i].index = i;
        //     lis[i].onclick = function() {
        //         console.log(this.index);

        //     }
        // }
        //2.利用閉包的方式得到當前li的索引號
        for (var i = 0; i < lis.length; i++) {
            //利用for循環創建了4個立即執行函數
            //立即執行函數也稱爲小閉包  因爲立即執行函數裏面任意一個函數都可以使用它的i這個變量
            (function(i) {
                // console.log(i);
                lis[i].onclick = function() {
                    console.log(i);

                }
            })(i);
        }
    </script>
</body>

</html>

閉包的案例二:3秒之後,打印所有的li元素的內容

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul class="nav">
        <li>巧克力</li>
        <li>棒棒糖</li>
        <li>牛奶</li>
        <li>蛋糕</li>
    </ul>
    <script>
        //閉包應用——3秒之後,打印所有的li元素的內容
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            (function(i) {
                setTimeout(function() {
                    console.log(lis[i].innerHTML);

                }, 3000)
            })(i);
        }
    </script>
</body>

</html>

閉包的案例三:計算打車價格

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //閉包運用:計算打車的價格
        //打車起步價13(3公里以內)  之後每多一公里增加5塊錢  用戶輸入公里數就可以計算打車的價格
        //如果有擁堵的情況  總價格多收取10塊錢擁堵費
        // function fn() {
        // }
        // fn;
        var car = (function fn() {
            var start = 13; //起步價  局部變量
            var totle = 0; //總價  局部變量
            return {
                price: function(n) {
                    if (n <= 3) {
                        totle = start;
                    } else {
                        totle = start + (n - 3) * 5;
                    }
                    return totle;
                },
                yd: function(flag) {
                        return flag ? totle + 10 : totle
                    } //擁堵之後的費用
            }
        })();
        console.log(car.price(5)); //23
        console.log(car.yd(true)); //33

        console.log(car.price(1)); //13
        console.log(car.yd(true)); //23
    </script>
</body>

</html>

思考題:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //思考題一:
        // var name = "The window";
        // var object = {
        //     name: "My Object",
        //     getNameFunc: function() {
        //         return function() {
        //             return this.name;
        //         };
        //     }
        // };
        // console.log(object.getNameFunc()()); // 輸出結果The window
        //解析:
        // var f = object.getNameFunc();
        //類似於
        // var f = function() {
        //     return this.name;
        // }
        // f();
        //思考題二:
        var name = "The Window";
        var object = {
            name: "My Object",
            getNameFunc: function() {
                var that = this;
                return function() {
                    return that.name;
                };
            }

        };
        console.log(object.getNameFunc()()); //輸出的是My Object
        // var f = object.getNameFunc();
        // var f = function() {
        //     return that.name;
        // };
        // f();
    </script>
</body>

</html>

3.遞歸

3.1什麼是遞歸?

如果一個函數在內部可以調用其本身,那麼這個函數就是遞歸函數,簡單理解:函數內部自己調用自己,這個函數就是遞歸函數。

遞歸函數的作用域和循環效果一樣,由於遞歸很容易發生“棧溢出”錯誤,所以必須要加退出條件return

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //遞歸函數:函數內部自己調用自己, 這個函數就是遞歸函數
        var num = 1;

        function fn() {
            console.log('我要打印六句話');
            if (num == 6) {
                return; //遞歸裏面必須加退出條件
            }
            num++;
            fn();
        }
        fn();
    </script>
</body>

</html>

遞歸求階乘:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //利用遞歸函數求n的階乘
        function fn(n) {
            if (n === 1) {
                return 1;
            }
            return n * fn(n - 1);
        }
        fn(3);
        console.log(fn(3));
        console.log(fn(4));
        //詳細思路  假如用戶輸入的是3
        //return 3 * fn(2)
        //return 3 * (2 * 1);
    </script>
</body>

</html>

遞歸求斐波那契數列:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //利用遞歸函數求斐波那契數列(兔子序列)1、1、2、3、5、8、13、21
        //用戶輸入一個數字n  就可以求出  這個數字對應的兔子序列
        //我們只需要知道用戶輸入n的前面兩項 (n - 1  n - 2)就可以計算出n  對應的序列值
        function fb(n) {
            if (n === 1 || n === 2) {
                return 1;
            }
            return fb(n - 1) + fb(n - 2);
        }
        console.log(fb(3));
        console.log(fb(1));
        console.log(fb(6));
    </script>
</body>

</html>

通過ID號得到相應的數據對象:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        var data = [{
            id: 1,
            name: '家電',
            goods: [{
                id: 11,
                gname: '冰箱',
                goods: [{
                    id: 111,
                    gname: '海爾'
                }, {
                    id: 112,
                    gname: '美的'
                }]
            }, {
                id: 12,
                gname: '洗衣機'
            }]
        }, {
            id: 2,
            name: '服飾'
        }];
        //我們想要做輸入id號, 就可以返回數據對象
        //1.利用我們的forEach去遍歷裏面的每一個對象
        function getID(json, id) {
            var o = {};
            json.forEach(function(item) {
                // console.log(item); //兩個數組元素
                if (item.id === id) {
                    // console.log(item);
                    o = item;
                    return item;
                    //2.我們想要得到裏層的數據11 12  可以利用遞歸
                    //裏面應該要有goods數組  並且數組的長度不爲0
                } else if (item.goods && item.goods.length > 0) {
                    o = getID(item.goods, id);
                }

            });
            return o;
        }
        console.log(getID(data, 1));
        console.log(getID(data, 2));
        console.log(getID(data, 11));
        console.log(getID(data, 12));
        console.log(getID(data, 111));
    </script>
</body>

</html>

3.2淺拷貝和深拷貝

1.淺拷貝只是拷貝一層,更深層次對象級別的只拷貝引用

2.深拷貝拷貝多層,每一級的數據都會拷貝

3.Object.assign(target,……sources) ES6新增方法淺拷貝

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 1.淺拷貝只是拷貝一層,更深層次對象級別的只拷貝引用

        // 2.深拷貝拷貝多層,每一級的數據都會拷貝

        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            }
        };
        var o = {};
        for (var k in obj) {
            //k是屬性名  obj[k]是屬性值
            o[k] = obj[k];
        }
        // console.log(o);
        // o.msg.age = 20;
        // console.log(obj);
        console.log('-----------------------');

        Object.assign(o, obj);
        console.log(o);
        o.msg.age = 20;
        console.log(obj);
    </script>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //深拷貝拷貝多層  每一級別的數據都會拷貝
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            }
        };
        var o = {};
        //封裝函數
        function deepCopy(newobj, oldobj) {
            for (var k in oldobj) {
                //判斷我們的屬性值屬於哪種數據類型
                //1.獲取屬性值  old[k]
                var item = oldobj[k];
                //2.判斷這個值是否是數組
                if (item instanceof Array) {
                    newobj[k] = [];
                    deepCopy(newobj[k], item);
                } else if (item instanceof Object) {
                    //3.判斷這個值是否是對象
                    newobj[k] = {};
                    deepCopy(newobj[k], item);
                } else {
                    //4.屬於簡單數據類型
                    newobj[k] = item;
                }



            }
        }
        deepCopy(o, obj);
        console.log(o);
        o.msg.age = 20;
        console.log(obj);
    </script>
</body>

</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章