JavaScript --函数 (实例结合)

JavaScript --函数 (实例结合)


函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块

我们用实例来初步了解一下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试实例</title>
<script>
function myFunction()
{
     
     
    alert("Hello World!");
}
</script>
</head> 
<body>
<button onclick="myFunction()">点我</button>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSFeYQw9-1602827586521)(函数.assets/image-20201015085239536.png)]

在这里插入图片描述

1.初识函数

函数: 用于封装一段完成特定功能的代码。

  • 相当于将一条或多条语句组成的代码块包裹起来,用户在使用时只需关心参数和返回值,就能完成特定的功能,而不用了解具体的实现。

    下面通过一段代码演示函数的使用:

<script>
  console.log(parseFloat('7.26e-2'));     // 返回解析后的浮点数:0.0726
  console.log(isNaN(' '));                // 判断是否是NaN:false
  console.log(parseInt('15.99'));		//返回解析后整数值是15
</script>

parseFloat() 用于返回解析字符串后的浮点数。
isNaN() 判断给定参数是否为NaN,判断结果为是,返回true,否则返回false。
parseInt() 用于返回解析字符串后的整数值。

  • 除了使用内置函数外,JavaScript中还可以根据具体情况自定义函数,提高代码的复用性,
    降低程序维护的难度。

    具体语法结构如下:

function函数名([参数1,参数2,…])
{
	函数体.....
}

function: 定义函数的关键字。
函数名: 由大小写字母、数字、下划线(_)和$符号组成,但是函数名不能以数字开头,且不能是JavaScript中的关键字。
参数: 是外界传递给函数的值,它是可选的,多个参数之间使用“,”分割。
函数体: 是专门用于实现特定功能的主体,由一条或多条语句组成。
返回值: 在调用函数后若想得到处理结果,在函数体中可用return关键字返回。

​ 具体来看一下自定义函数的使用 :
在代码中,定义一个id为btn的按钮和-个默认情况下隐藏的< div > 元素。其中,< div >元素的id值为demo。

<button id="btn">单击</button>
<div id="demo" style="display: none">这是一个惊喜!</div> //

<script>  // 为按钮添加事件,将隐藏的<div>元素设为可见。
  function $(id) {
     
         // 根据id获取元素对象
    return document.getElementById(id);
  }
  function show() {
     
        // 显示id为demo的元素
    $('demo').style.display = 'block';
  }
  $('btn').onclick = show;
</script>

在上述代码中,第5-7行封装的$()函数用于根据id获取元素对象,第8-10行封装的show()函数用于将id为demo的元素设置为可见。第11行代码用于为按钮添加单击事件,单击网页中触发时调show()函数处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n38FH3dz-1602827586555)(函数.assets/image-20201013100027626.png)]

2.参数设置

  • 无参函数: 适用于不需要提供任何数据,即可完成指定功能的情况。
<script>
  function greet() {
     
     
    console.log('Hello everybody!');
  }
</script>

在自定义函数时,即使函数的功能实现不需要设置参数,小括号“()”也不能够省略。

  • 有参函数: 适用于开发时函数体内的操作需要用户传递数据的情况。

    形参: 指的就是形式参数,具有特定的含义,在定义有参函数时设置的参数。
    实参: 指的是实际参数,也就是具体的值,在函数调用时传递的参数。

    通过实例1来观察:

<script>
  function maxNum(a, b) {
     
     
    a = parseInt(a);
    b = parseInt(b);
    return a >= b ? a : b;
  }
</script>

上述定义的maxNum()函数用于比较形参a和b的大小,首先在该函数体中对参数a和b
进行处理,确保参与比较运算的数据都是数值型,接着利用return关键字返回比较的结果。

​ 通过实例2来观察:

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>W3Cschool教程(w3cschool.cn)</title> 
</head>
<body>

<p>请点击其中的一个按钮,来调用带参数的函数。</p>
<button onclick="myFunction('Tom','Wizard')">点击这里</button>
<button onclick="myFunction('Bob','Builder')">点击这里</button>
<script>
function myFunction(name,job)
{
     
     
	alert("Welcome " + name + ", the " + job);
}
</script>

</body>
</html>

  • 获取函数调用时传递的所有实参

    适用于开发时函数体形参不确定的情况。
    实现方式: 利用arguments对象,在函数体内可获取函数调用时传递的实参。
    其他操作: length属性可获取实参的总数,具体实参值可利用数组遍历方式。

<script>
  function transferParam() {
     
     
    console.log(arguments.length);    // 获取用户实际传递的参数数量
    console.log(arguments);           // 在控制台输出用户调用函数时传递的参数
  }
