JavaScript 05 函数

js函数的概念和作用,js函数的定义,js函数的调用,js事件驱动的概念,js函数的实参和形参,js的作用域,js的匿名函数,js中的递归,函数中的arguments

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

欢迎大家去我的个人技术博客看看,点赞收藏注册的都是好人哦~

https://xiaowu.xyz



一、函数的概念及作用

函数就是把完成特定功能的一段代码抽象出来,使之成为程序中的一个独立实体,起个名字(函数名)。可以在同一个程序或其他程序中多次重复使用(通过函数名调用)。

那么我们为什么要用函数呢?


函数的作用:

使程序变得更简短而清晰

有利于程序维护

可以提高程序开发的效率

提高了代码的重用性(复用性)

(不使用函数,JS代码必须执行,且执行时间,执行哪部分代码都是不可控的。)

(使用函数后,浏览器不会直接执行它,只有在调用这个函数时,它才会被浏览器所执行。)


函数的分类

内置函数(系统函数,官方函数)

是官方提供好的函数,直接使用,如:alert()。

prompt(); 弹出一个带输入框的弹窗

isNaN():用于验证参数是不是数字,是not a number的意思。

setInterval(“函数名()”,1000);    //按照指定的周期(以毫秒计)来调用函数或计算表达式。此处的1000为毫秒,即是1秒。


自定义函数(用户自定义的函数)

用户根据实际需求,需要自己封装一个函数。


二、函数的定义

function关键字定义一个函数

function 函数名(参数1名字,参数2名字,……)//函数可以没有形参

{

    语句

    [return 返回值;]//函数可以没有返回值

}

函数的定义方法:

1. 写正常格式的代码 (完成某个特定的功能)

2. 将代码抽离出来  放到函数中  起一个名字(函数名  函数名的命名规范 同变量)

3. 把函数中的可变参数(变量) 改为形式参数

4.  确定函数的返回值  (函数的返回值根据实际情况制定  如果没有return  默认返回undefined)

5.  函数的调用 ( 函数名() )


示例:

function sum(one,two){

    var s = one + two

    return s;

}


三、如何执行函数(调用)


通过在程序中使用函数名称,可以执行函数中包含的语句,这称为调用函数,同时传入实参。

函数名(参数1值,参数2值,……);

alert(“你好呀小伙子”);   //这句话是调用内置函数

sum(2,8);这句话是在调用上面的自定义函数,计算2+8的结果。


四、事件驱动的概念

所有的函数,没有调用不会执行,那么函数调用的源头在何处,就是事件,我们已经学习了onclick,还有onload, onfocus、onblur等等。


示例:

制作一个简易计算器

效果:


<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>小五的技术blog-简易计算器</title>

</head>

<body>

    <input type="text" id="num1">

    <!--

        select  取值(option 中选中的值)

        (1) 如果option中有value 属性  取value中的值

        (2) 如果option中有value 属性  取option 标签中的内容

    -->

    <select name="" id="select">

        <option value="+">加</option>

        <option value="-">减</option>

        <option value="*">乘</option>

        <option value="/">除</option>

        <option value="%">余</option>

    </select>

    <input type="text" id="num2">

    <button id="btn">计算</button>

    <input type="text" id="result">

</body>

<script>

    var num1Inp = document.getElementById("num1");

    var num2Inp = document.getElementById("num2");

    var select = document.getElementById("select");

    var btn = document.getElementById("btn");

    var resultInp = document.getElementById("result");

    btn.onclick = function () {

        var a = num1Inp.value;

        var b = num2Inp.value;

        var fu = select.value;

        resultInp.value = calc(a, fu, b); //实际参数  (函数调用时  实际传入的值!!!!)

    }

function calc(num1, opt, num2) {  //形式参数

        console.log(num1, opt, num2);

        var result = 0;

        if (opt == "+") {

            result = num1 * 1 + num2 * 1;

        } else if (opt == "-") {

            result = num1 - num2;

        } else if (opt == "*") {

            result = num1 * num2;

        } else if (opt == "/") {

            result = num1 / num2;

        } else if (opt == "%") {

            result = num1 % num2;

        }

        return result;

}

</script>

</html>


五、函数的参数(实参,形参)


形参和实参:

函数的参数跟变量是一样使用。

形参就是在函数定义时,函数名后面的参数,不能用var修饰。

实参就是调用时,函数名后面的参数


参数可以传递多个,与函数的定义无关(这点和其他语言不太相同)

示例:

function test(paramX,paramY){

}

test("a","b","c");

