JavaScript函数与作用域

函数的作用是具有相同或相似逻辑的代码抽取出进行封装,执行特定任务的代码块(复用)。

比如我们前面使用的 alert()prompt()console.log()都是一些js 函数,只不过已经封装好了,我们直接使用的。

具名函数

 <script>
    // 函数声明
    function sayHi() {
      console.log('嗨~~~');
    }

    // 调用2次,可以任意调用
    sayHi()
    sayHi()
  </script>
  <script>
    // 函数声明 函数名 sheet99()
    function sheet99() {
      // 还是九九乘法表
      for (let i = 1; i <= 9; i++) {
        for (let j = 1; j <= i; j++) {
          document.write(`<span> ${j} X ${i} = ${i * j}</span>`)
        }
        document.write('<br>')
      }
    }

    // 调用2次,可以任意调用
    sheet99()
    sheet99()
  </script>

函数名命名规范

和变量命名基本一致
尽量小驼峰式命名法
前缀应该为动词

动词含义
can判断是否可执行某个动作
has判断是否含义某个值
is判断是否为某个值
get获取某个值
set设置某个值
load加载某些数据

函数传参

思考:这样的函数只能求10+20,这个函数功能局限非常大!

办法:把要计算的数字传到函数内。若函数完成功能需要调用者传入数据,那么就需要用有参数的函数。

形参:声明函数时写在函数名右边小括号里的叫形参(形式上的参数)

实参:调用函数时写在函数名右边小括号里的叫实参(实际上的参数)

形参可以理解为是在这个函数内声明的变量(比如 num1= 10)实参可以理解为是给这个变量赋值

  <script>
    // 调用()里叫形参,可以随意传单个或多个参值,相当在这个函数内声明的变量 
    // 1. start = 1 ; end = 50
    // 2. start = 20 ; end = 100
    // 3. start = 105
    function getSum(start, end) {
      let sum = 0
      for (let i = start; i <= end; i++) {
        sum = sum + i
      }
      console.log(sum)
    }
    // 调用()叫实参,getSum(50)中数会传给上面封装的代码块,
    getSum(1, 50)  // 1275
    getSum(20, 100) // 4860

    getSum(105) // 也设置一个或多个参数,注意要逗号隔开
  </script>

案例:函数封装求和

需求:采取函数封装的形式:输入2个数,计算两者的和,打印到页面中。

  <script>
    function getSum(num1, num2) {
      document.write(num1 + num2)
    }
    getSum(1, 9)  // 10
  </script>

参数默认值

形参可以看做变量,但是如果一个变量不给值,默认是 undefined(未知);

如果做用户不输入实参,则出现 undefined + undefined 结果是NaN ,我们可以改进下:

用户不输入实参,可以给形参默认值,可以默认为0,这样程序更严谨。

  <script>
    // 形参给个0,不管实参给不给值都能参与运算互不影响
    function getSum(x = 0, y = 0) {
      document.write(x + y)
    }

    getSum(1, 2) // 3 
    getSum(1) // 1
    getSum() // 0
  </script>

『案例』求学生总分

需求:学生的分数是一个数组,计算每个学生的总分,分析:

①: 封装一个求和函数
②: 传递过去的参数是一个数组
③: 函数内部遍历数组求和

  <script>
    // 如果函数封装的是数组,则形参可以是 arr = []
    function getArrSum(arr = []) {
      let sum = 0
      for (let i = 0; i <= arr.length; i++) {
        sum += arr[i]
      }
      console.log(sum)
    }
    getArrSum([1, 2, 3, 4, 5])      // 15
    getArrSum([11, 22, 33, 44, 54]) // 164
    getArrSum()                     // 0
  </script>

实参可以是数组:

  <script>
    // 如果函数封装的是数组,则形参可以是 arr = []
    function getArrSum(arr = []) {
      let sum = 0
      for (let i = 0; i <= arr.length; i++) {
        sum += arr[i]
      }
      console.log(sum)
    }
    getArrSum([1, 2, 3, 4, 5])      // 15
    getArrSum([11, 22, 33, 44, 54]) // 164
    getArrSum()                     // 0
  </script>

实参可以是变量

  <script>
    function getArrSum(n = 0, m = 0) {
      let sum = 0
      for (let i = n; i <= m; i++) {
        sum += i
      }
      console.log(sum)
    }
    let num1 = +prompt('请输入起始值:')
    let num2 = +prompt('请输入结束值:')

    getArrSum(num1, num2)
  </script>

函数返回值

函数执行完特定任务之后,然后把结果给我们就叫函数返回值。当函数需要返回数据出去时,用return关键字。

在函数体中使用 return 关键字能将内部的执行结果交给函数外部使用
return后面代码不会再被执行,会立即结束当前函数,所以return后面的数据不要换行写
return函数可以没有 return,这种情况函数默认返回值为 undefined

  <script>
    // 函数的返回值设置为20
    function fn() {
      return 20
    }

    // 相当于 fn() = 20
    console.log(fn())

    // 类同 let re = prompt('请输入数字')
    let re = fn()
    document.write(re)
  </script>
  <script>
    // 函数的返回值
    function getTotalPrice(x, y) {
      return x + y
    }

    // 变量 = 函数实参
    let sum = getTotalPrice(1, 2)
    document.write(sum)
  </script>