</script>
  • ps: 含有默认值的参数与剩余参数
<script>
  function greet(name, say = 'Hi, I\'m ') {
     
     
    console.log(say + name);
  }
</script>

函数的形参在设置时,还可以为其指定默认值。当调用者未传递该参数时,函数将使用默认值进行操作。

<script>
  function transferParam(num1, ...theNums) {
     
     
    theNums.unshift(num1);      // 在剩余参数数组的头部添加第一个参数
    console.log(theNums);       // 在控制台输出用户调用函数时传递的参数
  }
</script>

<script>
  function transferParam(...theNums) {
     
     
    console.log(theNums);       // 在控制台输出用户调用函数时传递的参数
  }
</script>

函数定义时,还可用“…变量名”的方式动态接收用户传递的不确定数量的实参。

3.函数的调用

  • 当函数定义完成后,要想在程序中发挥函数的作用,必须得调用这个函数。
  • 函数的调用非常简单,只需引用函数名,并传入相应的参数即可。
  • 函数声明与调用的编写顺序不分前后。
  • [参数1,参数2…]是可选的,用于表示形参列表,其值可以是零个、一个或多个。
<script>
  function getSum() {
     
                      // 定义函数
    var sum = 0;                      // 保存参数和
    for (i in arguments) {
     
                 // 遍历参数,并累加
      sum += arguments[i];
    }
    return sum;                       // 返回函数处理结果
  }
  console.log(getSum(10, 20, 30));    // 函数调用
</script>

【案例】字符串大小写转换

代码实现思路:

  • 编写HTML表单,设置两个文本框和两个按钮,文本框显示转换前后数据,按钮用于转换。
  • 为按钮添加点击事件,并利用函数deal()处理。
  • 编写deal()函数,根据传递的不同参数执行不同的转换操作。将转换后的数据显示到对应位置。
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>字符串大小写转换</title>
  </head>
  <body>
    <h2>大小写转换</h2>
    <p>原数据:<input id="old" type="text"></p>
    <p>
      操  作:
      <input type="button" value="转大写" onclick="deal('upper')">
      <input type="button" value="转小写" onclick="deal('lower')">
    </p>
    <p>新数据:<input id="new" type="text"></p>
    <script>
      function deal(opt) {
     
     
        var str = document.getElementById('old').value;
        switch (opt) {
     
     
          case 'upper':
            str = str.toUpperCase();
          break;
          case 'lower':
            str = str.toLowerCase();
          break;
        }
        document.getElementById('new').value = str;
      } 
    </script>
  </body>
</html>

4.变量的作用域

思考: 声明变量后就可以在任意位置使用该变量吗?
回答: 不是。
举例: 函数内var关键字声明的变量,不能在函数外访问。

<script>
  function info() {
     
     
    var age = 18;
  }
  console.log(age);    // 输出结果:Uncaught ReferenceError: age is not defined
</script>

总结: 变量的使用是有作用域范围的。
作用域划分: 全局作用域、函数作用域和块级作用域(ES6提供的)。
不同作用域对应的变量: 全局变量、局部变量、块级变量(ES6提供的)。

<script>
  var a = 'one';                   // 全局变量
  function test() {
     
     
    var a = 'two';                 // 局部变量
    console.log(a);
  }
  test();
  for (let a = 0; a < 3; ++a) {
     
         // 块级变量(ES6新增)
    console.log(a);
  }
  console.log(a);
</script>

在浏览器中访问demo3.html,运行结果如图4-4所示。从图中可以看出,调用函数test()
的输出结果为“two”,这是因为当局部变量与全局变量重名时,局部变量的优先级高于全局变量。
第8~10行代码利用let关键字定义了–个块级变量a,只在当前for 循环内有效,因此可在控
制台看到输出的结果为0、1和2。而第11行代码输出的变量a,既无权访问块级作用域中的值,又无权访问函数作用域内的值,从而只能输出第2行声明的全局作用域变量,结果为“one”。

  • 全局变量:不在任何函数内声明的变量(显示定义)或在函数内省略var声明变量(隐式定义)都称为全局变量。
    作用范围:它在同一个页面文件中的所有脚本内都可以使用。

  • 局部变量:在函数体内利用var关键字定义的变量称为局部变量,它仅在该函数体内有效。

  • 块级变量:ES6提供的let关键字声明的变量称为块级变量,仅在“蔡”中间有效,
    如if、for或while语句等。

ps:垃圾回收机制