使用arguments对象可以判断参数的个数,arguments是个数组(伪数组)。


由于js是弱类型语言,所以实参可以传递任何数据类型,甚至是传递函数。

六、作用域

作用域:就是起作用的范围。或者说有效范围。

全局变量 => 全局作用域

1. 在函数外通过关键词(var)声明的变量是全局变量  (在声明之后 可以在作用域内调用)

2. 不通过关键词声明变量也是全局变量  但是没有有变量提升


局部变量  =>  函数作用域(局部作用域)

1.在函数内通过关键词(var)声明的变量是局部变量  (只在函数内有效,不会影响全局变量);

2. 形式参数也是局部变量


讲到作用域就需要聊到JS中的一个很重要的情况:JS的预编译

js 从加载到执行 需要经历那些流程

全局作用域

     1. 语法解析

     2. 预编译

        (1) 变量提升  把作用域中所有的变量(通过var 声明的变量)提到当前作用域最前边  (不通过var 直接声明的变量没有变量提升)。

        (2) 确定函数体  把命名函数整个函数体(函数的声明)提升到当前作用域的最前边。

    3. 代码执行


函数作用域(局部作用域)

    1. 语法解析

    2. 预编译

      (1) 变量提升    把作用域中所有的变量(通过var 声明的变量)提到当前作用域最前边  (不通过var 直接声明的变量没有变量提升)

      (2) 确定函数体  把命名函数整个函数体(函数的声明)提升到当前作用域的最前边

      (3) 把实际参数赋值给形式参数

    3. 代码执行




全局变量 => 全局作用域

    1 .在函数外通过关键词(var, let ,const) 声明的变量是全局变量

    2. 不通过var 声明的变量是全局变量  (函数中也是如此)

    局部变量  =>  函数作用域(局部作用域) 

    注意 局部变量只能在函数中生效  不会影响全局变量

    1.  在函数中通过关键词(var, let ,const) 声明的变量是局部变量 

    2.  形式参数也是局部变量


关于局部作用域和全局作用域有很多有趣的案例。

var a = 1000;    // a = 1000;

    b = 100;

    console.log(a, b);

    function add() {

        var a = 10;

        var b = 20;

        c = a + b;

        return c;

    }

    add();

    console.log(c);


add();

    function add() {

        var a,b,c;

        console.log(a, b, c, fn);

        var a = 10;

        var b = 20;

        var c = a + b;

        function fn() {

            console.log(a, b, c);

        }

        return c;

    }


add(10, 20);

    function add(a, b) {

        // var c;

        // var a = 10;

        // var b = 20;

        console.log(a, b, c, fn);

        // 今天加班,

        // var a = 10;

        // var b = 20;

        var c = a + b;

        function fn() {

            console.log(a, b, c);

        }

        return c;

    }



注意:

1、变量在未声明后直接使用,会报语法错误。


2、变量声明后没有赋值直接使用


3、没有返回值的函数,接受其返回值,会得到undefined

七、匿名函数

匿名函数的定义很简单,就是指没有取名字的函数

语法:

function(){

    //功能代码;

}

我们之前用过的

var btn = document.getElementById("btn");

btn.onclick = function(){

}

就是给一个id为btn的元素绑定一个点击事件

点击事件就赋值了一个匿名函数。


1、匿名函数怎么定义和调用

由于函数是匿名函数,没有取名,所以不能通过名字调用。

通过赋值给变量来定义

通过变量名加括号调用

var a = function(){

    console.log(a);

}    

a();


匿名函数的自调用

把函数放到    括号()    里并在后面加上    括号()

(function(){

console.log("我是匿名函数");

})()

注意:匿名函数自调用只能执行一次

2、匿名函数怎么传参

还是写在function 后的括号里

var fn = function(a,b){

console.log("我是匿名函数");

}

(function(a,b){    //这里a,b是形参

console.log("我是匿名函数");

})(a,b)    //这里a,b是实参



八、递归


函数的递归调用

函数还可以自己调用自己,称为递归调用


递归属于编程中的一个小坎,我记得15年学java的时候,递归题做了几个觉得好像会了。

结果去参加算法比赛,换个题又一脸抓瞎,我好像又不会了,递归主要考察对函数的运行流程的理解。


其实递归的性能并不算很好,但是递归能快捷的解决很多看起来复杂、毫无头绪的问题。


递归的特点:

    1、自调用,就是在函数里面调用自己

    2、最关键的一点, 递归需要有临界值 (类似循环条件  满足条件的情况下递归 ,不满足条件时 结束(return )),不然就会陷入无穷的死循环

    3、缺点,就是消耗大量内存

