JS基础之函数--声(创建)明方式、函数类型/返回值/形参实参、作用域、匿名函数、箭头函数、递归函数、预解析...

在这里插入图片描述

一、概述
  • 描述:函数就是封装了一段可以被重复执行调用的代码块
  • 语法:function 函数名(函数参数1,函数参数2,函数参数3…){函数体}
  • 函数的调用:函数名(函数参数1,函数参数2,函数参数3…)
  • 函数参数可有可无

不使用函数的弊端:

  • 冗余代码太多
  • 需求变更后,需要修改很多代码
        // 向左变道
        console.log("打左转向灯");
        console.log("踩刹车");
        console.log("向左打方向盘");
        console.log("回正方向盘");

        // 向右变道
        console.log("打右转向灯");
        console.log("向右打方向盘");
        console.log("回正方向盘");

        // 向左变道
        console.log("打左转向灯");
        console.log("踩刹车");
        console.log("向左打方向盘");
        console.log("回正方向盘");

使用函数的优点:

  • 让大量代码重复使用(冗余代码变少了)
  • 需求变更, 需要修改的代码变少了
        // 定义向左变道的函数
        function toLeft(){
            console.log("打左转向灯");
            console.log("踩刹车");
            console.log("向左打方向盘");
            console.log("回正方向盘");
        }
        // 定义向右变道的函数
        function toRight(){
            console.log("打右转向灯");
            console.log("向右打方向盘");
            console.log("回正方向盘");
        }
        // 向左变道
        // 以下代码的含义: 找到名称叫做toLeft的函数, 执行这个函数中封装的代码
        toLeft();
        // 向右变道
        toRight();
        // 向左变道
        toLeft();
1.1 注意点
  • function 声明函数的关键字 全部小写
  • 函数的命名规则符合变量的命名规则即可
  • 函数是做某件事情,函数名一般是动词
  • 函数体允许有多句,它是函数在执行时执行的代码
  • 函数在创建时有几个参数,使用时就应有几个参数
  • 函数不调用自己不执行
  • 调用函数时候千万不要忘记加小括号
1.2 函数定义步骤

1 书写函数的固定格式
2 给函数起一个有意义的名称

  • 为了提升代码的阅读性
  • 函数名称也是标识符的一种, 所以也需要遵守标识符的命名规则和规范

3 确定函数的形参列表

  • 看看使用函数的时候是否需要传入一些辅助的数据

4将需要封装的代码拷贝到{}中
5确定函数的返回值

  • 可以通过return 数据; 的格式, 将函数中的计算结果返回给函数的调用者
function getSum(a, b){ // a = num1, b = num2;
            let res = a + b; // let res = 10 + 20; let res = 30;
            // 将res返回给函数的调用者
            return res;
        }
        
let num1 = 10;
let num2 = 20;
let result = getSum(num1, num2); // let result = res; let result = 30;
console.log(result);
1.3 函数的3种声(创建)明方式
  1. 利用函数关键字自定义函数(命名函数)
    语法:function 函数名(函数参数1,函数参数2,函数参数3…){}
 <script>
        function fn() {
            // 函数体
        }
</script>
  1. 函数表达式(匿名函数)
    语法:var 变量名 = function(函数参数1,函数参数2,函数参数3…) {};
    匿名函数不能只定义不使用,否则会报错
<script>
        var fun = function(aru) {
            console.log('我是函数表达式');
            console.log(aru);
        }
     //注意
     // (1) fun是变量名 不是函数名  
     // (2) 函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值 而 函数表达式里面存的是函数
    // (3) 函数表达式也可以进行传递参数
</script>
  1. function构造函数(很少使用)
    语法:var 变量 = new Function();
<script>
		//调用函数
        getSum(5, 18);
        function getSum(num1, num2) {
            var sum = 0;
            for (var i = num1; i <= num2; i++) {
                sum += i;
            }
            console.log(sum);
        }
        //调用函数
        getSum(1, 100);
        getSum(10, 50);
        getSum(1, 1000);
