[JavaScript]递归,深浅拷贝,处理this指向,异常处理

目录

递归

递归函数

递归求斐波那契数列的第n个数

递归实现阶乘

递归实现数组扁平化

深浅拷贝

浅拷贝

深拷贝

处理this指向

this指向

改变this指向

异常处理

1.throw 抛异常

2.try..catch 捕获异常

3.debugger


递归

递归函数

递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。递归函数必须有结束条件

// 递归的函数存在堆栈中先进后出

        function fn(n) {
            console.log(n);
            n -= 2
            if (n > 0) {
                fn(n)
            }
            console.log(n);
        }
        fn(10)//

详细图解如下:

 

递归求斐波那契数列的第n个数

        // 🏆斐波那契额数列

        /*已知第一项和第二项的数字是第一项 第二项 第三项 第四项第五项 第6项

         1 1 2 3 5 8 13

        我们想要知道 数列的第6项的值是第4项和第五项的和 第4项 是第二项和第3项数字的和

*/

        function fn(n) {
            if (n === 1 || n === 2) {
                return 1
            } else {
                // return n的前两项相加
                return fn(n - 1) + fn(n - 2)
            }
        }
        console.log(fn(7));//13

fn(5)的详细图解如下:

 

递归实现阶乘

       // 🏆递归实现阶乘
        // function factorial(x) {
        //     if (x === 1) return 1
        //     return x * factorial(x - 1)
        // }
        // console.log(factorial(3));

递归实现数组扁平化

        //使用递归来实现数组扁平化

        //扁平化:将多维数组转换为一维数组

        const arr = [1, 2, 3, [4, 5, [6], 7,[8,[9,[10]]]]]

        const newArr = []
        function flat(data) {
            data.forEach(item => {
                // 判断 该元素是不是 数组
                if (item instanceof Array) {
                    // 如果是数组 继续递归
                    flat(item)
                } else {
                    // 如果不是数组 则把这个元素放入新的数组中
                    newArr.push(item)
                }
            })
        }
        flat(arr)
        console.log(newArr);

深浅拷贝

浅拷贝

        // 🏆浅拷贝:拷贝的是地址

        // 1.只拷贝第一层,,如果是普通类型变量则拷贝值,引用类型变量拷贝内存地址

        // 常见方法:

        // 1.拷贝对象:Object.assgin() / 展开运算符 {...obj } 拷贝对象

        // 2.拷贝数组:Array.prototype.concat() 或者[...arr]

       const obj1 = {
            uname: '张三',
            age: 18,
            gender: "男",
            gfs: ['凤姐', "芙蓉姐姐", '黄蓉'],
            wife: {
                w1: '蔡徐坤',
                w2: 'ikun'

                
            }
        }
        // 浅拷贝 只拷贝对象的 第一层 如果第一层 有引用类型 拷贝的内存地址 如果是简单类型 拷贝的值
        // 💎1.使用展开运算符拷贝
        // const obj2 = { ...obj1 };
        // 💎2.使用Object.assign()拷贝
        const obj2={}
        Object.assign(obj2,obj1)

        obj2.uname = '王雄厚'
        obj2['wife']['w1'] = '迪丽热巴'

 详细图解如下:

 

深拷贝

        // 深拷贝:拷贝的是对象,不是地址

        // 常见方法:

        // 1. 通过递归实现深拷贝

        // 2. lodash/cloneDeep

        // 3. 通过JSON.stringify()实现

        // 1. 通过递归实现深拷贝

        // 函数递归:

        // 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数

        //  简单理解:函数内部自己调用自己, 这个函数就是递归函数

        //  递归函数的作用和循环效果类似

        //  由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return

        const obj1 = {
            uname: '张三',
            age: 18,
            gender: "男",
            gfs: ['凤姐', "芙蓉姐姐", '黄蓉'],
            wife: {
                w1: '蔡徐坤',
                w2: 'ikun'
            }
        }
        function deepCopy(newObj, oldObj) {
            for (let k in oldObj) {
                const item = oldObj[k]
                // 判断是不是数组
                if (item instanceof Array) {
                    newObj[k] = []
                    // 递归
                    deepCopy(newObj[k], item)
                } else if (item instanceof Object) {
                    newObj[k] = {}
                    // 递归
                    deepCopy(newObj[k], item)
                }
                else {
                    // 🏆对象名[新属性名] = 新值
                    newObj[k] = item
                }
            }
        }
        obj2 = {}
        deepCopy(obj2, obj1)
        obj2.uname = '王雄厚'
        obj2['wife']['w1'] = '迪丽热巴'

        // 2. js库lodash里面cloneDeep内部实现了深拷贝

lodashjs安装地址和中文文库

    <script src="./js/lodash.min.js"></script>  
    <script>
        const obj1 = {
            uname: '张三',
            age: 18,
            gender: "男",
            gfs: ['凤姐', "芙蓉姐姐", '黄蓉'],
            wife: {
                w1: '蔡徐坤',
                w2: 'ikun'
            }
        }
        const obj2 = _.cloneDeep(obj1)
        console.log(obj2);

       // 3. 通过JSON.stringify()实现深拷贝

      const obj2 = JSON.parse(JSON.stringify(obj1))

处理this指向

this指向

//  🏆普通函数的this指向