在JavaScript中,局部变量只有在函数的执行过程中存在,而在这个过程中会为局部变量在(栈或堆)内存上分配相应的空间,以存储它们的值,然后在函数中使用这些变量,直到函数结束。而一旦函数执行结束,局部变量就没有存在必要了,此时JavaScript就会通过垃圾回收机制自动释放它们所占用的内存空间。

在开发中若要保留局部变量的值,可以通过以下两种方式实现:

<script>
  // 第1种方式:利用return返回
  function test(num) {
     
     
    num = num + 1;
    return num;
  }
  var num = test(24);
  console.log(num);       // 输出结果:25
</script>

<script>
  // 第2种方式:利用全局变量保存
  var memory;
  function test(num) {
     
     
    memory = num + 1;
  }
  test(24);
  console.log(memory);    // 输出结果:25
</script>

5.匿名函数

1.函数表达式
概念:所谓函数表达式指的是将声明的函数赋值给一个变量,通过变量完成函数的调用和参数的传递,它也是JavaScript中另一种实现自定义函数的方式。

2.匿名函数
概念:匿名函数指的是没有函数名称的函数。
作用:可以有效的避免全局变量的污染以及函数名的冲突问题。
说明:既是函数表达式的另一种表示形式,又可通过函数声明的方式实现调用。

PS:箭头函数

概念:ES6中引入了一种新的语法编写匿名函数,我们称之为箭头函数。
特点:一个箭头函数表达式的语法比一个函数表达式更短。

<script>
  // 用法1:标准语法
  (p1, p2, /* …, */ pN) => {
     
      statements }
  // 用法2:返回值
  (p1, p2, /* …, */ pN) => {
     
      return expression; }    // 或  (p1, p2, …, pN) => expression
  // 用法3:含有一个参数
  (singleParam) => {
     
      statements; }    // 或  singleParam => { statements; }
  // 用法4:无参箭头函数
  () => {
     
      statements; }    // 或   _ => { statements; }
</script>

<script>
  // 设置1个参数
  var fn1 = x => x + 2;
  console.log(fn1(4));           // 输出结果:6
  // 设置2个参数
  var fn2 = (x, y) => x + y;
  console.log(fn2(1, 2) );       // 输出结果:3
</script>

箭头函数中箭头“=>”不是操作符或者运算符,但是箭头函数相比普通的函数受操作符的优先级影响。

3.回调函数
概念: 所谓回调函数指的就是一个函数A作为参数传递给一个函数B,然后在B的函数体内调用函数A。此时,我们称函数A为回调函数。
提示: 匿名函数常用作函数的参数传递,实现回调函数。
应用: 函数体中某部分功能由调用者决定,此时可以使用回调函数。

<script>
  function cal(num1, num2, fn) {
     
     
    return fn(num1, num2);
  }
  console.log(cal(45, 55, function (a, b) {
     
     
    return a + b;
  }));
  console.log(cal(10, 20, function (a, b) {
     
     
    return a * b;
  }));
</script>

上述第2-4行代码定义的cal()函数,用于返回fn回调函数的调用结果。第5-7行代码用
于调用cal()函数,并指定该回调函数用于返回其两个参数相加的结果,因此可在控制台查看到结果为100,如图4-5所示。同理,第8~10行代码在调用cal()函数时,将回调函数指定为返回其两个参数相乘的结果,因此可在控制台查看到结果为200,

以map()方法为例进行演示,对arr数组中的每个元素都按顺序调用一次回调函数。

<script>
  var arr = ['a', 'b', 'c'];
  arr.map(function(value, index) {
     
     
    console.log(value, index);
  });
</script>

参数:
map()的参数是一个回调函数fn。
fn的第1个参数表示当前数组的元素。
fn的第2个参数表示对应元素的索引下标。
返回值: 回调函数每次执行后的返回值会组合起来形成一个新数组。
示例结果:在控制台依次可查看到,'a 0”、“b 1”和“c 2”。

思考:如何利用map()方法实现二维数组的转置?

<script>
  var arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];    // 待转置的数组
  var reverse = arr[1].map(function (col, i) {
     
         // 利用i获取转置后数组元素的下标
    return arr.map(function (row) {
     
                    // 返回转置后新组合而成的数组元素
      return row[i];                              // 返回转置前数组元素的指定索引的元素
    });
  });
</script>

