【JavaScript】18_回调函数,闭包与递归

131 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第20天,点击查看活动详情

14、高阶函数

高阶函数(回调函数)

  • 如果一个函数的参数或返回值是函数,则这个函数就称为高阶函数
  • 为什么要将函数作为参数传递?(回调函数有什么作用?)
  • 将函数作为参数,意味着可以对另一个函数动态的传递代码
    <script>
        class Person {
            constructor(name,age){
                this.name = name
                this.age = age
            }
        }
​
        const personArr = [
            new Person('孙悟空',18),
            new Person('沙和尚',37),
            new Person('红孩儿',3),
            new Person('白骨精',54),
        ]
​
        function filter(arr,cb){
            const newArr = []
​
            for(let i = 0; i <arr.length; i++){
                if(cb(arr[i])){
                    newArr.push(arr[i])
                }
            }
            return newArr
        }
​
        // 我们这种定义回调函数的形式比较少见,通常回调函数都是匿名函数
        function fn(a){
            return a.name === '孙悟空'
        }
​
        result = filter(personArr,a => a.name === '孙悟空')
        result = filter(personArr,a => a.age >= 18)
​
        const arr = [1,2,3,,4,5,6,7,8,9,10]
        result = filter(arr,a => a % 2 === 0)
​
        console.log(result)
    </script>

动态生成新数组,非破坏性函数

希望在someFn()函数执行时,可以记录一条日志

在不修改原函数的基础上,为其增加记录日志的功能

可以通过高阶函数,来动态的生成一个新函数

    <script>
        function someFn(){
            return 'hello'
        }
​
        function outer(cb){
            return () => {
                console.log('记录日志~~~')
                const result = cb()
                return result
            } 
        }
​
        let result = outer(someFn)
​
        function test(){
            console.log("test~~~")
            return 'test'
        }
        let newTest = outer(test)
        newTest()
    </script>

15、函数

创建一个函数,第一次调用时打印1,第二次调用打印2,以此类推

可以利用函数,来隐藏不希望被外部访问到的变量

闭包:

闭包就是能访问到外部函数作用域中变量的函数

什么时候使用:

当我们需要隐藏一些不希望被别人访问的内容时就可以使用闭包

构成闭包的要件:

  1. 函数的嵌套
  2. 内部函数要引用外部函数中的变量
  3. 内部函数要作为返回值返回
    <script>
        // let num = 0
        // function fn(){
        //     num++
        //     console.log(num)
        // }
​
        // fn()
​
        function outer(){
            let num = 0 // 位于函数作用域中
            return () => {
                num++
                console.log(num)
            }
        }
​
        const newFn = outer()
        // console.log(newFn)
        newFn()
        newFn()
        newFn()
    </script>

函数在作用域,在函数创建时就已经确定的(词法作用域)

和调用的位置无关

闭包利用的就是词法作用域

    <script>
        let a = '全局变量a'
​
        function fn(){
            console.log(a)
        }
​
        function fn2(){
            let a = 'fn2中的a'
            fn()
        }
​
        fn2()//因为后来调用了方法fn(),该方法是定义在全局中的,所以调用的是'全局变量a'
​
        function fn3(){
            let a = 'fn3中的a'
            function fn4(){
                console.log(a)
            }
            return fn4;
        }
​
        let fn4 = fn3()
        fn4()
    </script>

16、闭包

闭包的生命周期:

  1. 闭包在外部函数调用时产生,外部函数每次调用都会产生一个全新的闭包
  2. 在内部函数丢失时销毁(内部函数被垃圾回收了,闭包才会消失)

注意事项:

闭包主要用来隐藏一些不希望被外部访问的内容,

这就意味着闭包需要占用一定的内存空间

注意事项:

相较于类来说,闭包比较浪费内存空间(类可以使用原型而闭包不能),

需要执行次数较少时,使用闭包

需要大量创建实例时,使用类

    <script>
        function outer(){
            let someVariable = 'someValue'
​
            return function(){
                console.log(someVariable)
            }
        }
​
        function outer2(){
            let num = 0;
            return () => {
                num++
                console.log(num)
            }
        }
​
        let fn1 = outer2()//独立闭包
        let fn2 = outer2()
​
        fn1()
        fn2()
​
        fn1 = null
        fn2 = null
    </script>

17、递归

递归

  • 调用自身的函数称为递归函数
  • 递归的作用和循环是基本一直

递归的核心思想

将一个大的问题拆分为一个一个小的问题,小的问题解决了,大的问题也就解决了

两个要件:

1.基线条件 —— 递归的终止条件

2.递归条件 —— 如何对问题进行拆分

递归的作用和循环是一致的,不同点在于,递归思路的比较清晰简洁,循环的执行性能比较好

在开发中,一般的问题都可以通过循环解决,也是尽量去使用循环,少用递归

只在一些使用循环解决比较麻烦的场景下,才使用递归

如:求斐波那契数列中的第n个数

    <script>
        /* 
            一对兔子出生后的两个月后每个月都能生一对小兔子
                - 编写一个函数,可以用来计算第n个月的兔子的数量

            1   2   3   4   5   6   7   8   9   10  11  12
            1   1   2   3   5   8   13  21  34 ....
            - 规律,当前数等于前两个数之和(斐波那契数列)
        */

        function fib(n) {
            // 确定基线条件
            if (n < 3) {
                return 1
            }

            // 设置递归条件
            // 第n个数 = 第n-1个数 + 第n-2个数
            return fib(n - 1) + fib(n - 2)
        }

        let result = fib(10)
        console.log(result)
    </script>