javaScript之函数详解(基础版)

函数

  • 在 JS 里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用。

  • 虽然 for循环语句也能实现一些简单的重复操作,但是比较具有局限性,此时我们就可以使用 JS 中的函数。

函数的概念

  • 函数:就是封装了一段可被重复调用执行的代码块

  • 目的:就是让大量代码被重复使用。

  • 在想要让这段代码执行的时候,直接执行这个代码块里面的代码就行

  • 例如:

    // 这个是一段代码
    for (var i = 0; i < 10; i++) {
      console.log(i)
    }
    
    // 函数,这个 {} 就是那个代码块
    function fn() {
      // 这个函数我们之前写的代码
      for (var i = 0; i < 10; i++) {
        console.log(i)
      }
    }
    
    //fn  函数名不加括号代表整个函数
    //fn()   函数名加括号代表函数的调用
    

函数的两个阶段(重点)

  • 函数在使用时分为两步:声明函数调用函数
声明函数阶段
  • 定义阶段就是把代码放进 代码块

  • 有两种定义方式 声明式赋值式

声明式
  • 使用 function 这个关键字来声明一个函数

  • 因为有名字,所以也被称为命名函数

  • 调用函数的代码既可以放到声明函数的前面,也可以放在声明函数的后

  • 语法:

    //声明函数
    function fn() {
      // 函数体代码
    }
    // function: 声明函数的关键字(必须小写),表示接下来是一个函数了
    // fn: 函数的名字,我们自己定义的(遵循变量名的命名规则和命名规范)
    // (): 必须写,是用来放参数的位置
    // {}: 就是我们用来放一段代码的位置(代码块)
    
    
  
  - 由于函数一般是为了实现某个功能才定义的, 所以通常我们将函数名命名为动词,比如 getSum

##### 赋值式

- 其实就是和我们使用 `var` 关键字是一个道理了

- 首先使用 `var` 定义一个变量,把一个函数当作值直接赋值给这个变量就可以了

- 因为函数没有名字,所以也被称为匿名函数 

- 这个fn 里面存储的是一个函数

-  函数表达式方式原理跟声明变量方式是一致的

- 函数调用的代码必须写到函数体后

