JavaScript基础(三)

133 阅读8分钟

函数

(1)函数的概念:

函数就是封装了一段可以被重复使用的代码块;目的就是让大量代码块重复使用。

(2)函数的使用

分两步:声明函数和调用函数
a.声明函数-------对应格式
函数必须要调用才可以执行

 function 函数名(){
     函数体;
     return 返回值;
 }

注意
a.function是声明函数的关键字,全部小写

b.调用函数 函数必须要调用才可以执行

 函数名();//调用函数的时候一定要加小括号

通过调用函数名来执行函数体代码

(3)函数的封装

函数的封装就是把一个或多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口;

(封装类似于快递打包)

(4)函数的参数

 function 函数名(形参1,形参2......){
     //函数代码
 }
 函数名(实参1,实参2.....);
  • 在函数声明的小括号里面是形参(形式上的参数),形参可以看作是不用声明的变量,默认值为undefined;
  • 在函数调用的小括号里面是实参(实际的参数);
  • 形参个数和实参个数是一致的,(多个参数用逗号隔开);
  • 实参个数大于形参个数,会取到形参的个数参与运算,剩余的实参不参与;
  • 实参个数小于形参个数,剩余的形参可以看作是变量但是没有接受值,结果就是undefined,函数输出结果为NAN
  • 所以尽量实参个数等于形参个数
     //案例1:利用函数计算1 ~ 100之间的累加和
     function getNum(min,max){
       // var sum ;没有赋值,sum为undefined,sum+=i输出值为NAN
       for(var i = min;i < max + 1; i++){
         sum +=i;
       }
     console.log(sum);
     }
     getNum(1,100);
  • 形参和实参的执行过程:形参是接受实参的,实参是传递给形参的
  • 参数的作用:在函数内部某些值不能固定,我们可以通过参数在调用函数时传递不同的值进去

(5)函数的返回值

 function 函数名(){
     return 需要返回的结果;
 }
 函数名();
  • 函数只是实现某种功能,最终的结果需要返回给函数的调用者函数名(),通过return实现
  • 只要函数遇到return就把后边的结果返回给函数的调用者
     //案列2:求两个数之间的最大值
     function getMax(a, b) {
       //三元运算符代替if-else更为简单
       // var max = a > b ? a : b;
       // return max;
 ​
       return a > b ? a : b;
     }
     console.log(getMax(100, 99));
  //案例3:利用函数求任意数组中的最大值
     function getMax(arr) {
       var max = arr[0];
       for (var i = 1; i <= arr.length; i++) {
         if (arr[i] > max) {
           max = arr[i];
         }
       }
       return max;
     }
     // console.log(getMax([1, 0, 3, 9, 6, 2]));
     //一般将函数返回值存到变量中,以便后续使用
     var re = getMax([1, 0, 3, 9, 6, 2]);
     console.log(re)

注意事项:

return作用

  • 终止函数的功能,即return后的语句不会在被执行

  • 只能返回一个值

     function fn(num1,num2){
         return num1,num2;
         //只会返回第二个值,num2
     }
     console.log(fn(1,2))
    
  • 如果要返回多个值,需要将多个值写进数组中,最后返回数组

     function getResult(num1,num2){
         return [num1-num2,num1+num2];
     }
     var re = getResult(1,2);
     console.log(re);
    
  • 函数如果有return则返回的是return后边的值,没有return则返回undefined

  • return、break、continue的区别:

    • break:结束当前的循环体(如:for,while)
    • continue:跳出当前本次循环,继续执行下次循环(如for,while)
    • return:不仅可以退出循环,还能够返回return语句的值,同时还可以结束当前的函数体内的代码

(6) arguments的使用

当我们不确定有多少个参数传递的时候,可以用arguments来获取。在JavaScript中,arguments实际上是当前函数的一个内置对象,所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有实参

