(javascript)(基础知识+实例) 5.函数回顾,作用域递归

114 阅读5分钟

函数

  • 存储一段代码

  • 定义(声明)

        function 函数名(a, b){
            /* 
                var a
                var b
                a = 1
                b = 2
             */
            // 一段代码
        }
    
  • 调用(执行)

    • 函数名(1,2)
  • 函数的优点

    1. 复用
    2. 维护
  • 函数参数

    • 含义
    • 形参: 其实就是在函数内部声明一个变量
    • 实参: 其实就是给形参赋值
    • 个数关系
  • 返回值

    • 含义:函数执行之后得到结果
    • 有没有返回值?
      • 看函数体内有没有写return 值
      • 默认函数返回值undefined
    • 什么时候使用?
      • 一般函数调用之后,需要得到函数执行的结果 这个结果用于下一步运算或者判断
  • return

    1. 返回值
    2. 终止函数的执行

函数定义方式

  • 声明式定义
        function 函数名(){
            // 一段代码
        }
    
  • 赋值式定义
        var 函数名 = function(){
            // 一段代码
        }
    

预解析、预编译

  • 正常我们写完的js直接打开浏览器就能够运行,运行之后就能看到运行的结果
  • 浏览在运行js之前会提前将整个js的代码通读,会挑出重点的内容提前执行,这个过程就是预解析的过程
  • 重点
    1. var声明的变量会提前声明
    2. 声明式函数会提前定义
  • 影响
    1. var声明的变量可以提前使用但是他的值undefined,因为只会将定义提前,不会将赋值提前
    2. 声明式的函数可以在定义之前调用

作用域

  • 含义: 一个变量或者函数起作用的范围
  • 分类:
    1. 全局作用域: 不被任何一个函数包裹的区域就称为全局作用域
    2. 局部作用域: 被一个函数所包裹的区域就称为局部作用域
  • 变量分类
    1. 全局变量: 在全局作用域定义的变量称为全局变量
      • 全局变量可以在全局使用,也可以在局部使用
    2. 局部变量: 在局部作用域定义的变量称为局部变量
      • 局部变量只能在当前自己的局部使用,无法在全局或者其他的局部使用

作用域的访问(就近、往上)

  • 在使用变量的时候遵循的原则
  • js在使用一个变量的时候 会优先在自身作用域里面查找,如果自身有就使用自身
  • 如果自身没有往上一级作用域查找,上一级有就使用上一级的
  • 上一级没有就在往上上一级,一直找到全局作用域,如果全局有就使用全局,全局没有就会报错

作用域的赋值

  • 在给一个变量赋值的时候遵循的原则

  • js在给一个变量赋值的时候 会优先看当前自身的作用域有没有定义 如果有就给当前自身的作用域变量赋值,不会往上赋值了

  • 如果自身没有定义,会往上一级查找,看上一级作用域里面有没有定义这个变量,如果上一级有定义,就给上一级作用域里面的变量赋值

  • 如果上一级没有定义,再往上上一级查找,一直找到全局作用域,如果全局作用域里面有定义,就给全局作用域里面变量赋值,全局没有定义,会自动在全局声明一个变量,然后给这个变量赋值。

  • 特殊函数

    • 匿名函数: 将一个函数当成一个数据赋值给某一个变量

      • 事件处理函数多为一个匿名函数
          function(){
      
          }
      
    • 自执行函数:在函数定义的时候就会自动调用函数

      • 将一段封装在一个函数内部 封装的时候需要立即执行这段代码
          (function(){
              // 此处代码在定义的位置就会执行
          })()
          (function getSum(){
              // 此处代码在定义的位置就会执行
          })()
      
    • 递归函数(js的重难点)

      • 函数在内部调用自己
      • 抛出错误Maximum call stack size exceeded
      • 递归有两个过程,第一个就是传递的过程,第二个返回的过程
        • 传递的过程中,没有得到结果的函数会在call stack(调用栈)等待
        • 返回的过程中,得到结果的函数就在一个一个出栈
    • 递归优点

      1. 可以解决循环不能解决的问题
    • 递归缺点

      • 每一次递归都会导致函数在call stack等待,如果等待的函数超出了call stack的最大长度 就会报错
    • 递归使用场景

      1. 对象深克隆
      2. 遍历dom