// 普通函数的调用方式决定了 this 的值,即【谁调用 this 的值指向谁】

// 普通函数没有明确调用者时 this 值为 window,严格模式下没有调用者时 this 的值为 undefined

//🏆   this指向-箭头函数

// 目标: 能说出箭头函数的this指向

// 箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this !

// 1. 箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的

// 2.箭头函数中的this引用的就是最近作用域中的this

// 3.向外层作用域中,一层一层查找this,直到有this的定义

// 注意情况1: 在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window

// 因此DOM事件回调函数如果里面需要DOM对象的this,则不推荐使用箭头函数

// 注意情况2:

// 同样由于箭头函数 this 的原因,基于原型的面向对象也不推荐采用箭头函数

// 总结:

// 1. 函数内不存在this,沿用上一级的

// 2.不适用

// 构造函数,原型函数,dom事件函数等等

// 3. 适用

// 需要使用上层this的地方

// 4. 使用正确的话,它会在很多地方带来方便, 

改变this指向

        //         JavaScript 中还允许指定函数中 this 的指向,有 3 个方法可以动态指定普通函数中 this 的指向

        //   call()

        //   apply()

        //   bind()

        // 🏆1. call() 了解

        // 使用 call 方法调用函数,同时指定被调用函数中 this 的值  语法:

        // fun.call(thisArg, arg1, arg2, ...)

        //  thisArg:在 fun 函数运行时指定的 this 值  arg1,arg2:传递的其他参数

        //  返回值就是函数的返回值,因为它就是调用函数

       // 🏆2. apply()-理解

        // 使用 apply 方法调用函数,同时指定被调用函数中 this 的值  语法:

        // fun.apply(thisArg, [argsArray])

        //  thisArg:在fun函数运行时指定的 this 值  argsArray:传递的值,必须包含在数组里面

        //  返回值就是函数的返回值,因为它就是调用函数

      // function f(x, y) {
        //     console.log(this, x, y);
        // }
        // const obj = {
        //     uname: 'zs'
        // }
        // f.apply(obj, [20, 34])
        //  因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值

        //求数组最大值
        // const arr = [3, 5, 2, 9]
        // console.log(Math.max.apply(null, arr))//9 利用apply
        // console.log(Math.max(...arr))//9利用展开运算符

       //         call和apply的区别 

        //  都是调用函数,都能改变this指向

        //  参数不一样,apply传递的必须是数组

        //🏆 3. bind()-重点

        //  bind() 方法不会调用函数。但是能改变函数内部this 指向

        //  语法:

        // fun.bind(thisArg, arg1, arg2, ...)

        //  thisArg:在 fun 函数运行时指定的 this 值  arg1,arg2:传递的其他参数

        //  返回由指定的 this 值和初始化参数改造的 原函数拷贝 (新函数)

        //  因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind,比如改变定时器内部的this指向

        function f(x, y, z) {
            console.log(this, x, y, z);
        }
        const obj = {
            uname: 'zs',
            age: 18
        }
        const ff = f.bind(obj, 10, 20, 30)//返回一个新的函数
        ff()

// 💎相同点:

//  都可以改变函数内部的this指向. 

// 💎 区别点:

// 区别:

// 1.bind不能调用函数可以返回一个新的函数cal1、apply可以调用函数

// 2.bind第一个参数是用于改变函数里面的this指向,其他的参数可以是参数列表

// 3.cal1第一个参数是用于改变函数里面的this指向,其他的参数可以是参数列表

// 4.apply只有2个参数第一个参数是用于改变函数里面的this指向第二个参数是数组

//   主要应用场景:

//  call 调用函数并且可以传递参数

//  apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值

//  bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.

异常处理

        // 异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行

        // 总结:

        // 1. throw 抛出异常信息,程序也会终止执行

        // 2. throw 后面跟的是错误提示信息

        // 3. Error 对象配合 throw 使用,能够设置更详细的错误信息

1.throw 抛异常

      function fn(x, y) {
            if (!x || !y) {
                // throw 阻止代码往下执行
                // throw‘参数不能为空!‘;
                throw new Error('参数不能为空! 人才')
            }
            return x + y
        }
        fn()

2.try..catch 捕获异常

        //         我们可以通过try / catch 捕获错误信息(浏览器提供的错误信息) try 试试 catch 拦住 finally 最后

        // 总结:

        // 1. try...catch 用于捕获错误信息

        // 2. 将预估可能发生错误的代码写在 try 代码段中

        // 3. 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息

        // 4. finally 不管是否有错误,都会执行

 

        function foo() {
            try {
                //查找 DOM 节点
                const p = document.querySelector('.p')
                p.style.color = 'red'
            } catch (error) {
                //try代码段中执行有错误时,会执行 catch 代码段
                //查看错误信息
                console.log(error.message)
                //终止代码继续执行
                return
            }
            finally {
                // 不管成功失败,代码都会执行到这里
                alert('执行')

            }
            console.log("‘如果出现错误,我的语句不会执行’")
        }
        foo()

3.debugger

debugger 语句调用任何可用的调试功能,例如设置断点。 如果没有调试功能可用,则此语句不起作用。

当 debugger 被调用时,执行暂停在 debugger 语句的位置。就像在脚本源代码中的断点一样。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值