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


    目录

    递归

    递归函数

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

    递归实现阶乘

    递归实现数组扁平化

    深浅拷贝

    浅拷贝

    深拷贝

    处理this指向

    this指向

    改变this指向

    异常处理

    1.throw 抛异常

    2.try..catch 捕获异常

    3.debugger


    递归

    递归函数

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

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

    1. function fn(n) {
    2. console.log(n);
    3. n -= 2
    4. if (n > 0) {
    5. fn(n)
    6. }
    7. console.log(n);
    8. }
    9. fn(10)//

    详细图解如下:

     

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

            // 🏆斐波那契额数列

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

             1 1 2 3 5 8 13

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

    */

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

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

     

    递归实现阶乘

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

    递归实现数组扁平化

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

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

    1. const arr = [1, 2, 3, [4, 5, [6], 7,[8,[9,[10]]]]]
    2. const newArr = []
    3. function flat(data) {
    4. data.forEach(item => {
    5. // 判断 该元素是不是 数组
    6. if (item instanceof Array) {
    7. // 如果是数组 继续递归
    8. flat(item)
    9. } else {
    10. // 如果不是数组 则把这个元素放入新的数组中
    11. newArr.push(item)
    12. }
    13. })
    14. }
    15. flat(arr)
    16. console.log(newArr);

    深浅拷贝

    浅拷贝

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

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

            // 常见方法:

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

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

    1. const obj1 = {
    2. uname: '张三',
    3. age: 18,
    4. gender: "男",
    5. gfs: ['凤姐', "芙蓉姐姐", '黄蓉'],
    6. wife: {
    7. w1: '蔡徐坤',
    8. w2: 'ikun'
    9. }
    10. }
    11. // 浅拷贝 只拷贝对象的 第一层 如果第一层 有引用类型 拷贝的内存地址 如果是简单类型 拷贝的值
    12. // 💎1.使用展开运算符拷贝
    13. // const obj2 = { ...obj1 };
    14. // 💎2.使用Object.assign()拷贝
    15. const obj2={}
    16. Object.assign(obj2,obj1)
    17. obj2.uname = '王雄厚'
    18. obj2['wife']['w1'] = '迪丽热巴'

     详细图解如下:

     

    深拷贝

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

            // 常见方法:

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

            // 2. lodash/cloneDeep

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

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

            // 函数递归:

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

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

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

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

    1. const obj1 = {
    2. uname: '张三',
    3. age: 18,
    4. gender: "男",
    5. gfs: ['凤姐', "芙蓉姐姐", '黄蓉'],
    6. wife: {
    7. w1: '蔡徐坤',
    8. w2: 'ikun'
    9. }
    10. }
    11. function deepCopy(newObj, oldObj) {
    12. for (let k in oldObj) {
    13. const item = oldObj[k]
    14. // 判断是不是数组
    15. if (item instanceof Array) {
    16. newObj[k] = []
    17. // 递归
    18. deepCopy(newObj[k], item)
    19. } else if (item instanceof Object) {
    20. newObj[k] = {}
    21. // 递归
    22. deepCopy(newObj[k], item)
    23. }
    24. else {
    25. // 🏆对象名[新属性名] = 新值
    26. newObj[k] = item
    27. }
    28. }
    29. }
    30. obj2 = {}
    31. deepCopy(obj2, obj1)
    32. obj2.uname = '王雄厚'
    33. obj2['wife']['w1'] = '迪丽热巴'

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

    lodashjs安装地址和中文文库

    1. <script src="./js/lodash.min.js"></script>
    2. <script>
    3. const obj1 = {
    4. uname: '张三',
    5. age: 18,
    6. gender: "男",
    7. gfs: ['凤姐', "芙蓉姐姐", '黄蓉'],
    8. wife: {
    9. w1: '蔡徐坤',
    10. w2: 'ikun'
    11. }
    12. }
    13. const obj2 = _.cloneDeep(obj1)
    14. 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:传递的值,必须包含在数组里面

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

    1. // function f(x, y) {
    2. // console.log(this, x, y);
    3. // }
    4. // const obj = {
    5. // uname: 'zs'
    6. // }
    7. // f.apply(obj, [20, 34])
    8. //  因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
    9. //求数组最大值
    10. // const arr = [3, 5, 2, 9]
    11. // console.log(Math.max.apply(null, arr))//9 利用apply
    12. // 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指向

    1. function f(x, y, z) {
    2. console.log(this, x, y, z);
    3. }
    4. const obj = {
    5. uname: 'zs',
    6. age: 18
    7. }
    8. const ff = f.bind(obj, 10, 20, 30)//返回一个新的函数
    9. 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 抛异常

    1. function fn(x, y) {
    2. if (!x || !y) {
    3. // throw 阻止代码往下执行
    4. // throw‘参数不能为空!‘;
    5. throw new Error('参数不能为空! 人才')
    6. }
    7. return x + y
    8. }
    9. fn()

    2.try..catch 捕获异常

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

            // 总结:

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

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

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

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

     

    1. function foo() {
    2. try {
    3. //查找 DOM 节点
    4. const p = document.querySelector('.p')
    5. p.style.color = 'red'
    6. } catch (error) {
    7. //try代码段中执行有错误时,会执行 catch 代码段
    8. //查看错误信息
    9. console.log(error.message)
    10. //终止代码继续执行
    11. return
    12. }
    13. finally {
    14. // 不管成功失败,代码都会执行到这里
    15. alert('执行')
    16. }
    17. console.log("‘如果出现错误,我的语句不会执行’")
    18. }
    19. foo()

    3.debugger

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

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

  • 相关阅读:
    关于java运行中抛出异常的几种写法
    光电柴微电网日前调度报告
    【论文阅读 05】图像异常检测研究现状综述
    【学习笔记】深度学习分布式系统
    VR全景数字工厂,制造业企业线上营销新助手
    CAD for JS:VectorDraw web library 10.1004.1 Crack
    2023年高级威胁预测:邮件服务器成优先攻击目标
    CSDN 重新开放付费资源的上传了,但要求如下
    Web开发中会话跟踪的隐藏表单字段(隐藏input)方法
    洛谷P1607 Fair Shuttle G
  • 原文地址:https://blog.csdn.net/wusandaofwy/article/details/126454402