数组(Array)

  • js中最重要的数据类型
  • 数组是一种复杂数据类型
  • 数组是一个有序的数据集合,有序是体现在下标(索引)上面
  • 往数组里面放一个数据,自动生成一个下标,从0开始
  • 数组创建
    1. 字面量方式
      • var 变量名 = []
    2. 内置构造函数方式
      • var 变量名 = new Array()
  • 如何往数组里面添加数据
    1. 可以通过下标
      • 数组名[下标] = 数据
    2. 可以通过方法来添加
      • 数组名.push(数据)
      • 数组名.unshift(数据)
  • 如何获取数组的长度
    • 数组名.length
  • 如何取出数组里面的数据
    • 下标法
      • 下标的范围 [0 - 数组长度-1]
      • 数组名[下标]
      • 越界访问 不会报错 得到的是undefined
    • 通过方法获取数据
      • 数组.pop() 取出数组的最后一位
      • 数组.shift() 取出数组的第一位
      • 会改变数组的长度

实例

(以下代码均为课程实例)

(1)函数的两种定义方式

    <script>
      /* 
            声明式
                可以在定义之前和定义之后使用
        */
      getSum();
      function getSum() {
        console.log("求和函数");
      }
      getSum();

      /* 
            赋值式
                只能在定义之后使用
       */
      getSum01()
      var getSum01 = function () {
        console.log("求和函数01");
      };

      getSum01();
    </script>

(2)作用域

    <script>
        // a是在全局定义的
        var a = 1
        function foo(){
            // b是在函数内部定义的
            var b = 2
            // 变量a可以在函数内部使用
            console.log(a)  // 1
            // 变量b可以在函数内部使用
            console.log(b) // 2
        }
        foo()
        // 变量a也可以在函数外部(全局)使用
        console.log(a) // 1
        // 变量b在函数外部无法使用
        console.log(b) // b is not defined

    </script>

(3)作用域误区

    <script>
        /* 
            相当于自动在全局 var b
            给这个变量赋值 b = 1
        */
        function foo(){
            b = 1 // 只是给b赋值了 没有定义b 所以b不是局部变量
            console.log(b)
        }
        foo()
        console.log(b)
    </script>

(4)作用域的赋值原则

    <script>
        // var a = 1
        function foo(){
            // var a = 2
            function baz(){
                // var a = 3
                a = 4 // 给变量a重新赋值
                console.log(a) // 4
            }
            // console.log(a) // 1
            baz() // 只有在调用baz之后a才会变化
            console.log(a) // 4
        }
        foo()
        console.log(a) // 4
    </script>

(5)使用递归求10!

    <script>
        // 累计去乘
        // 10! = 10 * 9 * 8 *...*3*2*1 
        // jieCheng(10) = jieCheng(9) * 10
        // jieCheng(9) = jieCheng(8) * 9
        // jieCheng(8) = jieCheng(7) * 8
        // jieCheng(7) = jieCheng(6) * 7
        // ...
        // jieCheng(2) = jieCheng(1) * 2
        // jieCheng(1) = 1
        function jieCheng(n){
            // 递归必须要有结束条件 否则就会出现死递归
            if(n === 1){
                return 1
            }
            debugger // 相当在这个打一个断点 程序每一次运行到这个地方的时候都会停止一下
            return jieCheng(n-1) * n
        }
        console.log(jieCheng(1000000))
    </script>

(6)求斐波那契数列的第n项

    <script>
        function fb(n){
            // 一般递归先写结束条件
            if(n==1 || n==2){
                return 1
            }
            debugger
            return fb(n-1) + fb(n-2)
        }

        // fb(10)=fb(9) + fb(8)
        // fb(9)=fb(8) + fb(7)
        // fb(8)=fb(7) + fb(6)
        // fb(7)=fb(6) + fb(5)
        // fb(6)=fb(5) + fb(4)
        // fb(5)=fb(4) + fb(3)
        // fb(3)=fb(2) + fb(1)
        console.log(fb(1))
        console.log(fb(2))
        console.log(fb(10))
    </script>

