[TOC] # 函数 <br> ## 概念: &emsp;&emsp;把一段相对独立的具有特定功能的代码块封装到 {} 中,形成一个独立的代码块,再给这个代码块起个名字,名字和代码块的组合就是函数,一个函数可以在后续开发中重复使用。 &emsp;&emsp;总的来说,函数就是使用{}封装起来的一段代码,可供将来重复使用。 <br> ## **函数的定义** ### **语法格式** >[success]//命名函数 function 函数名(){ // 函数体 } function:关键字,函数、方法的意思。 函数名:遵循变量的命名规则和规范 ():固定格式 {}:封装具有特定功能的代码 函数体:要实现的功能代码 实例: 定义一个函数,实现在浏览器控制台输出 '你好!' . ``` // 声明函数 function sayHello() { console.log("你好!"); } ``` <br> ### **函数表达式** >[success]//匿名函数 var fn = function() { // 函数体 } 将一个匿名函数赋值给变量fn,此时可以使用fn调用该匿名函数。 实例: 定义一个匿名函数,实现在浏览器控制台输出 '你好!' . ``` var sayHello = function () { console.log("你好!"); } ``` ### **特点** &emsp;&emsp;函数声明的时候,函数体并不会执行,只有当函数被调用的时候才会执行。函数一般都用来干一件事情,故函数名需用使用动词+名词,表示做一件事情, 比如 sayHello. <br> ## **函数的调用** ### **语法格式** >[success]函数名(); 实例: ``` // 调用函数 sayHello(); ``` ### **特点** >[info] 1. 函数体只有在调用的时候才会执行,调用需要()进行调用。 > 2. 可以调用多次(重复使用)。 > 3. 函数可以在任何地方调用。 ## 函数的执行过程流程 &emsp;&emsp;函数调用完毕之后,程序会回到调用处,执行完调用处的代码,再往下继续执行! 函数的执行过程如下图: ![](images/函数的执行过程.png) 实例: &emsp;求1-100之间所有数的和 &emsp;将求和的功能代码封装到函数中。 ``` // 定义一个函数 function getSum() { var sum = 0; for (var i = 1; i <=100 ; i++) { sum += i; } console.log(sum); } //调用函数 //格式:函数名(); getSum(); ``` <br> ## **函数的参数** ### **为什么要有参数** **需求** 求1-100之间所有整数的和 ``` function getSum() {    var sum = 0;    for (var i = 1; i <=100 ; i++) {        sum += i;   }    console.log(sum); } ``` &emsp;&emsp;思考:虽然上面代码可以重复调用,但是只能计算函数体中定义好的那个范围的和,如果想要计算200至300或者其他范围的和,应该怎么办呢? <br> ### **语法格式** &emsp;&emsp;函数内部是一个封闭的环境,可以通过传递参数的方式,把外部的值传递给函数内部。 >[success]// 带参数的函数声明 >function 函数名(形参1, 形参2, 形参...){ > // 函数体 >} > >[success]// 带参数的函数调用 >函数名(实参1, 实参2, 实参3); >参1 = 实参1; >形参2 = 实参2; >... <br> ### **形参和实参** >[info]// 1、形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固>定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,>仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。 > >2、实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。 ### 需求 1. 求两个数之和(练习) >[info]思路:x,y 是实参,有具体的值。函数执行的时候会把x,y复制一份给函数内部的a和b,函数内部的值是复制的新值,无法修改外部的x,y ~~~ //求两个数的和 function getSum(a, b) {//括号中的a,b是形式参数    var sum = a + b;    console.log(sum); } //调用带有参数的函数 getSum(1, 2);//传入实际参数,相当于 a=1;b=2 //实参的另一种写法 var x = 3; var y = 4; getSum(x, y); ~~~ >[info]思考:如果在getSum函数内部修改a,b的值,会不会影响x,y的值? 2. 求1-n之间所有数的和(练习) >[info]思路:跟我们之前学习的求一个数的和类似,此时需要将这个数作为函数的参数传给函数,然后在函数中做求和操作。 ~~~ function getSum(n) {    //累加和    var sum = 0;    for (var i = 1; i <= n; i++) {        sum += i;   }    console.log(sum); } getSum(5); ~~~ 3. 求n-m之间所有数的和(练习) 从n开始遍历,到m结束 ~~~ function getSum(n, m) {    //累加和    var sum = 0;    for (var i = n; i <= m; i++) {        sum += i;   }    console.log(sum); } ​ getSum(1, 100); ~~~ 4. 求圆的面积 >[info]圆的面积公式:A = πR\*R,我们只需要将圆的半径r传给函数即可 ~~~ // 圆的面积公式:A = πR*R function getArea(r) {    var area = 3.14*r*r;    console.log(area); } getArea(2); ~~~ 5. 定义一个函数,用于求2个数中的最大值 >[info]思路:给函数定义两个形参,用于接收外部的两个数 ~~~ function getMax(n, m) {    if (n > m) {        console.log('最大值:' + n);   } else {        console.log('最大值:' + m);   } } getMax(11, 9); ~~~ 6. 求3个数中的最大值 此时给函数定义三个形参就可以了 ~~~ function getMax(a, b, c) {    if (a > b && a > c) {        console.log('最大值:' + a);   } else if (b > a && b > c) {        console.log('最大值:' + b);   } else if (c > b && c > a) {        console.log('最大值:' + c);   } } getMax(4, 6, 3); ~~~ 7. 定义一个函数,求n个数的最大值 ~~~ function getMax(array) {    //假设最大值是max    var max = array[0];    for (var i = 0; i < array.length; i++) {        //判断最大值是不是max        if (max < array[i]) {            //存储两个数中的最大值            max = array[i];       }   }    console.log(max); } ​ var arr = [2, 3, 433, 5, 7, 8, 121, 2, 23, 67, 4, 12, 14]; getMax(arr); ​ ~~~ 8. 定义一个函数,判断一个数是否是素数(质数) >[info]素数(质数):只能被1和自己整除的数 ~~~ // 素数(质数):只能被1和自己整除的数 function isPrimeNum(num) {    var isPrime = true;    //遍历2-n之间的数    for(var i = 2; i< num; i++){        //判断num是否是质数        if (num % i === 0) {            //不是质数            isPrime = false;            break;       }   }    if (isPrime) {        console.log(num+'是素数');   }else {        console.log(num+'不是素数');   } } //调用函数 isPrimeNum(101); ~~~ <br> ## **函数的返回值** &ensp;&emsp;当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个返回值 ### **语法格式** >[success]//定义一个带返回值的函数 function 函数名(形参1, 形参2, 形参...){ &nbsp; &nbsp;&nbsp; &nbsp;//函数体 &nbsp;&nbsp;&nbsp; &nbsp; return 值; } 函数的返回值会返回到调用处,可以通过变量来接收这个返回值 >[success]var 变量 = 函数名(实参1, 实参2, 实参3); &nbsp; &nbsp;函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。 &nbsp; &nbsp;返回值详解: 1. 如果函数中没有写 return语句 ,那么函数有默认的返回值:undefined 2. 如果函数中写了return语句,但是return后面没有任何值,那么函数的返回值也是:undefined 3. 如果函数中写了return语句,并且return后面跟着一个值,那么该值就是函数的返回值在函数的执行过程中,如果遇到return语句,那么执行完 return 语句之后,函数就返回了,即函数执行结束。 >[info]小结 &emsp;&emsp;如果函数没有return语句或者return语句后面没有返回值,则函数返回undefined值,如果return后面有值,则返回该值,return除了能返回指定的值,还能结束函数。 推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。 #### 需求 1. 定义一个函数,求一组数中的最大值,将最大值返回到调用处 跟以前求最大值方式是一样的,只不过多了函数的入参(传入参数)和回参(返回值)。 ~~~ function getMax(arr) {    //函数体:要执行的代码    var max = arr[0];    for (var i = 0; i < arr.length; i++) {        if (max < arr[i]) {            max = arr[i];       }   }    return max ; } var a = [3, 5, 1, 66, 8, 18]; var result = getMax(a); console.log(result); ~~~ 2. 定义一个函数,求一组数中的最小值,将最小值返回到调用处 ``` // 求一组数中的最小值 function getMin(arr) { //函数体:要执行的代码 var min = arr[0]; for (var i = 0; i < arr.length; i++) { if (min > arr[i]) { min = arr[i]; } } return min ; } var a = [3, 5, 1, 66, 0, -1, 8, 18]; var result = getMin(a); console.log(result); ``` 3. 定义一个函数,求整数n阶乘,,将阶乘的结果返回到调用处 4n的阶乘就是1至n的所有整数的累乘。如 5! 表示 5的阶乘,可写成 1*2*3*4*5,求得结果是 120 ``` function getFactorial(num){ //函数体 var factorial = 1; for (var i = 1; i <= num; i++) { factorial *= i; } //函数返回语句 return factorial; } var result = getFactorial(20); console.log(result); ``` 4. 定义一个函数,求1!+2!+3!+....+n!,,将结果返回到调用处 在for循环中累加n以内所有整数的阶乘(调用 getFactorial),求得结果。 ~~~ //(1)求阶乘 function getFactorial(num){ //函数体 var factorial = 1; for (var i = 1; i <= num; i++) { factorial *= i; } //函数返回语句 return factorial; } //(2)累加阶乘的结果 function getFactorialSum(num){    var sum = 0;    for (var i = 1; i <= num; i++) {        sum = sum + getFactorial(i);//将阶乘累加   }    return sum ; } var resutl = getFactorialSum(3); console.log(resutl); ~~~ <br> ### **arguments的使用** &nbsp; &nbsp;JavaScript中,**arguments是一个类似数组的对象,arguments对象作为函数的一个内置属性,所有函数都内置了一个arguments对象,arguments对象中存储了外部传递给函数的所有的实参。arguments是一个伪数组(具有数据的部分属性),因此其可以进行遍历。** &nbsp; &nbsp;通过arguments获取外部传给函数的参数 ~~~ function test1() { //通过arguments获取函数调用时传入的实参 console.log(arguments); console.log(123); } ​ test1(5, 6); ~~~ **需求** 1. 使用arguments属性,求任意个数的最大值 思路:通过在函数内部使用arguments,可以获取到外出传给函数的所有是实参 ~~~ function getMax() {    //获取arguments中的第一个元素    var max = arguments[0];    for (var i = 1; i < arguments.length; i++) {        if (max < arguments[i]) {            max = arguments[i];       }   }    return max; } ​ var max = getMax(3, 5, 2, 11, 8, 1, 0); console.log(max); ~~~ 2. 求任意个数的和 思路:此题要求熟练使用arguments的索引 ~~~ function getSum() {    return arguments[0] + arguments[1]; } ​ console.log(getSum(4, 7)); ~~~ ### **实例** 1. 定义一个函数,求斐波那契数列 1 1 2 3 5 8 13 21... 中的第n个数的值,并将该值返回通过观察可以知道斐波那契数列( Fibonacci )的规律:第一项+第二项 = 第三项,即前两项之和等于第三项。假设第三项是n3,则它的前一项就是n2,前两项就是n1,已知n1和n2,则n3可以由n1+n2求得,所以不管n是多少,都有n1+n2=n3;n3+n4=n5 ...... (n-2) + (n-1) = n 推导得出。 ~~~ //用于记录n的前两项 var n1 = 1; //用于记录n的前一项 var n2 = 1; //用于记录n var n3; ​ function getFib(n) {    //因为n1和n2已经确定是1,所以循环从第三项开始    for (var i = 3; i <= n; i++) {        n3 = n1 + n2;        //每求得一项,就要向前递推数据        n1 = n2;        n2 = n3;   }    return n3; } ​ console.log(getFib(8)); ~~~ 2. 定义一个函数,用于翻转数组,返回翻转后的新数组 注意翻转数组的关键在于:`newArray[newArray.length] = array[i];` 逆向遍历数组和依次存入新数组。 ~~~ function reverse(array) {    var newArray = [];    for (var i = array.length - 1; i >= 0; i--) {        newArray[newArray.length] = array[i];   }    return newArray; } ​ // var arr = [1, 2, 3, 0, 9, 7, 12]; var str = ['ac', 'yy', 'zs']; console.log(reverse(str)); ~~~ 3. 定义一个函数,用于对数组作从小到大排序 >[success]思路:此题可以使用我们之前学过的冒泡排序算法 ~~~ //定义一个数组 var arr = [86, 57, 99, 61, 18, 40, 34]; function sort(array) {    //外层循环:控制比较的趟数    for (var i = 0; i < array.length - 1; i++) {        //假设已经排序完成        var isOver = true;        //内层循环:控制比较的次数,比较元素大小,交换元素        for (var j = 0; j < array.length - 1 - i; j++) {            //比较两个元素的大小            if (array[j] > array[j + 1]) {                //未完成排序                isOver = false;                //交换元素位置                var temp = array[j];                array[j] = array[j + 1];                array[j + 1] = temp;           }       }        //如果已经排序完成,则终止循环        if(isOver){            break;       }   }    //返回排好的数组    return array; } //调用函数 var a = sort(arr); console.log(a); ~~~ 4. 定义一个函数,函数可以接收一个年份,函数中判断该年份是否是闰年 >[success]什么是闰年?满足以下两点中的任何一点的年份,就是闰年! (1)能被4整数并且不能被100整数 (2)能被400整数 ``` function isLeapYear(year) {    var isRun;    if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {        //闰年        isRun = true;   } else {        //平年        isRun = false;   }    return isRun; } var result = isLeapYear(1988); //将结果转成文字 var isRun =  result?'闰年':'平年'; console.log(isRun); ``` 5. 定义一个函数,输入年月日,判断这一天是这一年的第几天? >[info]需求分析: (1) 循环累加当前月份的天数, 1,3,5,7,8,10,12月,每月31天, 4,6,10,11月,每个月30天; (2) 2月份需要判断闰年,调用前面写好的isLeapYear()函数即可; ~~~ //定义函数 function getDay(year, month, day) {    //接收外部变量    var d = day;    //循环每个月,累加每个月的天数    for (var i = 1; i < month; i++) {        //使用if语句        if (i === 1 || i === 3 || i === 5 || i === 7 || i === 8 || i === 10 || i === 12) {            d += 31;       } else if (i === 4 || i === 6 || i === 9 || i === 11) {            d += 30;       } else {            // 2月份            //调用判断闰年的方法            if (isLeapYear(year)) {                d += 29;                console.log('闰年');           } else {                d += 28;                console.log('平年');           }       }   }    return d; } ​ // 测试 var d1 = getDay(1988, 3, 2);//60+2 console.log(d1); var d2 = getDay(1989, 4, 2);//90+2 console.log(d2); ~~~ <br> ## **匿名函数** &emsp;&emsp;没有名字的函数称作匿名函数。如下这种定义函数的方式叫函数表达式,函数表达式等号右边部分的函数没有名字,叫匿名函数。 >[success]var fn = function() { // 函数体} ### **语法格式** >[success]//定义 var 变量名 = function( [形参1,形参2...] ){ &emsp;&emsp;//函数体 &emsp;&emsp;[return 返回值;] } //调用 var 返回值 = 变量名(); 举例 ``` //1.定义无参数、返回值的匿名函数 //定义匿名函,并将匿名函数赋值给一个变量,这样就可以通过变量进行调用    var fn = function() {      // 函数体        console.log('hello');   } //2.调用匿名函数 fn(); ``` ``` //1.定义有参数、返回值匿名函数 //定义匿名函,并将匿名函数赋值给一个变量,这样就可以通过变量进行调用    var fn = function(a, b) {      // 函数体        console.log('hello');        return a + b;   } //2.调用匿名函数 var c = fn(2, 3); ``` ​ ## **自调用函数(了解)** &emsp;&emsp;匿名函数不能通过直接调用来执行,所以需要将其赋值给一个变量,才能完成调用。除了赋值给变量的方式外,还可以通过自调用的方式来调用(执行)匿名函数。 >[success]格式:()(); 解释:第一个小括号写的是匿名函数的定义,第二个小括号写的是需要给匿名函数传入的参数,如下: (function (a) {  alert(a); })(888); //以上代码输出结果是 888 ## 函数是一种数据类型(熟悉) &emsp;&emsp;函数是一种数据类型,所以我们可以使用typeof关键字查看函数的类型。 ~~~ function fn() {} console.log(typeof fn);//function ~~~ ### **函数作为参数** &emsp;&emsp;因为函数也是一种数据类型,所以可以把一个函数当作另一个函数的实参传入,然后在另一个函数中调用。 >[success]//定义函数,用于当做另一个函数的实参 function fn() {    console.log('我是fn函数'); } //定义函数,形参用于接收其他函数 function test(func){ func();//调用外部传入的函数     } //调用test函数,将其他函数作为实参传入 test(fn); ​ ### **函数作为返回值** &emsp;&emsp;因为函数也是一种数据类型,所以可以把一个函数当作另一个函数的返回值,从另一个函数内部返回。 //写法一 ``` function fn() {   //定义一个匿名函数 test   var test = function () {       alert(1+2);   }   //返回一个函数   return test; } ``` //写法二 ``` function fn() {    //返回一个函数    return function () {        alert(1+2);   } } ``` >[info] //使用变量来接收fn函数的返回值,返回值是一个函数 > var func = fn(); > //调用返回的函数 > func(); > //以上步骤可以简写为一步 > fn()(); > //解释:以上fn()();的执行可分为两个步骤 > //步骤一:fn(),返回一个函数,此时可认为系统为返回的函数起了一个随机名字; > //步骤二:(); 其实是 随机名字(); 完成返回的函数的调用。 <br> ### 代码规范(记住) &emsp;&emsp;规范的代码整齐美观,易于阅读和维护,能加快开发人员对代码的理解。以下从命名规范、变量规范、注释规范、空格规范、换行规范等角度来学习代码规范。 >[success] 1.命名规范 > 变量、函数 的命名 必须要有意义 > 变量 的名称一般用名词 > 函数 的名称一般用动词或 动词+名词 > 2.变量规范   > 操作符的前后要有空格 > var name = 'zs';  5 + 6 > 3.注释规范 > // 这里是注释 > 4.空格规范 > if (true) { >       > } > for (var i = 0; i <= 100; i++) { >       > } > 5.换行规范 > var arr = [1, 2, 3, 4]; > if (a > b) { >       > } > for (var i = 0; i < 10; i++) { >       > } > function fn() { >       > } WebStorm工具生成的代码遵循规范,我们可以利用工具的这个特点。格式化快捷键:**Ctrl + alt + L**