案例:求数组中最大值和最小值

求任意2个数中的最大值,并返回

  <script>
    // 求最大值函数
    function getMax(x, y) {
      // 1大于2吗 ?对输出1 :不对输出2
      return x > y ? x : y
    }
    let max = getMax(1, 2)
    console.log(max);
  </script>

求任意数组中的最大值并返回这个最大值

 <script>
    // 求r任意数组的最大值并返回
    function getArrValue(arr = []) {
      // 先准备max变量放数组的第一个值
      let max = arr[0]
      // 遍历数组比较
      for (let i = 1; i < arr.length; i++) {
        if (max < arr[i])
          max = arr[i]
      }
      return max
    }
    let max = getArrValue([1, 3, 5, 7, 9])
    console.log(max)  // 9
  </script>

返回多个数据

求任意数组中既要最大值还要最小值的使用使用 return[max,min]

  <script>
    // 求r任意数组的最大值和最小值并返回
    function getArrValue(arr = []) {
      let max = arr[0] // 给个变量装数组第一个值
      let min = arr[0] // 再给个变量装数组第一个值

      // 遍历数组比较
      for (let i = 1; i < arr.length; i++) {
        // 最大值比较
        if (max < arr[i]) {
          max = arr[i]
        }
        // 最小值比较
        if (min > arr[i]) {
          min = arr[i]
        }
      }
      return [max, min]
    }
    let newArr = getArrValue([1, 3, 5, 7, 9])
    console.log(`数组的最大值:${newArr[0]}`) // 9
    console.log(`数组的最小值:${newArr[1]}`) // 1
  </script>

函数断点调试

断点调试:进入函数内部看执行过程 F11

JavaScript函数与作用域

函数补充

两个相同的函数后面的会覆盖前面的函数。

实参的个数和形参的个数可以不一致时,如果形参过多 会自动填上undefined (未知),如果实参过多,那么多余的实参会被忽略(函数内部有一个arguments,里面装着所有的实参)

函数一旦碰到return就不会在往下执行了 函数的结束用return

  <script>
    function fn(a, b) {
      console.log(a + b) // 1 + 2
    }
    // 如果实参多于形参,则多于就不参与运算
    fn(1,2,3)  // 3

    // 如果形参多于实参,则多出的形参为未知,数字和undefined参与运算,结果是NaN
    fn(1)          // NaN
  </script>

函数中断程序

// 一个封装函数
function asda() {
  if (true) {
    console.log(11)
    return false  // 中断程序,函数里下面的就都不再执行了
  }
  console.log(12) // 不执行
  console.log(13) // 不执行
}
asda() // 调用函数

作用域

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

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

全局作用域

作用于所有代码执行的环境(整个<script>内部)或者一个独立的 js文件

局部作用域

作用于函数内的代码环境,就是局部作用域。因为跟函数有关系,所以也称为函数作用域。

全局变量

函数外部let 变量,全局变量在任何区域都可以访问和修改。

局部变量

函数内部let = 变量,局部变量只能在当前函数内部访问和修改。

  <script>
    // 全局变量
    let num = 10
    console.log(num) // 10

    function fn() {
      console.log(num) //10 全局变量是通用的
    }
    fn()

    // 局部变量
    function fun() {
      let str = '霍欣标' // 在函数内部声明了一个变量
    }
    fun() // 结果错误,变量未声明
  </script>

函数变量的特殊情况

  <script>
    function fn() {
      num = 10 // 局部变量并没有声明,外部也能使用这个变量
    }

    fn()
    console.log(num) //结果是:10
  </script>
  <script>
    function fn(x, y) {
      // 形参可以看做是局部变量,可以在内部打印
      console.log(x) // 1
    }
    fn(1,2)
    console.log(x) // 外部打印不了,结果是错误的
  </script>

变量的访问原则

只要是代码,就至少有一个作用域

写在函数内部的局部作用域

如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域

访问原则:在能够访问到的情况下先局部,局部没有在找全局

  <script>
    let num = 10
    // 这是个函数
    function fu() {
      let num = 20
      let arr = 100
      console.log(num)     // 结果是:20
      // 里面再写一个函数
      function fun() {
        let num = 30
        console.log(num)   //结果是: 30
        console.log(arr)   //结果是:100
      }
      fun()
    }
    fu() 
  </script>

匿名函数

没有名字的函数,无法直接使用。使用方式有“函数表达式”和“立即执行函数”两种。

函数表达式

将匿名函数赋值给一个变量,并且通过变量名称进行调用 我们将这个称为函数表达式。

  <script>
    //函数表达式和
    // 具名表达式可以写在任何位置,而函数表达式只能写在函数的后面
    let fn = function () {
      console.log('我是函数表达式')
    }
    fn()

    // 具名函数
    fun()
    function fun() {
      console.log('我是具名表达式')
    }
  </script>

