第五节:JavaScript函数的认识

一.初步了解函数

为什么要有函数呢,我们先从函数的功能出发,来认识一下函数给我们带来的好处

比如:

if( 1 > 0 ){
    console.log("a");
    console.log("b");
    console.log("c");
}
if( 2 > 0 ){
    console.log("a");
    console.log("b");
    console.log("c");
}
if( 3 > 0 ){
    console.log("a");
    console.log("b");
    console.log("c");
}

当我们满足一定条件要执行一块代码,要执行一个功能,这就这个功能在很多条件满足的情况下都要执行.,我要是这么写,你会发现我代码的重复性非常高,你看这些代码都是重复的.

还记得我们css中的属性重复吧,我们可以通过分组解决重复代码的复用问题

那这种重复在我们js中也是不被推荐的 ,我们管这种重复交耦合,耦合就是重复冗余的.

那如何解决这种耦合问题呢?

js中也存在一个这种封装的盒子, 来对代码进行复用, 这个封装的合资就是函数



简而言之, 就是讲一些复用的代码封装在一个函数中,通过函数在不同地方的使用来复用封装的代码
直接上代码康康:

function test(){
    console.log("a");
    console.log("b");
    console.log("c");
}
if( 1 > 0 ){
    test();
}
if( 2 > 0 ){
   test();
}
if( 3 > 0 ){
   test();
}

函数组基本的作用就是简化代码, 提高开发效率


1. 函数的定义和调用

函数和变量一样, 先定义(声明)在使用

//定义一个函数,函数就是一组语句的集合
function haha(){
    console.log(1);
    console.log(2);
    console.log(3);
    console.log(4);
}

//调用函数
haha();


1.1 函数的声明

定义一个函数,用关键字function来定义

  1. function就是英语“功能”的意思。表示这里面定义的一个功能。
  2. function后面有一个空格,后面就是函数名字,函数的名字也是标识符,因此命名规范和变量命名是一样的。
  3. 名字后面有一对儿圆括号,里面放置参数,一会儿在介绍参数。然后就是大括号,大括号里就是封装的函数语句

定义一个函数:

function 函数名(){

}

函数如果不调用,那么里面的语句就一辈子不会执行,不调用就等于白写。


1.2 函数的调用

调用一个函数的方法非常简单,函数名后面加一个() ,() 是一个运算符,表示执行一个函数。

function theFirstName(){
    
}
console.log(theFirstName);  // 弱类型的语言用于不输出内存地址,,输出指针指向的房间

执行函数语句:

函数名()

一旦调用了函数,函数内部的语句就会执行。

函数是一些语句的集合,仔细体会, 感觉上函数就是让一些零散语句成为一个军团,集体作战。要不出动都不出动,要出动就全出动。前提得到命令(调用)才会出动(执行)。


2. 函数的参数

2.1 参数的了解

上一节了解了函数的定义和使用, 但是会发现, 函数内部的语句都是相同,
那么如果我们需要在不同的地方, 使用不同语句, 就回有问题, 那么如何解决这个问题呢?
实际上我们可以通过“参数”这个东西,来让语句有差别。

定义函数的时候,内部语句可能有一些悬而未决的量,就是变量,这些变量,我们要求在定义的时候都罗列在小括号中:

比如:

function fun(a){
    // 参数就相当于隐式的在函数体呢var a;
    console.log("我第" + a + "次说爱你");
}

调用的时候,要把这个变量的真实的值,一起写在括号里,这样随着函数的调用,这个值也传给了a:

执行这个函数:

fun(88);

罗列在function小括号中的参数,叫做形式参数;调用时传递的数值,叫做实际参数。

形式参数就像占位置,先把位置站好,等你来赋值;


2.2 参数个数

参数可以有无数个,用逗号隔开。

//有多少形式参数都可以,都罗列出来
function fun(a,b){
    console.log(a + b);
}

fun(3,5);   //输出8
fun(8,11);  //输出19