</script>

在这里插入图片描述

注意:

  • 多次重复声明同名的函数会出现后声明的函数覆盖先声明的函数
  • 对于javascript来说,把函数调用写在函数声明之前也是允许的,因为javascript存在一个隐式的函数提升(对于匿名函数不可用)
 <script>
        function study() {
            console.log('学习英语');
        }

        function study() {
            console.log('数学');
        }

        study();//数学
</script>

在这里插入图片描述

二、函数的类型
2.1 名词解释

返回值:【函数执行结束后】返回到【原本程序中函数所在的位置】,用来代替整个函数的【结果】,被称为函数的返回值。通常使用return关键词来实现。
形式参数(形参):函数在定义(声明)的时候写在小括号中的参数。形式参数只用来在函数内部使用,在函数外部形式参数失效。通常形式参数不用var声明,直接写变量名即可。
实际参数(实参):函数在调用的时候写在小括号中的参数称为实际参数。

2.2 无参无返回值函数
<script>
    function baozi() {
    		console.log("hello world");
    }
    baozi();
</script>
2.3 无参有返回值
<script>
    function baozi() {
        return '包子';
    }
    baozi();
</script>
2.4 有参无返回值
<script>
    function baozi(mianfen) {
    		console.log("hello " + mianfen);
    }
    baozi("大麦");
</script>
2.5 有参有返回值
<script>
    function baozi(mianfen, zhurou, dacong) {
        return '包子';
    }
    baozi();
</script>

注意:
函数没有通过return明确返回值, 默认返回undefined

        function say() {
            console.log("hello world");
            return; // undefined
        }
        let res = say();
        console.log(res);

return的作用和break相似, 所以return后面不能编写任何语句(永远执行不到)
调用函数时实参的个数和形参的个数可以不相同
JavaScript中的函数和数组一样, 都是引用数据类型(对象类型),既然是一种数据类型, 所以也可以保存到一个变量中(匿名函数);将一个函数保存到一个变量中,将来可以通过变量名称找到函数并执行函数

        let say = function () {
            console.log("hello world");
        }
        say();

示例1:

<script>
    //需求:求圆的面积
    function yuan(radius) {
        return 3.14 * radius * radius;
    }
    var result = yuan(10);
    console.log(result); //314

    //求平均值
    var arr = [2, 5, 1, 6, 8, 4, 9];

    function getAverage(tempArr) {
        var sum = 0;
        for (var i in tempArr) {
            sum += tempArr[i];
        }
        //返回平均值
        return sum / tempArr.length;
    }
    //函数的调用
    var result = getAverage(arr);
    console.log(result); //5

    //求长方形面积
    function squareMj(width, height) {
        return width * height;
    }
    var result = squareMj(5, 4);
    console.log(result); //20
</script>

示例2:

<script>
        // 1. return 终止函数
        function getSum(num1, num2) {
            return num1 + num2; // return 后面的代码不会被执行
            alert('我是不会被执行的哦!')
        }
        console.log(getSum(1, 2)); //3

        // 2. return 只能返回一个值
        function fn(num1, num2) {
            return num1, num2; // 返回的结果是最后一个值
        }
        console.log(fn(1, 2)); //2

        // 3.  我们求任意两个数的 加减乘数结果
        function getResult(num1, num2) {
            return [num1 + num2, num1 - num2, num1 * num2, num1 / num2];
        }
        var re = getResult(1, 2); // 返回的是一个数组
        console.log(re); //[3, -1, 2, 0.5]

        // 4. 我们的函数如果有return 则返回的是 return 后面的值,如果函数没有 return 则返回undefined
        function fun1() {
            return 666;
        }
        console.log(fun1()); // 返回 666

        function fun2() {
        }
        console.log(fun2()); // 函数返回的结果是 undefined
</script>
三、函数形参实参个数匹配
  • 如果实参的个数和形参的个数一致 则正常输出结果
