函数

Administrator
发布于 2020-07-24 / 14743 阅读 / 0 评论 / 1 点赞

函数

函数

JavaScript里面函数指的是封装任意多条语句,可以在任何地方任意多次的调用执行。ECMAScript 中的函数使用 function 关键字来声明,后跟一组参数以及函数体

在学习函数之前,我们先要了解一个点,为什么我们需要函数???函数可以帮我们解决什么问题?

函数的定义

函数的定义有多种方式,在目前的基本阶段,我们只讲解标准方式。在这种方式下面它使用function关键字来进行定义,它的语法格式如下

第一种定义方式

function 函数名(参数...?){
    //函数代码体
}

注意事项:

  1. 函数名也算是一个标识符,所以它应该遵守标识符的命名规范(也就是我们之前所学习的变量命令规范)
  2. 函数代码体可以是任意多条语句
  3. 这里的花括号是一定不能省略了
function sayHello() {
     console.log("大家好,我叫标哥哥");
     console.log("我今年18岁了");
     console.log("很高兴今天能跟大家见面");
 }

上面的代码就相当于封装了一个sayHello的方法

第二种定义方式

var 方法名 = function(参数...){
    //函数代码体
}

根据上面的语法,我们也可以定义一个方法

var sayHello = function(){
    console.log("大家好");
}

?注意:上面的两种方式定义的函数它是有区别的

abc();
function abc() {
    console.log("hello abc");
}

//调用这个方法,去执行 这个时候代码就会报错,提示def is not a function
def();		
var def = function () {
    console.log("hello def");
}

通过第一种方式function定义出来的函数,可以在定义函数的代码之前调用,而通过var定义的函数则会报错

方法也是有数据类型的,它也可以通过type去检测,检测出来的结果是"function"

typeof sayHello;		//"fucntion"

函数的调用

函数定义好了以后是不会自已执行,它需要经过调用才会执行(所以函数也叫调用执行)。函数的调用是通过函数名来进行的,它通过函数名()来进行调用

也主是说上面我们封装的方法可以通过下面的代码来调用执行

sayHello();

函数的参数

现有如下场景,请看代码

 // 思考这个变量应该定义在方法里面,还是在方法外边???
var userName;
function sayHello() {
    console.log("*****************************");
    console.log("大家好,我叫" + userName);
    console.log("*****************************");
}
//现在是标哥打招呼
userName = "标哥哥";
sayHello();
//现在又是小奥打招呼
userName = "小奥";
sayHello();
//何陈龙
userName = "何陈龙";
sayHello();

当我们去调用sayHello()的方法的时候,我们就看到可以根据变量userName去动态的改变打印的内容了

这个时候,我们把userName的变量定义在方法的外边,因为如果定义在方法的里面以后它就是一个局部变量,外边无法访问到这个变量。这样定义在方法外边形成全局变量以后有没有什么隐患呢?

全局变量在外边任何地方,任何时候也都可以使用,这样很容易出问题。会很容易形成变量冲突。所以我们的userName就会存在一个很尴尬的情况,既不能在外边,也不能在里边,那我就定义在方法上面

现在我们就以引入参数的概念了

function sayHello(userName) {
    //这个时候的userName就相当于一个特殊的变量,里面可以使用,外边也可以使用
    //这个特殊的变量,我们就叫函数的参数
    //方法外边只能赋值,方法里边即可以赋值,也可以取值
    console.log("*****************************");
    console.log("大家好,我叫" + userName);
    console.log("*****************************");
}

//现在是标哥打招呼

sayHello("帅气的标哥哥");

//现在又是小奥打招呼
sayHello("漂亮的小奥");

//何陈龙
sayHello("有钱的何陈龙");

参数是没有限制条件的,你需要多个少,你就定义多少个,下面的案例当中,我们就定义了两个参数

 function sayHello(userName, age) {
     console.log("********************************");
     console.log("大家好,我叫" + userName);
     console.log("我今年" + age + "岁了");
     console.log("--------------------------------");
 }

//标哥哥打招呼
sayHello("帅气小伙", 18);

