• module2-02-函数的基本使用


    函数的基本使用

    一、函数的概念

    • 讲一段代码一起封装起来,内部封装的一段代码为一个结构体,执行就全都一起执行(遇到return除外),不执行就都不执行

    • 可以重复使用

    二、函数的声明、参数和调用

    2.1 函数声明

    • 函数声明又叫函数定义,必须先定义才能使用

      • 跟变量的命名要求相似

      • 不然会报引用错误

    • 语法

      • function 函数命 (参数) {
           封装的结构体
        }
    • 特点:生命的时候函数并不会执行,只有调用的时候才会被执行

    (1)函数的参数1

    • 一个函数可以设置0个或者多个参数,用逗号分隔

    • 可以接受任意数据类型

    function sum (a, b) {
       console.log(a + b)
    }
    sum(1, 2)

    (2)函数的参数2

    • 形式参数(形参):定义的()内的参数,本质是变量,接受实参

      • function aaa(a, b) {...}

      • a, b就是形参

    • 实际参数(实参):调用的()内的参数,本质是传递各种类型的数据,再传递给实参

      • aaa(c, d)

      • c, d是实参

    • 传参过程:实参 -> 形参 -> 函数内部调用

    (3)函数参数的优点

    • 只要关心传递什么参数就可以知道函数怎么使用

    2.2 函数调用

    • 调用方法:函数名()

    • 函数内部的语句执行的位置,与函数定义的位置无关(因为会命名提升),与调用位置有关

    • 可以一次定义多次(重复)执行

    三、函数的返回值

    • 函数能够接受数据,也可以接受一个返回值

    • return作为一个关键字,可以设置函数的返回值

      • 甚至可以将函数作为返回值

    3.1 作用

    • ① 函数执行到return,会立即停止后面代码的执行

    • ② return后面空格再定义一个字面量/表达式可以,即可以把整个函数变成一个表达式

      • 如果没有指定表达式或者没有return,函数运行会返回一个undefined

    3.2 应用

    • 如果有返回值,执行结果可以当成普通数据参与程序

    • 甚至可以当成一个实参赋值给一个函数的变量

    四、函数表达式和函数的数据类型

    4.1 函数的表达式

    • 是函数定义的另外一种方式,即定义一个匿名函数然后赋给一个变量

      • 也可以是一个具名函数

      • 但是如果这个具名函数声明的时候(如果在声明之前就定义了后面的就不成立)赋值给了一个新变量,则函数原名称不可以调用,而console.log新函数名称中的表达式函数名还是旧函数名,但是可以调用

    • 匿名函数:没有函数命

    • 语法

      • var foo1 = function () { console.log('foo1') }
        var foo2 = foo1
        // foo1 和 foo2 都可以调用
        // console.log(foo2)中的函数名是foo1
        var foo4 = function foo3 () { console.log('foo3') }
        // foo4可以调用,foo3不可以调用
        // foo4被console.log输出的话函数名是foo3
        var foo5 = function () { console.log('foo5') }
        // foor5被console.log输出的话没有函数名

    4.2 函数的数据类型

    • 函数是属于object中的一种复杂数据类型

    • 用typeof检测的话会返回‘function’

    五、arguments对象

    • js中,arguments是函数内置的一个属性,其储存了传递的所有实参(并不形参),arguments是一个伪数组,也可以进行遍历

      • 可以通过argumeng.length返回传递了多少实参

      • 相似的,函数.length可以返回定义了多少形参

    • 函数的实参个数可以跟形参个数不一致,但是实参都可以在arguments中找到

    案例

    • 定义一个求和函数,如果传入1个参数,返回它自己,如果传入两个参数,返回他们的和,如果传入三个参数,先比较前两个的大小,大得与第三个参数求和返回,如果传入4个及以上,输出错误提示

    function sum (a, b, c) {
       switch (arguments.length) {
           case 1:
               return a
               break
           case 2:
               return a + b
               break
           case 3:
               return a > b ? a + c : b + c
               break
           default:
               throw new Error('请输入3个以下参数!')
      }
    }

    六、函数递归

    • 函数里面调用自己,就是函数递归

    • 但是递归次数太多容易出现错误:超出计算机计算最大能力

      • 推荐尾部递归优化

    案例

    • 斐波那契数列:1,1,2,3,5,8...

    function fibo (num) {
       if (num === 1 || num === 2) {
           return 1
      } else {
           return fibo(num - 1) + fibo(num - 2)
      }
    }

    七、函数作用域

    7.1 作用域

    • 作用域:变量可以起作用的范围

      • 如果变量定义在一个函数内部,只能在函数内部被访问到,在函数外部不能使用这个变量,函数就是变量定义的作用域

    • 块级作用域:任何一对 {} 中的结构体都属于一个块,其里面就称为一个块级作用域

    • 在es5之前没有块级作用域的概念,只有函数作用域

    7.2 全局变量和局部变量

    • 局部变量:定义在函数内部的变量,只能在函数作用域被范围到

    • 全局变量:从广义上讲也是一种局部变量,定义在全局的变量,作用域范围是全局,所以js在任意位置都可以被访问

    • 变量退出作用域(函数调用完毕)之后都会被销毁,全局变量等到关闭网站或者浏览器才会销毁

    7.3 函数参数也是局部变量

    • 函数的参数也是是属于函数内的一个局部变量

    函数的作用域

    • 只能在这个函数内部被访问

    • 定义在另一个函数内部,如果外部函数没有执行,相当于内部代码没写

      • function foo1 () {
           var a = 1
           function foo2 () { ... }
        }
        foo2() // 这里调用foo2的话会报错

    7.4 作用域链和遮蔽效应

    (1)作用域链

    • 定义一个函数,都会生成一个作用域函数内部的函数,也会有一个新的作用域

    • 把这些所有作用域列出来,就会有一个函数内指向函数外的链式结构,叫作作用域链

    (2)遮蔽效应

    • 每个作用域都可以访问到外层作用域,若每作用域都有一个值,则会就近作用域依次往外层选取,直到找到第一个变量定义,并遮蔽更上层作用域的值

    • 案例

      • var a = 1
        function foo1 () {
           var a = 2
           function foo2 () {
               var a = 3
               console.log(a)
          }
           foo2()
           console.log(a)
        }
        foo1() // 3 2

    7.5 不写var关键字的影响

    • 比如在函数内部定义新变量不加var,则视为在全局作用域定义该变量。

    • 应该每次定义都书写var,否则会定义在全局,会污染全局变量

    八、预解析和变量提升

    8.1 预解析

    • 执行js代码的时候分为两个过程:预解析代码执行

    • 预解析

      • ① 把变量的声明提升到当前作用域的最前面

      • ② 把函数的生民给提升到当前作用域的最前面,只会提升声明,不会提升调用

      • ③ 先提升var,再提升function

    • JavaScript的代码执行过程:在预解析之后,根据新的代码顺序,从上往下按照既定规律执行js代码

    8.2 变量声明提升

    • 在预解析过成功,所有定义变量,都会将声明的过程提升到所在的作用域最上面

    • 只提升声明不提升变量赋值,相当于变量定义未赋值,变量存储undefined

    • 所以在定义之前使用该变量,不会报错,只会提示undefined

    console.log(a) // undefined
    var a = 1
    console.log(a) // 1

    相当于变成下面的过程

    var a
    console.log(a)
    a = 1
    console.log(a)

    8.3 函数声明提升

    • 在预解析过程中,所有定义的函数会将声明的过程提升到所在作用域最上面

    • 所以可以在声明语句之前就调用该函数

    foo() // 不会报错
    function foo () {}

    8.4 提升顺序

    • 先提升var,后提升function

    • 如果变量名和函数名相同,则函数名覆盖变量名,之后到了变量赋值,一直都是函数

    • 所以不建议变量名和函数名相同

    console.log(foo) // 输出函数
    foo() // 不会报错
    var foo = 1
    console.log(foo) // 1
    foo() // 会报错
    function foo () {}

    8.5 函数表达式的提升

    • 函数表达式进行的是变量提升,而不是函数声明提升,在赋值之前,会存储undefined先

    • 建议:最好用function来定义函数

    console.log(foo) // undefined
    var foo = function () {}
    console.log(foo) // 输出函数

    8.6 函数声明提升的引用

    • 开头先声明变量

    • 把赋值放在声明之后

    • 函数声明放在最后

    九、IIFE自调用函数

    • 及时调用表达式,也叫自调用函数,表示函数在定义的时候就立刻调用

    • 自调用方式:在函数声明之后立刻加()

      • 注意如果开头是()的话要加上分号!

    • 这种方法会把整段代码矮化成一个表达式

    function foo () {}() // 会报错
    var foo = function () {}() // 不会报错
    ;(function foo () {})() // 不会报错

    矮化成表达式

    • 算数运算符 + - () (* / %不能使用,因为前面必须有其它值)

    • 逻辑运算符: ! (|| &&不能使用,前面要有其它值)

    +function () { console.log(1) }() // 1
    -function () { console.log(1) }() // 1
    !function () { console.log(1) }() // 1
    ;(function () { console.log(1) })() // 1
    
    • 自调用的函数执行完就会销毁,外界无法访问函数名和里面定义的东西

    • IIFE使用的最多的是()加上匿名函数

  • 相关阅读:
    python 的class和def 定义执行语句相关
    python _和__ 下划线命名规则
    python2和python3编码问题【encode和decode】
    cpython源码阅读
    eCPRI
    python内存管理/垃圾回收
    Class() vs self.__class__()
    JAVA学习日报 11/24
    JAVA学习日报 11/23
    JAVA学习日报 11/22
  • 原文地址:https://www.cnblogs.com/lezaizhu/p/14093698.html
Copyright © 2020-2023  润新知