<script>
    function getSum(num1, num2) {
        console.log(num1 + num2);
    }
    getSum(1, 2); //3
</script>
  • 如果实参的个数多于形参的个数 会取到形参的个数
<script>
    function getSum(num1, num2) {
        console.log(num1 + num2);
    }
    getSum(1, 2, 3); //3
</script>
  • 如果实参的个数小于形参的个数 多于的形参定义为undefined 最终的结果就是 NaN
<script>
    function getSum(num1, num2) {
        console.log(num1 + num2);
    }
    getSum(1); // NaN
</script>
四、函数形参、实参的长度问题
4.1 实参长度
 <script>
        function sum(a, b, c, d) {
            console.log(arguments);
        }
        sum(1, 4, 5, 7, 8, 9, 10);
</script>

在这里插入图片描述
遍历:

<script>
        function sum(a, b, c, d) {
            for (var i = 0; i < arguments.length; i++) {
                console.log(arguments[i]);
            }
        }
        sum(1, 4, 5, 7, 8, 9, 10);
</script>

在这里插入图片描述

4.2 形参长度
 <script>
        function sum(a, b, c, d) {
            console.log(sum.length);
        }
        sum(1, 4, 5, 7, 8, 9, 10);
</script>

在这里插入图片描述

示例:

 function sum(a, b, c, d) {
            if (sum.length > arguments.length) {
                console.log("形参多了");
            } else if (sum.length < arguments.length) {
                console.log("实参多了");
            } else {
                console.log("形参实参一样多");
            }
        }
        sum(1, 4, 5, 7, 8, 9, 10);
</script>

在这里插入图片描述

示例2:

<script>
        function sum(a, b, c, d) {
            var result = 0;
            for (var i = 0; i < arguments.length; i++) {
                result += arguments[i];
            }
            console.log(result)
        }
        sum(1, 4, 5, 7, 8, 9, 10);
</script>

在这里插入图片描述
示例3:

<script>
        function sum(a, b) {
            a = 15;
            console.log(arguments[0]);
            arguments[1] = 35;
            console.log(b);
        }
        sum(9, 10);
</script>

在这里插入图片描述

示例4:

<script>
        function sum(a, b) {
            b = 12;
            console.log(arguments[1]);
        }
        sum(15);
</script>

在这里插入图片描述

是映射关系,你变我变、我变你变,但并不是同一个!!!

五、函数arguments

因为console.log();也是通过()来调用的, 所以log也是一个函数
log函数的特点:可以接收1个或多个参数

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

为什么log函数可以接收1个或多个参数:内部的实现原理就用到了arguments

5.1 作用

保存所有传递给函数的实参,其实是一个伪数组
每个函数中都有一个叫做arguments的东东

示例1:

        function fn() {
            console.log(arguments);
            console.log(arguments[0]);
            console.log(arguments[1]);
            console.log(arguments[2]);
        }
        fn(10,15,20,30);

在这里插入图片描述
示例2:

        function fn() {
            let m=0;
            for(let i=0;i<arguments.length;i++){
                let num=arguments[i];
                m+=num;
            }
            return m;
        }
        let result = fn(10,15,20,30);
        console.log(result);

在这里插入图片描述

5.2 扩展

在开发中如果想使用类似于 console.log(); 的功能,除了使用 arguments 外,还可以使用 ES6 中新增的 扩展运算符(…)

  • 扩展运算符在等号左边, 将剩余的数据打包到一个新的数组中(只能写在最后)
let [a, ...b] = [1, 3, 5]; a = 1; b = [3, 5];
  • 扩展运算符在等号右边, 将数组中的数据解开
 let arr1 = [1, 3, 5];
 let arr2 = [2, 4, 6];
 let arr = [...arr1, ...arr2]; let arr = [1, 3, 5, 2, 4, 6];