//小奥打招呼 
sayHello("卢小奥", 19);

//何陈龙打招呼
sayHello("何陈龙", 20);

?应用点:如果你在封装函数的过程当中,如果发些某值是需要通过外部来给定的,我们就可以使用参数了

实参与形参

形参:形式参数,指的是函数在定义的时候括号里面的参数名

实参:实际参数,调用函数的时候,括号里面的实际参

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

这个时候的a与b就是形式参数,没有具体的值,也没有具体的类型

var x = 11,y=13;
add(x,y);

这个时候的x与y就是实际参数,因为这个时候的x与y就是一个实际的值的

通过我们之前的调用函数的理解,在调用函数add的时候,实参x赋值给了形参a,实参y赋值给了形参b

函数在调用的时候,是实参赋值给开参,但是实参与形参的个数是没有必要形成一一的对应关第的

函数调用实参形参

所以上面的方法在调用的时候我们完全可以有现面的情况产生

add("hello");			//这个时候的实参小于形参
add(10,20,30);			//这个时候的实参大于形参

函数的重载

函数的重载指的是函数在定义的时候,它的方法名相同,而函数的参数个数与参数类型不相同。这个时候这种同名的函数就称之为重载函数(overload)

JavaScript里面的函数没有重载。只有强类型语言里面的函数才有重载,现在我们来对比一下

C#强类型语言里面的重载

public static void add(int a, int b)
{
    Console.WriteLine("第一个方法");
    Console.WriteLine(a + b);
}

public static void add(int a, int b, int c)
{
    Console.WriteLine("第二个方法");
    Console.WriteLine(a + b + c);
}

public static void add(string a,string b)
{
    Console.WriteLine("第三个方法");
    Console.WriteLine(a + b);
}
//上面定义了三个同名的方法,只是它们的参数类型或参数的个数不相同

static void Main()
{
    //add(1, 2);  调用的是第一个方法 

    //add(1, 2, 3);  调用的是第二个方法

    //add("hello", "world"); 调用的是第三个方法
}

但是在JavaScript里面,它没有函数重载,为什么 ,请看下面的代码来分析结果

function add(a,b){
    console.log("第一个方法");
    console.log(a+b);
}

function add(a,b,c){
    console.log("第二个方法");
    console.log(a+b+c);
}

这个时候我们看到它的函数名相同,但是参数的个数不相同。我们的重载是根据参数的类型与个数来决定的。但是JavaScript的这两个点都法实现

  • JavaScript是弱类型语言,变量在没有赋值之前全部都是undefined,所以不能通过类型去区分
  • JavaScript函数里面的参数,实参与形参没有必要形成一一对应的关系,这样也无法通过参数的个数去区分

正是因为没有重载,所以当JavaScript里面出现同名函数的时候,是会发生覆盖的


函数的返回值

函数的返回值指的是函数将内部某个些值返回到函数的外边,它使用的关键字是returnJavaScript每个函数都有返回值

首先我们可以通过下面最简单的代码来理解函数的返回值

function abc(){
    return "hello";
}

var x = abc();			

这个时候的abc函数就有一个返回值,这个返回值就是"hello",然后变量x就接收了函数 abc的返回值,x的值最终就是"hello"

function add(a, b) {
    var c = a + b;

    //这样变量c就会被返回出来
    return c;
}

var x = 10;
var y = 20;
var z = add(x, y);

这个时候的变量z就是接收函数add的返回值,最终的结果就是30

关于return关键字

return关键字在函数里面用将值返回到函数外边。同时要注意,一个函数一旦碰到了return关键字就跳出了当前函数,return后面的代码就不会再执行了,如下所示

 function abc() {
     var a = 11 + 22;
     return a;	//返回一个值,跳出函数
     console.log("求和的结果为"+a);		//这一行代码 永远也不会执行
 }

var x =  abc();

在有些时候,return关键字也可以用于跳出当前函数,后面可以不用接任何值

function abc(){
    var a = 123;
    return ;			//函数在这里就跳出去了,
    console.log("hello");
}
abc();

递归函数

如果一个函数在内部又调用了自己,那么这个函数就称之为递归函数