定义函数的时候,参数是什么类型的没写,不需要指定类型

function sum(a,b){
    console.log(a + b);
}

也就是说调用的时候,传进去什么什么类型,就是a、b什么类型

sum("5",12);

输出512,做的是连字符的运算。

有了参数以后,函数就实现了真正的功能

function sum(a,b){
    var c = a + b;
    console.log(c);
}
sum(1,3);    // 这就是一个求和的功能吧
// 咱们就相当于把两个函数的规则抽象出来了
// 这个时候是不是就像我们数学中x,y, 随便代换值
// 这里面变量就是参数
// 有了参数以后这里就变成了抽象规则了,而不是原来的那种聚合代码的作用
// 原来只是为了聚合代码 不让代码重复 现在就是抽象规格的作用了
// 有参数才变得强大,没参数发现没什么用

例子:

function sum(a,b){
    if( a > 10){
        console.log( a - b );
    }else if( a < 10){
        console.log( a + b );
    }else{
        consol.log(10);
    }
}


2.3 实参和形参个数不等(天生不定参数)

我们还可以发现,定义的时候和调用的时候参数个数可以不一样多,不报错

sum(10);          //NaN 因为计算的就是10+undefined = NaN

因为我们只传了一个参数,b就没有传递,b被隐式的var了,所以值undefined。10+undefined就是NaN

sum(10,20,32,23,22,2,4);   // 30

只有前两个参数被形参接收了,后面的参数无视了


2.4. arguments

JS有一个非常强大的东西,就是每一个函数内部,都可以使用一个arguments这个类数组对象。

这个arguments对象,就涵盖了所有实参。

调用函数的时候,比如:

fun(45,436,457,34,23,12);

此时函数内部,arguments就有一个下标,就依次等于上面调用的数:

arguments[0]  // 45
arguments[1]  // 436
……
arguments[5]  //12

如果函数里面有形式参数列表,那么是和arguments同步的:

function fun(a,b){
    arguments[0] = 8;   //改变了第一个参数的值
    alert(a);       //8  ,弹出改变后的值
    // agruments[0]和a 不是同一个变量,只是函数内部有一个绳子,叫映射规则,就是我变你也的变,你变我也得变
    
    //  实参有几个,arguments就有几个,此时
    // 没有arguments[1] 
    b = 2;
    console.log(argumnts[1]);
    // 就算此时你把b改为2  也不会往arguments里面加了,
    // arguments 出生有几个就是几个
    
}

fun(45);

arguments的功能,是模拟函数的重载,使得同一个函数,根据参数个数的不同,有不同的作用。

那什么是重载呢?

在Java中,同一个函数名,但是参数个数不一样,视为是两个函数。

也就是说,Java中能够定义两个:

function sum(a,b){
}
function sum(a,b,c){
}

同名的两个function,都是sum函数,但是java中是允许这么做的,因为参数个数不一样,这种现象叫做重载。

JavaScript没有重载的概念:

比如现在我们想设计一个sum函数,如果传进来一个参数,就得到这个数字的加1;如果是2个参数,那么返回两个数字的和。比如

sum(10);  //1
sum(3,4);  //7

就要通过arguments.length 实际参数的个数,来进行判断:

function sum(a,b){
    //如果实际参数的长度是1,说白了,你只传进来一个参数
    switch(arguments.length){
        case 1:
            return ++a;
            break;
        case 2:
            return a + b;
            break;
    }
}

更牛逼的,我们可以无限参数,设计一个函数sum,能够接受无限参数

sum(3,4,6,5,8,2)   //28

获取实参形参个数,判断实参和形参的个数;

function sum(a,b,c,d){
    if()
}

用函数算累和

function sum(){
    var result = 0;
    for(var i = 0; i < arguments.length;i++){
        result += arguments[i]
    }
    console.log(result);
}
sum(1,2,3,4)

这是不是就是不定参的好出吧,系统内置的一些函数都是不定参的.


