让我们来开始下一部分吧

69 阅读6分钟

函数(下)

作用域(重点)

  • 什么是作用域,就是一个变量可以生效的范围
  • 变量不是在所有地方都可以使用的,而这个变量的使用范围就是作用域

全局作用域

  • 全局作用域是最大的作用域

  • 在全局作用域中定义的变量可以在任何地方使用

  • 页面打开的时候,浏览器会自动给我们生成一个全局作用域 window

  • 这个作用域会一直存在,直到页面关闭就销毁了

    // 下面两个变量都是存在在全局作用域下面的,都是可以在任意地方使用的
    var num = 100
    var num2 = 200
    

局部作用域

  • 局部作用域就是在全局作用域下面有开辟出来的一个相对小一些的作用域

  • 在局部作用域中定义的变量只能在这个局部作用域内部使用

  • JS 中只有函数能生成一个局部作用域,别的都不行

  • 每一个函数,都是一个局部作用域

    // 这个 num 是一个全局作用域下的变量 在任何地方都可以使用
    var num = 100
    
    function fn() {
      // 下面这个变量就是一个 fn 局部作用域内部的变量
      // 只能在 fn 函数内部使用
      var num2 = 200
    }
    
    fn()
    

变量使用规则(重点)

  • 有了作用域以后,变量就有了使用范围,也就有了使用规则
  • 变量使用规则分为两种,访问规则赋值规则

访问规则

  • 当我想获取一个变量的值的时候,我们管这个行为叫做 访问

  • 获取变量的规则:

    • 首先,在自己的作用域内部查找,如果有,就直接拿来使用
    • 如果没有,就去上一级作用域查找,如果有,就拿来使用
    • 如果没有,就继续去上一级作用域查找,依次类推
    • 如果一直到全局作用域都没有这个变量,那么就会直接报错(该变量 is not defined)
    var num = 100
    
    function fn() {
      var num2 = 200
      
      function fun() {
        var num3 = 300
        
        console.log(num3) // 自己作用域内有,拿过来用
        console.log(num2) // 自己作用域内没有,就去上一级,就是 fn 的作用域里面找,发现有,拿过来用
        console.log(num) // 自己这没有,去上一级 fn 那里也没有,再上一级到全局作用域,发现有,直接用
        console.log(a) // 自己没有,一级一级找上去到全局都没有,就会报错
      }
      
      fun()
    }
    
    fn()
    
  • 变量的访问规则 也叫做 作用域的查找机制

  • 作用域的查找机制只能是向上找,不能向下找

    function fn() {
      var num = 100
    }
    fn()
    
    console.log(num) // 发现自己作用域没有,自己就是全局作用域,没有再上一级了,直接报错
    

赋值规则

  • 当你想给一个变量赋值的时候,那么就先要找到这个变量,再给他赋值

  • 变量赋值规则:

    • 先在自己作用域内部查找,有就直接赋值
    • 没有就去上一级作用域内部查找,有就直接赋值
    • 还没有再去上一级作用域查找,有就直接赋值
    • 如果一直找到全局作用域都没有,那么就把这个变量定义为全局变量,再给他赋值
    function fn() {
      num = 100
    }
    fn()
    
    // fn 调用以后,要给 num 赋值
    // 查看自己的作用域内部没有 num 变量
    // 就会向上一级查找
    // 上一级就是全局作用域,发现依旧没有
    // 那么就会把 num 定义为全局的变量,并为其赋值
    // 所以 fn() 以后,全局就有了一个变量叫做 num 并且值是 100
    console.log(num) // 100
    

arguments:

  1. 一个在函数内天生的变量, 只要在函数内部, 不需要定义, 直接使用就可以了

  2. arguments 就是所有实参的集合

  3. 获取 arguments 的长度

  4. arguments 内某一个数据 , arguments[索引]

  5. arguments 内最后一位的索引, 一定是 arguments.length - 1

  6. arguments 的遍历

递归函数

  • 什么是递归函数

  • 在编程世界里面,递归就是一个自己调用自己的手段

  • 递归函数: 一个函数内部,调用了自己,循环往复

  • 递归弊端: 递归的次数大于一定次数,会导致内存中的栈溢出,也叫爆栈. (解决方案使用循环方式解决)

    解释: 函数调用的参数是通过栈空间来传递的,在调用过程中会占用线程的栈资源。而递归调用,只有走到 最后的结束点后函数才能依次退出,而未到达最后的结束点之前,占用的栈空间一直没有释放,如果递归调用次数过多,就可能导致占用的栈资源超过线程的最大值,从而导致栈溢出,导致程序的异常退出

    // 下面这个代码就是一个最简单的递归函数
    // 在函数内部调用了自己,函数一执行,就调用自己一次,在调用再执行,循环往复,没有止尽
    function fn() {
      fn()
    }
    fn()
    
  • 其实递归函数和循环很类似

  • 需要有初始化,自增,执行代码,条件判断的,不然就是一个没有尽头的递归函数,我们叫做 死递归

简单实现一个递归

  • 我们先在用递归函数简单实现一个效果

  • 需求: 求 1 至 5 的和

  • 思路: 函数自己调用自己 希望传一个数,函数返回当前1 -- 该数的求和

    • n =1; fn (1) =>1
    • n =2; fn (n) => 1+2=>fn(1)+2
    • n =3; fn (n) => 1+2+3 =>fn(2)+3
    • n =4; fn(n) => 1+2+3+4 = > fn(3)+4
    • n =5; fn(n) => 1+2+3+4 +5= > fn(4)+5
    • 结束
  • 开始书写,写递归函数先要写结束条件(为了避免出现 “死递归”)

    function add(n) {
      // 传递进来的是 1
      // 当 n ==1 的时候要结束
      if (n == 1) {
        return 1
      }
    }
    
    add(5)
    
  • 再写不满足条件的时候我们的递归处理

    function add(n) {
      // 当 n ==1 的时候要结束
      if (n ==1 ) {
        return 1
      } else {
        // 不满足条件的时候,就是当前数字 + 比自己大 1 的数字
        return n + add(n - 1) // 累计效果
      }
    }
    add(5)
    

案例2: 使用递归函数式实现一个数的阶乘 5!

  • 思路: 函数自己调用自己 希望传一个数,函数返回当前1 -- 该数的所有的数的乘积;
    • n=1; fn(1) = > 1
    • n=2; fn(2)=> 1*2 => fn(1) * 2
    • n=3; fn(3)=> 1* 2 * 3 => fn(2) * 3
    • n=4 fn(4)=> 1* 2 * 3 * 4=> fn(3) * 4
    • n=5; fn(5)=> 1* 2 * 3 * 4 * 5=> fn(4) * 5
fn(n){
	if(n==1){
		return 1
	}
	return n* fn(n -1)
}