只有函数才有arguments对象,而且每个函数都内置好了这个arguments

     function fn(){
       console.log(arguments);
     }
     fn(1,2,3)

输出结果:

image.png

arguments的展示形式是一个伪数组,传几个实参就存几个,因此可以按照数组的方式遍历arguments。

伪数组:并不是真正意义上的数组,有以下特点:

  • 具有数组的length属性
  • 按照索引方式存储数据
  • 没有真正数组的方法,如pop()、push()等

(7) 数组基础案例巩固

   //案例5:利用函数求任意个数的最大值
     function getMax(){
       var max = arguments[0];
       for(var i = 1;i<arguments.length;i++){
         if(arguments[i] > max){
           max = arguments[i];
         }
       }
       return max;
     }
     var re = getMax(1,4,88,100,21,0)
     console.log(re);
     //案例6:利用函数封装方式,反转任意一个数组
     function reservse(arr){
       var newArr = [];
       // for(var i = 0 ; i < arr.length;i++){
       //   newArr[i] = arr[arr.length-i-1]
       // }
       for(var i = arr.length-1;i >= 0;i--){
         newArr[newArr.length] = arr[i] ;
       }
       return newArr;
     }
     var re = reservse([1,2,3,4,5,6,7]);
     console.log(re);
     //案例7:
     //判断一个年份是不是闰年(闰年就是可以被四整除但是不能被100整除,或者是能被400整除)
     function leapYear(year) {
       var flag = false;
       if (year % 4 == 0 && year%100 != 0 ||year % 400 == 0) {
         flag = true;
       }
       return flag;
     }
     console.log(leapYear(3000))

函数可以调用另一个函数(

     //案例8:函数相互调用
     function fn1(){
       fn2();
       console.log('11');
     }
     function fn2(){
       console.log('22');
     }
     fn1();
     //案例9:函数相互调用,分析结果
     function fn1() {
       console.log(111)
       fn2();
       console.log('fn1');
     }
     function fn2() {
       console.log('222');
       console.log('fn2');
     }
     fn1();
 //输出: 111   222   fn2   fn1   
     //案例10:用户输入年份,输出当前年份2月份的天数
     //如果是闰年,则二月份就是29天;如果是平年,则二月份就是28天
     // function february(year) {
     //   var day = 0;
     //   if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
     //     day = 29;
     //   } else {
     //     day = 28;
     //   }
     //   return day;
     // }
     // var re = february(2022);
     // console.log(re);
 ​
     //函数相互调用输出2月份的天数
     function leapYear(year) {
       var flag = false;
       if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
         flag = true;
       }
       return flag;
     }
     function february(year) {
       // var re = leapYear(year);
       if (leapYear(year)) {
         return 29;
       } else {
         return 28
       }
     }
     console.log(february(2022));

(8) 函数的两种声明方式

a.利用函数关键字定义函数(命名函数)

 function  函数名(){
     函数体;
 }
 函数名();

b.函数表达式 var 变量名 = function(){};-----(函数没有自己的名字)----匿名函数

 var fun = function  函数名(){
     函数体;
 }
 fun(123);
 //fun 是变量名不是函数名
 //函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值,而函数表达式里面存的函数
 //函数表达式也可以传递参数

(9)函数作用域

a.JS作用域:

就是代码名字(变量)在某个范围内起作用和效果,目的是为了提高程序的可靠性和减少命名冲突

b.es6之前

  • 全局作用域:整个script标签或者是一个单独的JS文件
  • 局部作用域(函数作用域):在函数内部就是局部作用域,这个代码的名字只在函数内部起作用