3. 返回值

3.1 函数的返回值

函数可以通过参数来接收东西,更可以通过return的语句来返回值,“吐出”东西。

既返回也终止

function sum(a,b){
    return a + b;       //现在这个函数的返回值就是a+b的和
}

console.log(sum(3,8));  //sum没有输出功能,就要用console.log输出
//sum(3,8)实际上就成为了一个表达式,需要计算
//计算后就是11,console.log(11);

函数只能有唯一的return,有if语句除外

程序遇见了return,将立即返回结果,返回调用它的地方,而不执行函数内的剩余的语句。

function fun(){
    console.log(1);
    console.log(2);
    return;            //返回一个空值
    console.log(3);  //这行语句不执行,因为函数已经return了
}

fun();    //1, 2
console.log(fun()); //undefined
function myNumber(target){
    return +target;
}
var num = myNumber("123");
console.log(typeof num + " " + num);


3.2 函数作为参数使用

函数有一个return的值,那么现在这个函数,实际上就是一个表达式,换句话说这个函数就是一个值。

所以这个函数,可以当做其他函数的参数。

sum(3,sum(4,5));     //12
//输出12,实际上有两次执行了sum函数
//先执行最内层,计算出9,然后 sum(3,9)
//就是12

程序从最内层做到最外层, sum(3,9) 12

函数可以接受很多值,返回一个值,

函数的意义:

  1. 在出现大量程序相同的时候,可以封装为一个function,这样只用调用一次,就能执行很多语句。

  2. 我们在调用一个函数的时候,不用关心函数内部的实现细节,甚至这个函数是你上网抄的,可以运用。所以这个东西,给我们团队开发带来了好处。

  3. 模块化编程,让复杂的逻辑变得简单。


4. 应用函数简化编程

我们现在做一个程序,输出2~100的所有质数。所谓的质数,就是只有1、自己两个约数,没有其他约数。

要把一个复杂的问题,拆分为一个个小问题。

高层的业务,就能使用底层的函数提供的API:

约数个数函数 → 判断质数函数 → 高层业务

//约数个数函数:能够传入一个数字,吐出来它约数的个数
function yueshugeshu(a){
    //计算a这个数字约数的个数
    var count = 0;
    for(var i = 1 ; i <= a ; i++){
        if(a % i == 0){
            count++;
        }
    }
    return count;   //返回这个数字的约数的个数
}
//判断是否是质数,如果一个函数的名字取is
//就暗示了将返回布尔值,要么是true要么是false。是通常做法,不是规定
//接收一个参数m,返回是否是质数t或者f
function isZhishu(m){
    if(yueshugeshu(m) == 2){
        return true;
    }else{
        return false;
    }
}
//寻找1~100的质数
for(var i = 1 ; i <= 100 ; i++){
    if(isZhishu(i)){
        console.log(i);
    }
}


5. 递归

怎么解出来不重要,这种解题方法一定要知道,这种方式叫递归

就是频繁调用自己,只有一个好处简化代码,除此之外,没有其他好处,

递归是不是实现的块啊,递归是最慢的,特别复杂的程序不能用递归,为什么慢,想想为什么慢

// !n  阶乘
function mul(){
    if( n == 1 || n == 0){
        return 1;
    }
    return n * mul(n - 1);
}

递归的规律,先执行的最后执行完


二. 函数表达式

定义函数除了使用function之外,还有一种方法,就是函数表达式。就是将函数声明赋值给一个变量

如果现在这个函数表达式中的function不是匿名的,而是有名字的:

// 命名函数表达式
var haha = function xixi(a,b){
    return a + b;
}
// 这种定义方式
console.log(haha.name);  // xixi

那么JS表现非常的奇怪,在外部只能用haha()来调用,xixi()非引发错误!此时可以console.log()函数名

console.log(haha);   // 为函数体
console.log(xixi);   // Uncaught ReferenceError: xixi is not defined