扩展运算符在函数的形参列表中的作用:剩余参数
将传递给函数的所有实参打包到一个数组中
和在等号左边一样, 也只能写在形参列表的最后

        function getSum(...values) {
            // console.log(values);
            let sum = 0;
            for (let i = 0; i < values.length; i++){
                let num = values[i];
                sum += num;
            }
            return sum;
        }
        let res = getSum(10, 20, 30, 40);
        console.log(res);

在这里插入图片描述

六、函数形参默认值

示例1:

        function getSum(a, b) {
            // 在ES6之前可以通过逻辑运算符来给形参指定默认值
            // 格式: 条件A || 条件B
            // 如果条件A成立, 那么就返回条件A
            // 如果条件A不成立, 无论条件B是否成立, 都会返回条件B
            a = a || "lwj";
            b = b || "nb";
            console.log(a, b);
        }
        getSum(123);

示例2:

        // 从ES6开始, 可以直接在形参后面通过=指定默认值
        // 注意点: ES6开始的默认值还可以从其它的函数中获取
        // function getSum(a=4,b="lwj"){ // 或
        function getSum(a=4,b=getDefault()){
            console.log(a,b);
        }
        getSum();
        function getDefault() {
            return "wj"
        }

在这里插入图片描述

七、函数作为参数和返回值
7.1 函数作为其他函数的参数(回调函数)

将一个函数作为实参,传递给另一个函数,那么作为实参的这个函数就叫做回调函数

        let say = function () {
            console.log("hello world");
        }

        // let fn = say;
        // fn(); // 调用

        // 将函数作为其他函数的参数
        function test(fn) { // let fn = say;
            fn();
        }

        test(say);


		// 或如下写法:
		function fn(a){
           a();
        }
        fn(function(){
            console.log(1);
        });

		// 回调函数传参
        /**
         * 声明f函数并有形参a
         * 调用f函数,声明带data形参的匿名函数,将匿名函数作为实参赋值给f函数中的形参a
         * 调用回调函数,将 hi 作为实参赋值给回调函数的形参 data
        */
        function f(a){
            a("hi");
        }

        f(function(data){
            console.log(data);
        })


		// 带参数带返回值的回调函数
        function f(a) {
            var r = a(10);
            console.log(r);
            return r + 1;
        }

        f(function(data){
            console.log(data);
            return data + 1;
        });
		console.log(r);

在这里插入图片描述

回调函数应用场景:
7.2 将函数作为其他函数的返回值
        function test() {
            // 注意点: 在其它编程语言中函数是不可以嵌套定义的,
            // 但是在JavaScript中函数是可以嵌套定义的
            let say = function () {
                console.log("hello world");
            }
            return say;
        }
        let fn = test(); // 等价于 let fn = say;
        fn(); // 等价于 test()()

在这里插入图片描述

八、匿名函数
8.1 什么是匿名函数?

匿名函数就是没有名称的函数

function() {
            console.log("hello lnj");
        }
8.2 匿名函数的注意点

匿名函数不能够只定义不使用

8.3 匿名函数的应用场景
  • 作为其他函数的参数
        let say = function () {
            console.log("hello world");
        }
       function test(fn) {//let fn = say;
            fn();
       }
       test(say);

在这里插入图片描述

  • 作为其他函数的返回值
       function test() {
           /*在其他编程语言中函数不能嵌套,但在JS中可以*/
           let say = function () {
               console.log("hello world");
           }
           return say;
       }
       let fn=test();// let fn =say;
        fn();

在这里插入图片描述

  • 作为一个立即执行的函数
  • 注意点: 如果想让匿名函数立即执行, 那么必须使用()将函数的定义包裹起来才可以
       ;(function () {
           console.log("hello world");
       })();

			// 或
			(function () {
            console.log("hello it666");
       })();

在这里插入图片描述

九、箭头函数

箭头函数是ES6中新增的一种定义函数的格式
目的就是为了简化定义函数的代码

9.1 如何定义箭头函数
let 函数名称 = (形参列表) =>{
            需要封装的代码;
      }
// 通过 函数名称(); 进行调用
 let say = () => {
            console.log("hello lwj");
        }
 say();