(7)求最大公约数

    <script>
        /* 
            公约数
            约数 倍数
            最大公约数

            算法: 拿大数除以小数 判断余数 如果余数为0 那么小数就是他们的最大公约数
            不为0 继续拿小数除以余数  一直算到余数为0
            10  1 2 5 10
            12  1 2 3 4 6 12

            10  5    0
            12 10  2
            10 2   0

            20  8  4
            8   4  0
            25  15  10
            15  10  5
            10  5   0
        */

        function gys(m,n){
            // 判断m和n的大小 如果m>n交换位置
            if(m>n){
                var temp = null
                temp = m
                m = n
                n = temp
            }
            // m一定比n小
            var yu = n%m
            if(yu==0){
                // 如果余数为0 那么小数就是他们的最大公约数
                return m
            }
            // 到这个代表 n除以m余数不为0
            // 继续拿小数除以余数
            return gys(yu, m)
        }
        console.log(gys(10,5)) // 5
        console.log(gys(25,15)) // 5
        console.log(gys(15,25)) // 5
    </script>

(8)录入成绩的系统

    <script>
      
        // 定义一个数组存储成绩
        var scoreList = []
        // 录入6个同学
        for(var i=1;i<=6;i++){
            // 提示用户输入
            var score = prompt('请输入成绩')
            // 输一个录一个
            scoreList.push(score)
        }

        console.log(scoreList)
    </script>

(9)生成四位随机的验证码 0000 - 9999 放到数组里面

    <script>
        // 生成四位随机的验证码  0000 - 9999 放到数组里面

        // function suiji(m, n) {
        //    return (parseInt(Math.random() * (n - m + 1)) + m)
        // }
        // var a = suiji(0000, 9999)
        // console.log(a)
        // var shuzu = []
        // shuzu[0]=a
        // console.log(shuzu)
        // var a = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
        // var code = "";
        // for (var i = 0; i < 4; i++) {
        //     //0-1的随机小数 -->  0~数组长度-1的范围   取整
        //     var randPosition = Math.floor(Math.random() * (a.length - 1));  //每次生成一个随机数的位置
        //     code += a[randPosition];//带有随机位置作为下标,指示到当前随机产生的某个字符上,
        // }
        // alert(code);
        // while (1) {


        //     var ss = prompt("请输入验证码");
        //     if (ss == code) {
        //         alert("输入正确");
        //         break;
        //     }
        //     else {
        //         alert("输入错误");

        //     }
        // }
        // 生成四位随机的验证码  0000 - 9999 放到数组里面
        // var arr=[]
        // arr.push(String(parseInt(Math.random()*10))+String(parseInt(Math.random()*10))+String(parseInt(Math.random()*10))+String(parseInt(Math.random()*10)))
        // console.log(arr)
        var str = []
        for (var i= 1; i <= 4; i++) {
            str.push(parseInt(Math.random() * 10))
        }
        console.log(str)
    </script>

(10)生成四位不重复随机的验证码 0000 - 9999 放到数组里面

    <script>
        // 生成四位不重复随机的验证码  0000 - 9999 放到数组里面

        var str = []
        for (var i = 1; i <= 4; i++) {
            var n = Math.floor(Math.random() * 10)
            if (str.indexOf(n) === -1) {     //str.indexOf(n)识别是否和n一样的字符,如果无 则输出-1
                str.push(n)
            } else {
                i--
            }
        }
        console.log(str)
    </script>

(11)定义一个数组存储成绩,录入6个同学, 提示用户输入,输一个录一个, 求出输入的平均数

    <script>

        // 定义一个数组存储成绩
        var scoreList = []
        // 录入6个同学
        for (var i = 1; i <= 6; i++) {
            // 提示用户输入
            var score = prompt('请输入成绩')
            // 输一个录一个
            scoreList.push(score)
        }
        console.log(scoreList)
        // 求出输入的平均数

        function avg(array) {//封装求平均值函数
            var sum = 0;
            for (var i = 0; i <= 6; i++) {
                sum += array[i];
            }
            return sum/6;
        }
        console.log(avg(scoreList));
    </script>