递归函数是可以通过循环的演变来理解的,同学们请先看下面的代码

// 假设要实现1~10之前求和
var sum = 0;
for (var i = 1; i <= 10; i++) {
    sum += i;
}
console.log(sum);

在上面的代码里面,我们会理清楚下面的几

循环的条件

  1. 起始值:i=1;
  2. 结束条件:i<=10;
  3. 自变量:i++

循环的本质

  1. i<=10;
  2. sum+=i;
  3. i++;

现在我们只需要让上面的代码不停的按1,2,3这三个步骤去运行就可以得到结果了,不一定非要使用循环

所以这个时候,我们可以将代码演变成下面的方式

var i = 1; //初始值
var sum = 0 //求和的值

function add() {
    sum += i; //代码体
    i++;
    if (i <= 10) {
        add();		//在这行代码里面又调用了自身
    }
}
add();
console.log(sum);

在上面的代码里面,我们编写了一个add的函数 ,这个方法的内部就使用了递归,它在内部又调用了自己。

我们通过分析可以得了, add的函数的代码运行其实就是循环里面的本质性代码运行

?案例:斐波拉契数列中递归的应用

现有一系列数,排列是这这样的1,1,2,3,5,8,13,21,34…,请求出这个数的第n项是多少?

第一种解法:通过循环语句去完成的

//假设我们要求如第20项的数是多少???
var a = 1; //前二项
var b = 1; // 前一项
var c; //代表当前项
for (var i = 1; i <= 9; i++) {
    if (i == 1 || i == 2) {
        c = 1;
    } else {
        c = a + b;  //这是完成了当前项的计算

        a = b;      //前一项的值变成前二项
        b = c;      //当前的值就变成前一项,这们做是为了方便下一次求值
    }
}

第二种解法:通过递归去完成的

//假设我们要求如第20项的数是多少???
//n是一个参数,代表求第几项
function abc(n) {
    if (n == 1 || n == 2) {
        //得到的结果应该是1
        return 1;
    } else {
        //如果不是第1项或第2项,是拿前两项前一项之各
        return abc(n - 1) + abc(n - 2);
    }
}
//求第9项的值
var x = abc(9);

?递归案例:求1~n之前所有质数的和

第一种方式:使用for循环去完成

function getSum(n) {
    var sum = 0; //所有质数的和
    for (var a = 2; a <= n; a++) {
        var flag = true; //假设这个数是质数
        for (var b = 2; b < a; b++) {
            if (a % b == 0) {
                flag = false;
                break;
            }
        }
        if (flag) {
            sum += a;
        }
    }
    //将所有质数的和返回
    return sum;
}
getSum(10);

第二种方试:使用递归去完成

function getSum(n) {
    var sum = 0;

    var a = 2;
    _a();
    function _a() {
        var flag = true;

        var b = 2;
        _b();
        function _b() {
            if (a % b == 0 && b < a) {
                flag = false;
                return; //跳出当前方法 
            }
            b++;
            if (b < a) {
                _b();
            }
        }

        if (flag) {
            sum += a;
        }
        a++;
        if (a <= n) {
            _a();
        }
    }
    return sum;
}
getSum(10);

变量的作用域

我们之前已经学过通过var来定义了变量,通过var关键字来定义的变量是没有“块级作用域”。具体可以看下面的代码

var a = 123;    //全局变量
if (true) {
    console.log(a);
}
//---------------------------------------------
{
    var b = 456;  //全局变量
}
console.log(b);
//---------------------------------------------
for(var i=1;i<2;i++){
    var c = "hello";  //全局变量
}
console.log(c);

这几个变量在这里都是全局变量,这里的花括号没有形成作用域 ,它与其它的编程语言完成不全一样

但是要注意,function的花括号是会形成作用域

function abc() {
    //局部变量,只能在这个函数的花括号内部去使用,出了花括号就不能使用了
    var userName = "标哥哥";
}
console.log(userName);   //这个地方,我们就会看到报错了,因为userName是一个局部量
是否在方法里面变量范围全局变量局部变量

评论