(形参列表) =>{
            需要封装的代码;
     }
// 不能直接调用
9.2 箭头函数注意点
  • 在箭头函数中如果只有一个形参, 那么()可以省略
        let say = name => {
            console.log("hello  " + name);
        }
        say("lwj")

在这里插入图片描述

  • 在箭头函数中如果{}中只有一句代码, 那么{}也可以省略
        let say = name => console.log("hello  " + name);
        say("666");

在这里插入图片描述

十、递归函数

递归函数即函数中自己调用自己,在一定程度上可以实现循环的功能
递归函数的注意点:每次调用递归函数都会开辟一块新的存储空间, 所以性能不是很好

      function login() {
        //1.接收用户输入的密码
          let pwd = prompt("请输入密码:");
          //2.判断密码是否正确
          if(pwd !== "123456"){
              login();
          }
          //3.删除欢迎回来
          alert("欢迎回来");
      }
      login();

在这里插入图片描述

十一、作用域
11.1 初识作用域和变量

作用域:

  • 全局作用域:整个程序本身(在JavaScript中{}外面的作用域, 我们称之为全局作用域)
  • 函数作用域(局部作用域):作用在函数大括号内(在JavaScript中函数后面{}中的的作用域, 我们称之为"局部作用域")

变量类型:

  • 全局变量:在全局作用域中通过var声明的变量或者直接写出变量的变量名(形参除外)
  • 局部变量:在函数内部通过var来声明的变量

在JavaScript中定义变量有两种方式:ES6之前: var 变量名称;ES6开始: let 变量名称;

  • 通过var定义变量,可以重复定义同名的变量,并且后定义的会覆盖先定义的
        var num = 123;
        var num = 456;
        console.log(num);//456
  • 通过let定义变量, "相同作用域内"不可以重复定义同名的变量
let num = 123;
let num = 456; // 报错
  • 通过var定义变量, 可以先使用后定义(预解析)
  • 通过let定义变量, 不可以先使用再定义(不会预解析)
        console.log(num);
        var num = 123;
        console.log(num); // 报错
			let num = 123;
  • 无论是var还是let定义在{}外面都是全局变量
        var num = 123;
        let num = 123;
  • 将var定义的变量放到一个单独的{}里面, 还是一个全局变量
 		{
            var num = 123;
        }
        console.log(num);  //不会报错
  • 将let定义的变量放到一个单独的{}里面, 是一个局部变量
 {
  	let num = 123;
  }
 console.log(num);  //会报错

注意:

  • 全局变量在整个程序范围内都能整除使用
  • 局部变量只能在声明的函数内使用
  • 在ES6中只要{}没有和函数结合在一起, 那么就是"块级作用域"
 			{
            // 块级作用域
        }
        if(false){
            // 块级作用域
        }
        while (false){
            // 块级作用域
        }
        for(;;){
            // 块级作用域
        }
        do{
            // 块级作用域
        }while (false);
        switch () {
            // 块级作用域
        }
        function say() {
            // 局部作用域
        }
  • 在块级作用域中通过var定义的变量是全局变量
  • 在局部作用域中通过var定义的变量是局部变量
  • 无论是在块级作用域还是在局部作用域, 省略变量前面的let或者var就会变成一个全局变量
  • 在不同作用域范围内可以出现同名变量
 {
           let num = 123;
            {
            // 注意点: 在不同的作用域范围内, 是可以出现同名的变量的
                let num = 456; // 不会报错
            }
        }
  • 只要出现了let,在相同作用域内就不能出现同名的变量
let num = 123;
var num = 456; // 会报错


var num = 123;
let num = 456; // 会报错

示例:

 <script>
        var num = 10;
        console.log(num); //10

        function show() {
            var index = 100;
            console.log("函数内部:" + num); //函数内部:10
            console.log("函数内部:" + index); //函数内部:100
        }
        show();
</script>
11.2 作用域链
11.2.1 ES6之前的作用域链