- 语法: 

  ```javascript
  var fn = function () {
    // 一段代码
  }
  // 不需要在 function 后面书写函数的名字了,因为在前面已经有了
函数调用阶段
  • 就是让 代码块 里的代码执行一下
  • 让函数执行
  • 两种定义函数的方式不同,但是调用函数的方式都以一样的
  • 函数不调用,自己不执行
调用一个函数
  • 函数调用就是直接写 函数名() 就可以了

    // 声明式函数
    function fn() {
      console.log('我是 fn 函数')
    }
    
    // 调用函数
    fn()   //通过调用函数名来执行函数体代码
    
    // 赋值式函数
    var fn2 = function () {
      console.log('我是 fn2 函数')
    }
    
    // 调用函数
    fn()
    
    • 注意: 声明函数本身并不会执行代码,只有调用函数时才会执行函数体代码
调用上的区别
  • 虽然两种定义方式的调用都是一样的,但是还是有一些区别的

  • 声明式函数: 调用可以在 定义之前或者定义之后

    // 可以调用
    fn()
    
    // 声明式函数
    function fn() {
      console.log('我是 fn 函数')
    }
    
    // 可以调用
    fn()
    
  • 赋值式函数: 调用只能在 定义之后

    // 会报错
    fn()
    
    // 赋值式函数
    var fn = function () {
      console.log('我是 fn 函数')
    }
    
    // 可以调用
    fn()
    

例子🌰: 利用函数计算1-100之间的累加和

 // 1. 声明函数
        function getSum() {
            var sum = 0
            for (var i = 1; i <= 100; i++) {
                sum += i
            }
            console.log(sum)

        }
        // 2. 调用函数
        getSum()

函数的参数(重点)

  • 在声明函数时,可以在函数名称后面的小括号()中添加一些参数,这些参数被称为形参,而在调用该函数时, 同样也需要传递相应的参数,这些参数被称为实参

  • 参数的作用 : 在函数内部某些值不能固定,我们可以通过参数在调用函数时传递不同的值进去。

    参数说明
    形参形式上的参数,函数定义的时候 传递的参数 当前并不知道是什么
    实参实际上的参数,函数调用的时候传递的参数 实参时传递给形参的
  • 参数分为两种 形参实参

    // 声明式
    function fn() {
      // 一段代码
    }
    
    fn(实参)
    
    // 赋值式函数
    var fn = function (行参) {
      // 一段代码
    }
    fn(实参)
    
行参和实参的作用
  1. 行参

    • 在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参。

    • 就是在函数内部可以使用的变量,在函数外部不能使用

    • 每写一个单词,就相当于在函数内部定义了一个可以使用的变量(遵循变量名的命名规则和命名规范)

    • 多个单词之间以 , 分隔

      // 带参数的函数声明
      function 函数名(形参1, 形参2 , 形参3...) { // 可以定义任意多的参数,用逗号分隔
       // 函数体
      }
      // 带参数的函数调用
      函数名(实参1, 实参2, 实参3...)
      
    • 如果只有行参的话,那么在函数内部使用的值个变量是没有值的,也就是 undefined

    • 行参的值是在函数调用的时候由实参决定的

  2. 实参

    • 在调用该函数时, 需要传递相应的参数,这些参数被称为实参。

    • 也就是说调用的时候,实参值是传递给形参的;即,在调用的时候是给形参一个实际的内容的

      function fn(num) {
        // 函数内部可以使用 num 
      }
      
      // 这个函数的本次调用,书写的实参是 100
      // 那么本次调用的时候函数内部的 num 就是 100
      fn(100) 
      
    • 函数内部的行参的值,由函数调用的时候传递的实参决定

    • 多个参数的时候,是按照顺序一一对应的

      function fn(num1, num2) {
        // 函数内部可以使用 num1 和 num2
      }
      
      // 函数本次调用的时候,书写的参数是 100 和 200
      // 那么本次调用的时候,函数内部的 num1 就是 100,num2 就是 200
      fn(100, 200)
      
参数个数的关系
参数的个数说明
实参个数等于形参个数输出结果
实参个数多余形参个数只取到形参的个数
实参个数小于形参个数多的形参定义为undefined,结果为NaN
  1. 行参比实参少

    • 因为是按照顺序一一对应的

    • 行参少就会拿不到实参给的值,所以在函数内部就没有办法用到这个值

      function fn(num1, num2) {
        // 函数内部可以使用 num1 和 num2
      }
      
      
      fn(100, 200) // 形参和实参个数相等,输出正确结果
      
      // 100 对应了 num1,200 对应了 num2,300 没有对应的变量
      fn(100, 200, 300) // 实参个数多于形参,只取到形参的个数
      
  2. 行参比实参多

    • 因为是按照顺序一一对应的

    • 在JavaScript中,形参的默认值就是 undefined

      function fn(num1, num2, num3) {
        // 函数内部可以使用 num1 num2 和 num3
      }
      // 100 对应了 num1, num2没有相对应的实参
      fn(200) // 实参个数少于形参,多的形参定义为undefined,结果为NaN
      

函数的return(重点)

  • return 返回的意思,其实就是给函数一个 返回值终断函数

  • 语法:

    // 声明函数
    function 函数名(){
     ...
     return 需要返回的值 或者 中断函数的位置;
    }
    // 调用函数
    函数名() // 此时调用函数就可以得到函数体内return 后面的值
    
终断函数
  • 当函数开始执行以后,函数内部的代码就会从上到下的依次执行,直到函数内的代码执行完毕

  • return 关键字就是可以在函数中间的位置停掉,让后面的代码不在继续执行

    function fn() {
      console.log(1)
      console.log(2)
      console.log(3)
      
      // 写了 return 以后,后面的 4 和 5 就不会继续执行了
      return
      console.log(4)
      console.log(5)
    }
    
    // 函数调用
    fn()
    
返回值
  • 我们函数只是实现某种功能,最终的结果需要返回给函数的调用者函数名() 通过return 实现的

  • 只要函数遇到return 就把后面的结果 返回给函数的调用者 函数名() = return后面的结果

  • 函数调用本身也是一个表达式,表达式就应该有一个值出现

  • 如果不return一个值,那么在函数执行完毕之后,是不会有结果出现

    // 比如 1 + 2 是一个表达式,那么 这个表达式的结果就是 3
    console.log(1 + 2) // 3
    
    function fn() {
      // 执行代码
    }
    
    // fn() 也是一个表达式,这个表达式就没有结果出现
    console.log(fn()) // undefined
    
  • return 关键字就是可以给函数执行完毕一个结果

    function fn() {
      // 执行代码
      return 100
    }
    
    // 此时,fn() 这个表达式执行完毕之后就有结果出现了
    console.log(fn()) // 此时打印100 , 因为 return 语句会把自身后面的值返回给调用者,此时调用者fn
    
    • 我们可以在函数内部使用 return 关键把任何内容当作这个函数运行后的结果

    • 在使用 return 语句时,函数会停止执行,并返回指定的值

函数的优点

  • 函数就是对一段代码的封装,在我们想调用的时候调用
  • 函数的几个优点
    1. 封装代码,使代码更加简洁
    2. 复用,在重复功能的时候直接调用就好
    3. 代码执行时机,随时可以在我们想要执行的时候执行

argument的使用

  • 当不确定有多少个参数传递的时候,可以用 arguments 来获取。

  • 在 JavaScript 中,arguments 实际上 它是当前函数的一个内置对象。

  • 所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的 所有实参。

  • arguments展示形式是一个伪数组,因此可以进行遍历。

  • 伪数组(不是真正意义上的数组)具有以下特点:

    • 具有 length 属性
    • 按索引方式储存数据
    • 不具有数组的 push , pop 等方法
     function fn() {
                // console.log(arguments); // 里面存储了所有传递过来的实参  arguments = [1,2,3]
         //可以用arguments.length来判断函数中实参的个数(也就是数组的长度)
                // console.log(arguments.length);
                // console.log(arguments[2]);
                // 可以按照数组的方式遍历arguments
         
                for (var i = 0; i < arguments.length; i++) {
                    console.log(arguments[i]);
    
                }
            }
            fn(1, 2, 3);
            fn(1, 2, 3, 4, 5);
    

作用域(重点)

  • 作用域指一个变量的作用的范围 。通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

  • 作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。

全局作用域

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

  • **全局作用域的概念:**直接写在 script 标签中的 JavaScript 代码都是全局作用域。

  • 当页面打开运行时全局作用域就会自动创建,而当页面关闭时就会销毁。

  • 在全局作用域中有一个全局的 window 对象可以使用,而所有全局作用域对象都会作为 window 对象的属性来使用。

    // 变量都是存在在全局作用域下面的,都是可以在任意地方使用的
    var num = 10
    var age = 20
    

局部作用域(函数作用域)

  • 局部作用域:由于跟函数有关,所以也称为函数作用域。

  • 局部作用域:是在调用函数时才会被创建,函数执行完毕后就自动销毁

  • 而且,每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的。

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

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

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

自动全局作用域

  • 如果您为尚未声明的变量赋值,此变量会自动成为全局变量。

    function fn() {
        //此时这个num 没有声明 所以它自动成为全局变量
                    num = 10
                    console.log(num)
                }
                fn()
                console.log(num)
    
  • 注意:如果在函数中定义变量时没有指定 var 关键字,那么这个变量会自动提升为全局作用域的变量。

从执行效率来看全局变量

  • 全局变量只有浏览器关闭的时候才会销毁,比较占内存资源
  • 局部变量当我们程序执行完毕就会销毁, 比较节约内存资源

作用域链(重点)

  • 作用域链就是执行环境中变量对象中变量和函数访问的顺序。

  • 作用域的集合就是作用域链(子集可以访问父集,父集不能访问子集)

  • 在局部作用域中可以访问全局作用域,而在全局作用域中不能访问局部作用域。

  • 当在局部作用域中使用一个变量时,它会先在自身作用域内查找,如果找到就直接使用,如果没有找到则会向上一级作用域查找,直到找到全局作用域为止。如果都没有找到则会报错。

  • 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值 这种结构我们称为作用域链 就近原则

    var num = 10
    
            function fn() { // 外部函数
                var num = 20
    
                function fun() { // 内部函数
                    console.log(num)//站在目标层 一层一层往外查找 先在自身函数里寻找对应的变量,没找到
                //然后跳出自身函数取上一层找,找到了,使用,这时num=20
              //但是如果这时候在外一层还没有找到就会继续向上找
    
                }
                fun()//因为调用了fun函数是打印20
            }
            fn() //20  
    

    一个小理解

    • 全局的变量就是window的属性
    • 全局的函数就是window的方法

块级作用域(了解,es6新增)

  • 块级作用域可通过新增命令 let 和 const 声明,所声明的变量在指定块的作用域外无法被访问。
  • 块级作用域在如下情况被创建:
    • 在一个函数内部
    • 在一个代码块(由一对{}包裹)内部
    • let 声明的语法与 var 的语法一致。
    • 基本上可以用 let 来代替 var 进行变量声明,但会将变量的作用域限制在当前代码块中。
  • 块级作用域有以下几个特点:
    • 声明变量不会提升到代码块顶部
    • let/const 声明并不会被提升到当前代码块的顶部,因此你需要手动将 let/const 声明放置到顶部,以便让变量在整个代码块内部可用。

递归函数

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

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

    // 在函数内部调用了自己,函数一执行,就调用自己一次,在调用再执行,循环往复,没有止尽
    function fn() {
      fn()
    }
    fn()
    
  • 其实递归函数和循环很类似

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

简单实现一个递归

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

  • 需求: 求 1 至 5 的和

    • 先算 1 + 2 得 3
    • 再算 3 + 3 得 6
    • 再算 6 + 4 得 10
    • 再算 10 + 5 得 15
    • 结束
  • 开始书写,写递归函数先要写结束条件(为了避免出现 “死递归”)

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

    function add(n) {
      // 传递进来的是 1
      // 当 n === 5 的时候要结束
      if (n === 5) {
        return 5
      } else {
        // 不满足条件的时候,就是当前数字 + 比自己大 1 的数字
        return n + add(n + 1)
      }
    }
    add(1)
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值