简单案例:

求一个数的阶乘:

(n = 1*2*3*...*n)

  以5为例子

(!在数学里表示阶乘的意思,5!的意思就是5的阶乘)

    // 5   5*4*3*2*1   5! = 5 * 4!

    // 4   4*3*2*1     4! = 4 * 3!

    // 3   3*2*1       3! = 3 * 2!

    // 2   2*1         2! = 2 * 1!

    // 1   1           1!= 1

最后得出

n! = n * (n-1)!


代码:

function fn(n) {

        if (n == 1) {

            return 1;

        }

        return n * fn(n - 1);

    }

fn(10);   //调用

函数展开:

    /*

     fn(5) 的阶乘等于  5 * fn(4) => 4 * fn(3)  => 3 * fn(2)=> 2 * fn(1)   =>    1

                                5*4*3*2*1     4*3*2*1         3*2*1              2*1              1

     */


斐波那契数列的问题

    有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

        程序分析

        兔子的规律为数列1, 1, 2, 3, 5, 8, 13, 21….,也就是这个数等于后两个数之和:

        n = (n - 1) + (n - 2)  (n>=3)

        1  1  2   3 5  8 13  21  34  55  89  144   233

    */


    function feiBo(n) { //3

        if (n == 1 || n == 2) {

            return 1;

        }

        return feiBo(n - 1) + feiBo(n - 2);  // 2

    }


欧几里得辗转相除法

     求最大公约数   (假设是18 和 12)

        1.取两个数中较大的数 %  两个数中较小的数   

        2.如果余数不为0  那么继续用 除数 % 余数

        3.如果余数为0  那么此时的除数(两个数中较小的数 )就是最大公约数

            (1) 取两个数中较大的数 %  两个数中较小的数

                18(被除数) % 12(除数 )  余  6     余数不为 0

            (2) 如果余数不为0  那么继续用 除数(两个数中较小的数 ) % 余数


            (3) 如果余数为0  那么此时的除数(两个数中较小的数)就是最大公约数

                    12 % 6 余 0  


  最小公倍数  

   (两个数的乘积)/最大公约数

      18*12/6 => 36


function maxYue(x, y) {   

        // var x = 1997; //测试数据

        // var y = 615;  //测试数据

        var m = x % y;

        if (m == 0) {

            return y;

        } else {

            // y%m

            return maxYue(y, m);   // 函数调用时的实际参数  

        }

    }


    // 最小公倍数  

    // (两个数的乘积)/最大公约数

    function minBei(x, y) {

        var yue = maxYue(x, y);

        return x * y / yue;

    }

    var result = minBei(24, 18);

    console.log(result);


九、arguments


ar是函数中的内置参数

存储的是函数调用过程中的所有的实际参数的集合(是一个伪数组)


   arguments的特点

    1. 有length属性 (实际参数的个数);

    2. 可以根据下标(索引) 取值      下标的最大值 = length -1

    3. 可以遍历(循环)取值 / 可以被循环遍历


    arguments 的取值方式   

    (1)  arguments.length        arguments.属性名

    (2)  arguments["length"]     arguments["属性名的字符串"]    如果是数字的话  可以简写 不用加字符串 arguments[0]


示例:

function add(a, b, c) {

        console.log(arguments); //是函数中的内置参数 (每个函数都有);

        console.log(arguments[5]);     // arguments 的取值

        console.log(arguments["5"]);   // arguments 的取值

        console.log(arguments.length);

        console.log(arguments["length"]);

        return a + b + c;

    }

    var result = add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);

    // var result = add(1, 2, 3, 4, 5, 6);

    // console.log(result);


函数里还有一个this,我会放到后面讲。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • JavaScript的相关语法知识:1、函数(important)基本上所有的高级语言(C、OC、JavaScri...
    天山雪莲_38324阅读 659评论 0 2
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 14,037评论 0 38
  • 函数只定义一次,但可能被执行或调用任意次。JS函数是参数化的,函数的定义会包括一个称为形参的标识符列表,这些参数在...
    PySong阅读 574评论 0 0
  • 函数只定义一次,但可能被执行或调用任意次。JS函数是参数化的,函数的定义会包括一个称为形参的标识符列表,这些参数在...
    PySong阅读 341评论 0 0
  • 三、闭包和高阶函数 3.1 闭包 3.1.1 变量的作用域 所谓变量的作用域,就是变量的有效范围。通过作用域的划分...
    梁同学de自言自语阅读 1,508评论 0 6