立即执行函数

场景为了避免全局变量之间的污染。

第一种写法:先写()();,第一个括号写function(){} ,执行代码写在{} 中,第二个() 可以写实参。

  <script>
    (function () {
      console.log(13)
    })();
    (function () {
      console.log(11)
    })();

    // (本身还是函数(行参){}) (实参);
    (function (x, y) { console.log(x + y) })(1, 2);
  </script>

第二种写法:先写一个(); ,在这个括号里写function(){}()

  <script>
    (function (x, y) {
      console.log(x + y)
    }(1, 2));

    // (本身函数(形参)     {执行代码}          (实参));
    (function (x, y) { console.log(x + y) }(1, 2));
  </script>

案例:总秒数转换为时分秒

需求:用户输入秒数,可以自动转换为时分秒。

分析:

① 用户输入总秒数(注意默认值)
② 计算时分秒(封装函数) 里面包含数字补0
③ 打印输出

计算公式:

小时: h = parselnt(总秒数 / 60 / 60 % 24)
分钟: m = parselnt(总秒数 / 60 % 60)
秒数: s = parselnt(总秒数 % 60)

  <script>
    // 1. 用户输入秒数
    let second = +prompt('请输入秒数:')
    // 封装函数
    function getTime(t) {
      console.log(t) // 总秒数
      // 2. 转换时间
      let h = parseInt(t / 60 / 60 % 24)
      let m = parseInt(t / 60 % 60)
      let s = parseInt(t % 60)
      console.log(h, m, s)
      // 3.数字补零
      h = h < 10 ? '0' + h : h
      m = m < 10 ? '0' + m : m
      s = s < 10 ? '0' + s : s
      return `转换完毕之后的时间是:${h}时 ${m}分 ${s}秒`
    }
    let str = getTime(second)
    document.write(str)
  </script>

逻辑中断

短路:只存在于&&||中,当满足一定条件会让右边代码不执行。

原因:通过左边能得到整个式子的结果,因此没必要再判断右边。

运算结果:无论&&还是||,运算结果都是最后被执行的表达式值,一般用在变量赋值

符号短路条件
&&左边为false就短路
||左边为true就短路
  <script>
    // 实参没有数据,也可以使用逻辑中断给形参赋值
    function fn(x, y) {
      x = x || 0 // 假的 || 0, x = 0
      y = y || 2 // 假的 || 0, y = 0
      console.log(x + y)
    }
    fn()  //结果是 0
  </script>

&& 先看左边,如果是假的,右边就不再执行了。

    // 逻辑运算符一假则假,先看&&左边的,如果错误的,右边就不再执行了
    console.log(false && 22) // false
    console.log(false && 3 + 5) // false

    // 了解拓展
    console.log(true && 3 + 5) // 结果是8
    console.log(4 && 3 + 5)   // 结果是8
    console.log('x' && 3 + 5)   // 结果是8

|| 先看左边,如果是真的,右边就不再执行了。

    // 逻辑运算符一真则真,先看||左边的,如果真的,右边就不再执行了
    let age = 18
    console.log(true || age++); // true
    console.log(11 || age++); // 11
    console.log(age)         // 18
    console.log(true || 3 + 5) // true

    // 了解拓展
    console.log(false || 3 + 5) // 8
    console.log(12 || 3 + 5) // 12
    console.log('x' || 3 + 5) // x

如果两边都为真,&& 是后面的真值;|| 是前面的真值。

  <script>
    console.log(11 && 22) // 22
    console.log(11 || 22) // 11
  </script>

转换为Boolean型

'' 0 undefined null false NaN转换为布尔值后都是false,其余则为true

  <script>
    console.log(Boolean('xinbiao')) // true
    console.log(Boolean(''))        // false
    console.log(Boolean(0))         // false
    console.log(Boolean(90))        // true
    console.log(Boolean(-1))        // true      
    console.log(Boolean(undefined)) // false
    console.log(Boolean(null))      // false
    console.log(Boolean(NaN))       // false
    console.log(Boolean(false))     // false
  </script>
JavaScript函数与作用域
  <script>
    console.log('2' - 2)              // 0
    console.log(null + 3)             // 3
    console.log(undefined + 3)        // NaN
    console.log(null == undefined)    // true
    console.log(null === undefined)   //false
    console.log('' - 1)               //-1
    console.log('xinbiao' - 1)        //NaN
    console.log(undefined + 1)        // NaN
    console.log(NaN +1)               // NaN
  </script>

原创文章,作者:霍欣标,如若转载,请注明出处:https://www.bigengwu.cn/xue/81.html

霍欣标的头像霍欣标
上一篇 2024-07-12 16:10
下一篇 2024-07-19 01:24

相关推荐

博主人懒,应管局要求暂不开启站内私信和评论功能,如需帮助请发邮件。

邮箱账号:1969600480@qq.com