c.根据作用域区分变量

  • 全局变量--在全局作用域下声明的变量,

    • 在代码的任何位置都可以使用;
    • 在全局作用域下var声明的变量就是全局变量;
    • 特殊情况下,在函数内部不使用var声明的变量也是全局变量(不建议使用)
  • 局部变量--在局部作用域声明下的变量

    • 只能在函数内部使用
    • 在函数内部用var声明的变量式局部变量
    • 函数的形参实际上是局部变量
  • 执行效率:全局变量只有浏览器关闭之后才会销毁,比较占用资源;局部变量在程序执行完毕之后就会销毁,比较节约资源。

      //全局变量     
      var num = 10;
      console.log(num);
      function fn1(){
       nn = 1;//在函数内部没有声明,直接复制,就是全局变量
       console.log(num);
      }
      fn1();
      //局部变量
      //函数的形参也可以看作局部变量
      function fn2(aru){
       var mm = 2;
       console.log(mm);
      }
      fn2();
      console.log(mm);

输出结果:

image.png

  • 块级作用域(ES6新增)
    块作用域由 { } 包括,if语 句和for语句里面的{ }等,在{}内有效。
    块级变量:{} 内部的声明的变量。定义的变量,只能在块作用域里访问,不能跨 块访问,也不能跨函数访问。

注意:

如果函数内部或者块级作用域内部,变量没有声明,直接赋值,也当全局变量看,但是强烈不推荐 但是有一种情况,函数内部的形参可以看做是局部变量。

(10)作用域链

内部函数可以访问外部函数变量,采取的是链式查找的方法来决定取哪个值,这种结构我们称为作用域链,采取就近原则。

     function fn1() {
       let num = 123;
       function fn2() {
         console.log(num);
       }
       fn2();
     }
     var num = 456;
     fn1();
 //一层一层往上查找,取最近的那个值,输出结果为2

image.png

(11)预解析

js引擎运行js分为两步:预解析代码执行

a.预解析 js引擎会把js里面所有的 var 还有 function提升到当前作用域的最前面

b.代码执行按照代码书写的顺序从上往下执行

2.预解析分为变量预解析(变量提升)和函数预解析(函数提升)

a. 变量提升就是把所有的变量声明提升到当前的作用域最前面不提升赋值操作

b. 函数提升就是把所有的函数声明提升到当前作用域的最前面不调用函数

     // 预解析
     //变量提升
     console.log(num);
     var num = 10;
     // 相当于以下代码
     var num;
     console.log(num);
     num = 10;
 ​
     //变量提升
     fn();
     // 函数表达式,fn是变量名,不是函数名
     var fn = function(){
       console.log(22);
     }
     // 相当于以下代码
     // 函数表达式,调用必须写在函数表达式的下边
     // 当前作用域是全局作用域,将变量提升到当前作用域前边,其余代码按照顺序写
     var fn;
     fn();
     fn = function(){
       console.log(22);
     }
 ​
    // 函数提升
     fn();
     function fn(){
       console.log(11);
     }
     // 相当于以下代码
     function fn(){
       console.log(11);
     }
     fn();

先把外部的变量提升搞完,再提升函数作用域里的

 ​
     // 案例分析
     var num = 10;
     function fn(){
       console.log(num);
       var num = 20;
       console.log(num);
     }
     fn();
     // 相当于以下代码
     var num;
     function fn(){
       var num ;
       console.log(num);//输出undefined,对应的是上一行的num
       num = 20;
       console.log(num);//输出20,对应上一行的20
     }
     num = 10;
     fn();
 ​
     //案例分析
     f1();
     console.log(a);
     console.log(b);
     console.log(c);
     function f1() {
       var a = b = c = 9;
     // var a = b = c = 9相当于 var a = 9; b = 9; c = 9;b和c直接赋值,没有var声明当全局变量看
     // 集体声明 var a = 9,b = 9, c = 9;
       console.log(a);
       console.log(b);
       console.log(c);
     }
     // 相当于以下代码
     function f1() {
       var a;
       a = b = c = 9;
       console.log(a);//9
       console.log(b);//9
       console.log(c);//9
     }
     f1();
     console.log(a);//报错:a is not defined,局部变量
     console.log(b);//9
     console.log(c);//9