在上述代码中,arr[1]调用map()时,i表示—维数组[4,.5.6]中元素对应的下标0、1和2,arr
调用map()方法时,row依次表示[1,2.3]、[4,5,6]和[7,8,9]。因此当i等于0时,arr调用map()
方法的返回值是[1,4.7],依次类推,当arr[1]中所有元素都执行一次回调函数后,即可得到转置
后的二维数组为[[1,4,7].[2,5,8],[3,6,9]。
另外,以上示例中的arr[1]还可以使用arr[0]或arr[2]代替,只要能获取到转置后数组元素的
下标即可。

6.嵌套与递归

1.函数嵌套与作用域链
什么是嵌套函数: 是在一个函数内部存在另一个函数的声明。

特点: 内层函数只能在外层函数作用域内执行,在内层函数执行的过程中,若需要引入某个变量,首先会在当前作用域中寻找,若未找到,则继续向上一层级的作用域中寻找,直到全局作用域,我们称这种链式的查询关系为作用域链。

<script>
  var i = 26;
  function fn1() {
     
             // 声明的第1个函数
    var i = 24;
    function fn2() {
     
           // 声明的第2个函数
      function fn3() {
     
         // 声明的第3个函数
        console.log(i);
      }
      fn3();
    }
    fn2();
  }
  fn1();
</script>

在上述代码中,函数fn1()内嵌套了函数fn2(),fn2()函数内嵌套了函数fn3(),并在 fn3()函
数中输出变量i。但是fn3()和fn2()函数中都没有变量i的声明,因此程序会继续向上层寻找,在fn1()函数中找到了变量i的声明,最后在控制台的输出结果为24。效果如图4-6所示。

2.递归调用
概念:递归调用是函数嵌套调用中一种特殊的调用。它指的是一个函数在其函数体内调用自身的过程,这种函数称为递归函数。

递归调用虽然在遍历维数不固定的多维数组时非常合适,但它占用的内存和资源比较多,同时难以实现和维护,因此在开发中要慎重使用函数的递归调用。

<script>
  function factorial(n) {
     
             // 定义回调函数
    if (n == 1) {
     
     
      return 1;                  // 递归出口
    }
    return n * factorial(n - 1);
  }
  var n = prompt('求n的阶乘\n n是大于等于1的正整数,如2表示求2!。');
  n = parseInt(n);
  if (isNaN(n)) {
     
     
    console.log('输入的n值不合法');
  } else {
     
     
    console.log(n + '的阶乘为:' + factorial(n));
  }
</script>

上述代码中定义了一个递归函数factorial(),用于实现n的阶乘计算。当n不等于1时,递
归调用当前变量n乘以factorial(n - 1),直到n等于1时,返回1。其中,第8行用于接收用户
传递的值,第9~14行用于对用户传递的数据进行处理,当符合要求时调用factorial()函数,否则在控制台给出提示信息。

【案例】求斐波那契数列第N项的值

斐波那契数列又称黄金分割数列,如“1,1,2,3,5,8,13,21…”
找规律:这个数列从第3项开始,每一项都等于前两项之和。

代码实现思路:

小于0,给出错误提示信息。
等于0,返回0。
等于1,返回1。
大于1,按找到的规律并利用函数递归调用实现。

<script>
  function recursion(n) {
     
     
    if (n < 0) {
     
     
      return '输入的数字不能小于0';
    } else if (n == 0) {
     
     
      return 0;
    } else if (n == 1) {
     
     
      return 1;
    } else if (n > 1) {
     
     
      return recursion(n - 1) + recursion(n - 2);
    }
  }
  console.log(recursion(10));
</script>

在上述代码中,函数recursion()的参数n指的是求斐波那契数列中第几项的值。其中,当n
小于0时直接返回错误提示信息,当n等于0时,返回0;当n等于1时,返回1;当n大于1时,返回其前两项的和。通过浏览器测试程序,控制台中输出斐波那契数列中第10项的值,如图4-8所示。

7.闭包函数

1.什么是闭包函数
在JavaScript中,内嵌函数可以访问定义在外层函数中的所有变量和函数,并包括其外层函数能访问的所有变量和函数。但是在函数外部则不能访问函数的内部变量和嵌套函数。此时就可以使用“闭包”来实现。

所谓“闭包”指的就是有权访问另一函数作用域内变量(局部变量)的函数。
它最主要的用途是以下两点:

  • 可以在函数外部读取函数内部的变量。
  • 可以让变量的值始终保持在内存中。

由于闭包会使得函数中的变量一直被保存在内存中,内存消耗很大,所以闭包
的滥用可能会降低程序的处理速度,造成内存消耗等问题。

2.闭包函数的实现

<script>
  function fn() {
     
     
    var times = 0;
    var c = function () {
     
     
      return ++times;
    };
    return c;
  }
  var count = fn();    // 保存fn()返回的函数,此时count就是一个闭包
  // 访问测试
  console.log(count());
  console.log(count());
  console.log(count());
  console.log(count());
  console.log(count());
</script>

上述第4~6行代码,利用闭包函数实现了在全局作用域中访问局部变量times,并让变量
的值始终存储在内存中。在第9行代码调用fn()函数后,将匿名函数的引用返回给count变量,
且匿名函数中使用了局部变量times。因此,局部变量times不会在fn()函数执行完成后被
JavaScript回收,依然保存在内存中。所以,当通过第11~~15行代码进行测试时,就可以在控
制台看到图4-9所示的输出结果。

【动手实践】

网页计算器

  • 代码实现思路:
    ①编写HTML页面,两个文本框用于输入数据,4个按钮用于完成加减乘除运算, 最后一个文本框显示计算结果。
    ②编写calc()函数,获取并转换输入的数据,判断,不合法给出提示,调用指定函数完成相关计算。
    ③编写函数,实现加、减、乘、除运算。

1.编写html

 <p>整数1:<input id="num1" type="text"></p>
    <p>整数2:<input id="num2" type="text"></p>
    <p>
      <input type="button" value="相加" onclick="calc(add)">
      <input type="button" value="相减" onclick="calc(sub)">
      <input type="button" value="相乘" onclick="calc(mul)">
      <input type="button" value="相除" onclick="calc(div)">
    </p>
    <p>结果:<input id="result" type="text" readonly></p>

上述第1-2行代码设置的文本框用于输入需要运算的操作数,第3-8行用于指定计算方
式。例如,单击“相加"按钮,则触发calc()函数,传递add参数,实现加法运算,并将结果显示到第9行设置的只读文本框中。

2.编写calc()函数,用于获取并处理用户输入的数据,完成指定操作的函数调用。

<script>
      function calc(func) {
     
     
        var result = document.getElementById('result');
        var num1 = parseInt(document.getElementById('num1').value);
        var num2 = parseInt(document.getElementById('num2').value);
        if (isNaN(num1) || isNaN(num2)) {
     
     
          alert('请输入数字');
          return false;
        }
        result.value = func(num1, num2);
      }
</script>
     

上述第3行代码,用于返回id为result的元素对象。第4~9行代码获取并转换用户输入
的数据,保证参与运算的都是整数,当转换后的数据为NaN时给出提示信息,并停止脚本继续
运行。第10行代码用于调用指定的函数(如add、sub等),并将结果显示到指定的区域中。

3.继续编写函数,实现加、减、乘、除运算。

 <script>
function add(num1, num2) {
     
         // 加法
        return num1 + num2;
      }
      function sub(num1, num2) {
     
         // 减法
        return num1 - num2;
      }
      function mul(num1, num2) {
     
         // 乘法
        return num1 * num2;
      }
      function div(num1, num2) {
     
         // 除法
        if (num2 === 0) {
     
     
          alert('除数不能为0');
          return '';
        }
        return num1 / num2;
      }
    </script>

在上述代码中,在进行除法运算前,首先要判断除数是否为0,若不为0则进行运算,若为0则在弹出的警告框中给出提示信息,然后返回空字符串。

总的就是:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>网页计算器</title>
  </head>
  <body>
    <p>整数1:<input id="num1" type="text"></p>
    <p>整数2:<input id="num2" type="text"></p>
    <p>
      <input type="button" value="相加" onclick="calc(add)">
      <input type="button" value="相减" onclick="calc(sub)">
      <input type="button" value="相乘" onclick="calc(mul)">
      <input type="button" value="相除" onclick="calc(div)">
    </p>
    <p>结果:<input id="result" type="text" readonly></p>
    <script>
      function calc(func) {
     
     
        var result = document.getElementById('result');
        var num1 = parseInt(document.getElementById('num1').value);
        var num2 = parseInt(document.getElementById('num2').value);
        if (isNaN(num1) || isNaN(num2)) {
     
     
          alert('请输入数字');
          return false;
        }
        result.value = func(num1, num2);
      }
      function add(num1, num2) {
     
         // 加法
        return num1 + num2;
      }
      function sub(num1, num2) {
     
         // 减法
        return num1 - num2;
      }
      function mul(num1, num2) {
     
         // 乘法
        return num1 * num2;
      }
      function div(num1, num2) {
     
         // 除法
        if (num2 === 0) {
     
     
          alert('除数不能为0');
          return '';
        }
        return num1 / num2;
      }
    </script>
  </body>
</html>

猜你喜欢

转载自blog.csdn.net/m0_49095721/article/details/109115020