需要明确:

  • 1.ES6之前定义变量通过var
  • 2.ES6之前没有块级作用域, 只有全局作用域和局部作用域
  • 3.ES6之前函数大括号外的都是全局作用域
  • 4.ES6之前函数大括号中的都是局部作用域

ES6之前作用域链

  • 全局作用域我们又称之为0级作用域
  • 定义函数开启的作用域就是1级/2级/3级/…作用域
  • JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链(0 —> 1 ----> 2 ----> 3 ----> 4);除0级作用域以外, 当前作用域级别等于上一级+1

变量在作用域链的查找规则:

  • 先在当前找,找到就使用当前作用域找到的
  • 如果当前作用域未找到,则向上一级作用域中查找
  • 依次类推,如果0级作用域还没找到,就报错

对于ES6之前只有在函数中才会开启作用域链

     var num=123;//0级作用域
     function test() {
         //1级作用域
         let num=456;
         function demo(){
             //2级作用域
             let num=789;
             console.log(num);
         }
         demo();
     }
11.2.2 ES6作用域链

ES6定义变量通过let
在ES6及之后在函数和块级作用域之后都会开启作用域链
ES6除了全局作用域、局部作用域以外, 还新增了块级作用域
ES6虽然新增了块级作用域, 但是通过let定义变量并无差异(都是局部变量)

ES6作用域链:

  • 全局作用域我们又称之为0级作用域
  • 定义函数或者代码块都会开启的作用域就是1级/2级/3级/…作用域
  • JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链(0 —> 1 ----> 2 ----> 3 ----> 4)
  • 除0级作用域以外, 当前作用域级别等于上一级+1

变量在作用域链查找规则:

  • 先在当前找, 找到就使用当前作用域找到的
  • 如果当前作用域中没有找到, 就去上一级作用域中查找
  • 以此类推直到0级为止, 如果0级作用域还没找到, 就报错
     let num=123;//0级作用域
     {
         //1级作用域
         let num=456;
         function test() {
            //2级作用域
             let num=789;
             console.log(num);
         }
         test();     
     }
十二、函数预解析
12.1 什么是预解析

浏览器在执行JS代码的时候会分成两部分操作:预解析以及逐行执行代码,也就是说浏览器不会直接执行代码,而是加工处理之后再执行,这个加工的过程就是预解析

预解析规则:
将变量声明和函数声明提升到当前作用域最前面
将剩余代码按照书写顺序依次放到后面

注意:
通过let定义的变量不会被提升(不会被预解析)

示例1:

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

     /*
     *如果将函数赋值给一个var定义的变量,函数不会被预解析,只有变量会被预解析
     * 此时say为undefined,那么say()就是 say is not a function
     */
     say();
     var say=function () {
         console.log("hi");
     }

     /*
     不会预解析
     * say is not defined
     */
     say();
     let say=()=>{
         console.log("world");
     }

示例2:

    var a=10;
    test();
    function test() {
        var b=777;
        console.log(a);
        console.log(b);
        console.log(c);
        var  a=888;
        let c=999;
    }
    /*
    var a;
     function test() {
        var b;
        var a;
        b=777;
        console.log(a);//undefined
        console.log(b);//777
        console.log(c);//报错
        a=888;
        let c=999;
    }
    a=10;
	*/

在这里插入图片描述

  • 在ES6之前没有块级作用域, 并且没有将这两个函数定义到其它的函数中,所以这两个函数应该属于全局作用域
  • 在高级浏览器中不会对 {} 中定义的函数进行提升
  • 只有在低级浏览器中才会按照正常的方式进行解析
        if(true){
            function demo() {
                console.log("hi");
            }
        }else {
            function demo() {
                console.log("hello");
            }
        }
        demo();//hi
  • 如果变量名称和函数名同名,那么函数的优先级高于变量
        console.log(value);
        var value=123;
        function value() {
            console.log("hi");
        }

        console.log(value);//123

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白小白从不日白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值