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使用的最多的是()加上匿名函数

posted @ 2020-12-06 18:48  叻仔猪  阅读(84)  评论(0编辑  收藏  举报