所以此时函数可以没有名字,称为“匿名函数”,为了今后能够调用它,我们把这个匿名函数,直接赋值给一个变量。

// 匿名函数表达式  
var haha = function(a,b){
    return a + b;
}
// 因为比较常用,所有以后我们讲的函数表达式就是指匿名函数表达式,
// 如果要讲命名函数表达式会特殊强调

以后想调用这个函数的时候,就可以直接使用haha变量来调用。

console.log(haha(1,3));

也就是说,JS这个奇怪的特性,给我们提了个醒,定义函数,只能用这两种方法,但是不能杂糅:

第一种,通过函数声明定义函数

function haha(){
    
}

第二种,通过匿名函数的赋值定义函数

var haha = function(){
    
}

尽量不要用:

var xixi = function haha(){
}


三. 函数声明的提升(预解析)

JS在执行前,会有一个预解析的过程,把所有的函数声明,都提升到了最最开头,然后再执行第一行语句。

所以,function定义在哪里,都不重要,程序总能找到这个函数。

//先调用
fun();  //可以弹出警告框,因为函数有函数声明头提升的特性
//然后定义
function fun(){
    alert("我是函数,我执行了!");
}

不会引发错误,alert能够弹出。

函数声明会被提升,但是函数表达式却不会被提升

函数表达式提升的是变量,变量提升后并不是一个函数,所以在表达式之前执行,会报错,为类型错误,因为不是函数.

// ************************************
//函数表达式不会有提升的
fun();  //报错
var fun = function(){
    alert("我是函数,我执行了!");
}

又给我们提了个醒,没有极特殊的理由,都要使用function 关键字来定义函数,而不要使用函数表达式来定义函数.


1.函数优先

aaa();  //现在这个aaa到底是函数,还是变量5呢?
//函数优先,遇见同名标识符,预解析阶段一定把这个标识符给函数

var aaa = 5;        //定义一个变量,是5

function aaa(){
    alert("我是aaa函数,我执行了");
}

面试很容易考:

foo();
var foo;
function foo(){
    console.log(1);
}
foo = function(){
    console.log(2);
}

函数优先,现在foo这个标识符冲突了,一个函数叫做foo,一个变量也叫作foo。预解析阶段,如果遇见标识符冲突,这个标识符给函数。

函数声明的提升,是无节操的,强制提升,即使用if语句,也会提升。


四.IIFE

IIFE就是immediately-invoked function expression,即时调用函数表达式

如果一个函数,在定义的时候,我们就想直接调用它,就是一个IIFE。

我们试图在定义函数的后面,直接写圆括号:

function fun(){
    alert("哈哈")
}();

控制台报错,这是因为函数是一个函数体,并不是表达式,只有表达式能够用()来执行。

所以就要把function fun(){}“降级”, 从函数体降级为表达式。方法有很多:

+function fun(){
    alert("哈哈")
}();
-function fun(){
    alert("哈哈")
}();

更通常更常用的:

(function fun(){
    alert("哈哈")
})();

用这种方法定义的函数,名字是无效的,其他的地方想调用这个函数

fun();

所以IIFE里面的函数,都是匿名函数:

(function(){
    alert("哈哈");
})();

上面就是一个标准的IIFE。

例子:设计一个函数,这个函数接收三个参数,比如sum(4,7,9);返回的是前两个数字大的那个数字,与第三个数字的和。

sum(4,2,3);   //7
sum(2,4,3);   //7
sum(5,4,3);   //8
sum(a,b,c){
    return (function(a,b){
        return a >= b ? a : b;
    })(a,b) + c;
}

红色部分是一个IIFE,本质上是一个表达式,表达式计算之后,就是值,什么值呢?a、b中大的那个数字。


五. 引用类型

我们之前说的,基本类型:number、string、boolean、undefined、null

function fun(){

}
console.log(typeof fun);   // funtion

你可能感兴趣的:(第五节:JavaScript函数的认识)