第一章:函数的定义和调用

1.1 函数的定义方式

  • ①函数声明方式:function关键字(命名函数)。
  • ②函数表达式(匿名函数)。
  • ③new Function()。
  1. var fn = new Function('','',...,'函数体');
  • Function里面参数都必须是字符串格式。
  • 第三种执行效率低,不方便书写,较少使用(几乎不用)。
  • 所有的函数都是Function的实例(对象)。
  • 函数也属于对象。
  • 示例:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport"
  6. content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  7. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  8. <title>函数的定义</title>
  9. <script>
  10. // 函数的定义方式
  11. // 1. 自定义函数(命名函数)
  12. function fn() {
  13. console.log('自定义函数(命名函数)');
  14. }
  15. // 2. 函数表达式(匿名函数)
  16. var fn2 = function () {
  17. console.log('数表达式(匿名函数)');
  18. }
  19. // 3. 利用new Function('参数1','参数2',...,'函数体') 较少使用,效率低,书写不方便。
  20. var fn3 = new Function('a', 'b', 'return a+b');
  21. // 所有的函数都是Function的实例(对象)
  22. console.dir(fn3);
  23. // 函数也属于对象
  24. console.log(fn3 instanceof Object);
  25. </script>
  26. </head>
  27. <body>
  28. </body>
  29. </html>

1.2 函数的调用方式

  • 普通函数。
  • 对象的方法。
  • 构造函数。
  • 绑定事件函数。
  • 定时器函数。
  • 立即执行函数。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>函数的调用方式</title>
</head>

<body>
    <button>点击</button>
    <script>
        /* 函数的调用方式 */
        // 普通函数
        function fn() {
            console.log('我是普通函数');
        }

        fn();
        fn.call();
        // 对象的方法
        var o = {
            sing: function () {
                console.log('我是对象的方法');
            }
        }
        o.sing();

        // 构造函数
        function Star() {
        }

        new Star();
        // 绑定事件函数
        var btn = document.querySelector('button');
        btn.onclick = function () {
            console.log('我是绑定事件函数');
        }
        // 定时器函数
        setTimeout(function () {
            console.log('我是定时器函数');
        }, 1000);
        // 立即执行函数
        (function () {
            console.log('立即执行函数')
        })();

    </script>
</body>

</html>

第二章:this

2.1 函数内部this的指向

  • this的指向,是当我们调用函数的时候才能确定的。调用方式的不同决定了this的指向不同。一般指向我们的调用者。 | 调用方式 | this指向 | | —- | —- | | 普通函数调用 | window | | 构造函数调用 | 实例对象,原型对象里面的方法也是指向实例对象 | | 对象方法调用 | 该方法所属对象 | | 事件绑定方法 | 绑定事件对象 | | 定时器函数 | window | | 立即执行函数 | window |
  • 示例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>函数内部this的指向</title>


</head>

<body>
    <button>点击</button>
    <script>
        // 普通函数 this指向window
        function fn() {
            console.log('我是普通函数');
        }

        window.fn();
        // fn.call();
        // 对象的方法 this指向的是对象 o
        var o = {
            sing: function () {
                console.log('我是对象的方法');
            }
        }
        o.sing();

        // 构造函数 this指向ldh这个实例对象,原型对象里面的this也是指的ldh这个实例对象
        function Star() {
        }

        Star.prototype.sing = function () {

        }

        var ldh = new Star();
        // 绑定事件函数 this指向的是函数的调用者,btn这个按钮对象
        var btn = document.querySelector('button');
        btn.onclick = function () {
            console.log('我是绑定事件函数');
        }
        // 定时器函数 this指的是window
        setTimeout(function () {
            console.log('我是定时器函数');
        }, 1000);
        // 立即执行函数 this指的是window
        (function () {
            console.log('立即执行函数')
        })();

    </script>
</body>

</html>

2.2 改变函数内部this的指向

2.2.1 概述

  • JavaScript为我们专门提供了一些函数方法帮我们更优雅的处理函数内部this的执行问题,常用的有bind()call()apply()三种方法。

2.2.2 call()方法

  • 语法:
fun.call(thisArg,arg1,arg2,...)
  • call()方法调用一个对象。简单理解为调用函数的同时并可以改变函数的this指向。
  • thisArg:在fun函数运行时指定的this的值。
  • arg1,arg2:传递的其他参数。
  • 返回值就是函数的返回值,因为它就是调用函数。
  • 因此,当我们想改变this的执行,同时想调用这个函数的时候,可以使用call(),比如继承。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>改变函数内部this的指向之call方法</title>
</head>

<body>

    <script>
        // 改变函数内部的this指向,js提供了call()、apply()和bind()方法

        var o = {
            name: '许大仙'
        }

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

        fn.call(o, 1, 2);

        // call可以调用函数,并可以改变函数内的this指向。
        // call的主要作用可以实现继承
        function Father(uname, age) {
            this.uname = uname;
            this.age = age;
        }

        function Son(uname, age) {
            /*
            * 原来Father构造函数的this指向的是其实例化的对象,Son构造函数的this也是指向其实例化的对象。
            * 当使用下面的代码的时候,就是将Father构造函数的this指向Son构造函数的实例化对象,那么Son构造函数就不需要再写this.uname=uname,...
            */
            Father.call(this, uname, age);
        }

        var son = new Son('许大仙', 18);
        console.log(son);

    </script>

</body>

</html>

2.2.3 apply()方法

  • 语法:
fun.apply(thisArg,[argsArray])
  • apply()方法调用一个函数。简单理解为调用函数的同时并可以改变函数的this指向。
  • thisArg:在fun函数运行的时候指定的this的值。
  • argsArray:传递的值,必须包含在数组中。
  • 返回值就是函数的返回值,因为它就是调用函数。
  • 因此apply主要和数组有关,比如使用Math.max()求数组的最大值。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>改变函数内部this的指向之apply方法</title>
</head>

<body>
    <script>
        // 改变函数内部的this指向,js提供了call()、apply()和bind()方法
        var o = {
            name: '许大仙'
        }

        function fn(a, b) {
            console.log(this);
            console.log(a);
            console.log(b);
        }

        fn.apply(o, ['pink', 'red']);
        // apply可以调用函数,并可以改变函数内的this指向。
        // apply的参数必须是数组(伪数组)。
        // apply 的主要应用 比如:我们可以利用apply借助于数学内置对象求最大值
        // Math.max()
        var arr = [1, 2, 3, 4, 5, 6];
        var max = Math.max.apply(Math, arr);
        console.log(max);
    </script>
</body>

</html>

2.2.4 bind()方法

  • 语法:
fun.bind(thisArg,arg1,arg2,...)
  • bind()方法不会调用函数,但是能改变函数内部this指向。
  • thisArg:在fun函数运行时指定的this的值。
  • arg1,arg2:传递的其他参数,
  • 返回由指定的this值和初始化参数改造的原函数拷贝
  • 当我们只想改变this指向,并且不想调用这个函数的时候,可以使用bind。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>改变函数内部this的指向之bind方法</title>
</head>

<body>
    <button>按钮</button>

    <script>
        // 改变函数内部的this指向,js提供了call()、apply()和bind()方法
        // bind()方法不会调用函数,但是能改变函数内部this指向。
        var o = {
            name: '许大仙'
        }

        function fn() {
            console.log(this);
        }

        var f = fn.bind(o);
        f();
        // 不会调用原来的函数,可以改变原来函数内部的this指向
        // 返回的是原函数改变this产生之后的新函数
        // 如果有的函数,我们不需要立即调用,又想改变这个函数内部的this指向,此时用bind方法最合适。

        //有一个按钮,当我们点击之后,就禁用这个按钮,3秒钟之后开启这个按钮
        var btn = document.querySelector('button');
        btn.onclick = function () {
            this.disabled = true; //指向的是btn这个按钮
            var num = 3;
            var timer = setInterval(function () {
                if (num == 0) {
                    clearInterval(timer);
                    this.disabled = false;
                } else {
                    num--;
                }
                console.log(num)
            }.bind(this), 1000);
        }

    </script>
</body>

</html>

2.2.5 call()、apply()、bind()总结

  • 相同点:都可以改变函数内部的this指向。
  • 区别点:
    • call和apply会调用函数,并且会改变函数内部的this指向。
    • call和apply传递的参数不一样,call传递的参数是arg1,arg2,…,apply传递的参数是数组形式[args]。
    • bind不会调用函数,只会改变函数内部的this指向。
  • 主要应用场景:
    • call经常用来做继承。
    • apply经常和数组有关。比如:借助于数学对象Math实现数组的最大值和最小值。
    • bind不调用函数,但是可以改变this指向。比如:改变定时器内部的this指向。

第三章:严格模式

3.1 什么是严格模式

  • JavaScript除了提供正常模式外,还提供了严格模式(strict mode)。ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行JS代码。
  • 严格模式在IE10以上版本的浏览器中才会被支持,旧版本的浏览器中将被忽略。
  • 严格模式对正常的JavaScript语义做了一些更改:
    • 消除了JavaScript语法的一些不合理、不严谨之处,减少了一些怪异行为。
    • 消除代码运行的一些不安全之处,保证代码运行的安全。
    • 提高编译效率,增加运行速度。
    • 禁用了在ECMAScript在未来版本中可能会定义的一些语法,为未来新版本的JavaScript做好铺垫。比如:一些保留字如:class、enum、export、extends、import、super不能做变量名。

3.2 开启严格模式

  • 严格模式可以应用到整个脚本个别函数中,因此在使用的时候,我们可以将严格模式分为为脚本开启严格模式为函数开启严格模式两种情况。
  • 为脚本开启严格模式:

    • 为整个脚本开启严格模式,需要在所有语句之前放一个特殊语句“use strict;”(或‘use strict;’)

      <script>
        "use strict";
        console.log("这是严格模式。");
      </script>
      
    • 因为"use strict"加了引号,所以老版本的浏览器会将它当做一行普通的字符串而忽略。

    • 有的script脚本是严格模式,有的script脚本是正常模式,这样不利于文件的合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中,这样独立创建一个作用域而不影响其他script脚本文件。
      <script>
      (function (){
          "use strict";
        var num = 10;
          function fn() {}
         })();
      </script>
      
  • 为函数开启严格模式:

    • 要给某个函数开启严格模式,需要将“use strict;”(或‘use strict;’)声明放在函数体所有语句之前

      function fn(){
        "use strict";
        return "这是严格模式。";
      }
      
    • "use strict"放到函数体的第一行,则整个函数以“严格模式”运行。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>开启严格模式</title>
</head>

<body>
    <script>
        /* 开启严格模式,下面的JS代码就会按照严格模式执行 */
        "use strict";
        a = 100; // 在严格模式下会报错
        console.log('这是严格模式');
    </script>
    <script>
        (function () {
            //在立即执行函数中开启严格模式
            "use strict";
        })()
    </script>
    <script>
        function fn() {
            //在函数中开启要严格模式
            "use strict";
            console.log('这是严格模式');
        }
    </script>
</body>

</html>

3.3 严格模式的变化

3.3.1 变量规定

  • ①在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种写法,变量必须先用var声明,然后再使用。
  • ②严禁删除已经声明的变量。比如:delete x;语法是错误的。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>严格模式的变化之变量规定</title>
</head>

<body>
    <script>
        "use strict";
        /* 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种写法,变量必须先用var声明,然后再使用。 */
        // a = 100;
        // console.log(a);
        /* 严禁删除已经声明的变量。比如:delete x;语法是错误的。 */
        // var a = 100;
        // delete a;
    </script>


</body>

</html>

3.3.2 this指向问题

  • ①以前在正常模式中全局作用函数中的this指向的是window对象;严格模式下全局作用域中函数中的this是undefined
  • ②以前在正常模式中构造函数不加new也是可以调用的,当做普通函数,this指向全局对象;严格模式下,如果构造函数不加new调用,this指向的是undefined,如果给其赋值,则会报错。
  • ③正常模式和严格模式下,构造函数如果使用new调用,则this指向创建的对象实例。
  • ④正常模式和严格模型下的定时器的this都是指向window。
  • ⑤正常模式和严格模式下的事件、对象还是指向调用者。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>严格模式的变化之this指向问题</title>
</head>

<body>
    <script>
        "use strict";

        /* 以前在正常模式中全局作用函数中的this指向的是window对象,严格模式下全局作用域中函数中的this是undefined。 */
        /*
            function fn() {
                console.log(this);
            }

            fn(); //undefined
        */

        /* 以前在正常模式中构造函数不加new也是可以调用的,当做普通函数,this指向全局对象;严格模式下,如果构造函数不加new调用,this指向的是undefined,如果给其赋值,则会报错。 */
        /*
        function Star() {
            this.sex = '男';
            console.log(this);
        }
        Star();
        */

        /* 正常模式和严格模式下,构造函数如果使用new调用,则this指向创建的对象实例。 */
        function Star() {
            this.sex = '男';
            console.log(this);
        }
        var str = new Star();

    </script>
</body>

</html>

3.3.3 函数变化

  • ①严格模式下,函数不能有重名的参数。
  • ②严格模式下,函数必须声明在顶层,新版本的JavaScript会引入“块级作用域”(ES6中已经引入)。为了和新版本接轨,不允许在非函数的代码块内声明函数。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>严格模式的变化之函数变化</title>
</head>

<body>
    <script>
        "use strict";

        /* ①严格模式下,函数不能有重名的参数。 */
        /*
            以下函数会报错。

            function fn(a, a) {

            }
        */

        /* ②严格模式下,函数必须声明在顶层,新版本的JavaScript会引入“块级作用域”(ES6中已经引入)。为了和新版本接轨,不允许在非函数的代码块内声明函数。 */
        /*
            以下函数会报错。
            if (true) {
                function f() {

                }
                f();
            }

            for (let i = 0; i <5 ; i++) {
                function f() {

                }
                f();
            }
        */



    </script>
</body>

</html>

第四章:高阶函数

  • 高阶函数是对其他函数进行操作的函数,它接受函数作为参数将函数作为返回值输出
  • 函数也是一种数据类型,同样可以作为参数,传递给另一个参数使用。最典型的就是回调函数。
  • 同理,函数也可以作为返回值传递回来。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>高阶函数</title>
</head>

<body>

    <script>
        /* 高阶函数:接受函数作为函数或将函数作为返回值输出 */
        /* 接受函数作为函数 */
        function fn(callback) {
            callback && callback();
        }

        fn(function () {
            alert('你大爷的');
        });

        /* 将函数作为返回值输出 */
        function fn2() {
            return function () {
                alert('呵呵');
            }
        }

        fn2();
    </script>
</body>

</html>

第五章:闭包

5.1 变量作用域

  • 变量根据作用域不同分为两种:全局变量和局部变量。
  • ①函数内部可以使用全局变量。
  • ②函数外部不可以使用局部变量。
  • ③当函数执行完毕,本作用域内的局部变量就被销毁。

5.2 闭包

  • 闭包(closure)是指有权访问另一个函数作用域中变量函数。简单理解就是,一个作用域可以访问另外一个函数内部的局部变量,这个局部变量所在的函数就称为闭包函数。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>闭包</title>
</head>

<body>

    <script>
        //闭包是指有权访问另一个函数作用域中变量的函数。
        //闭包:fn2这个函数作用域访问了另一个函数fn里面的局部变量num,那么fn就称为闭包函数。
        function fn() {
            var num = 10;

            function fn2() {
                console.log(num); // 10
            }

            fn2();
        }

        fn();
    </script>
</body>

</html>

5.3 闭包的作用

  • 闭包的作用:延伸了变量的作用范围。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>闭包的作用</title>
</head>

<body>
    <script>
        //闭包是指有权访问另一个函数作用域中变量的函数。
        //闭包:fn2这个函数作用域访问了另一个函数fn里面的局部变量num,那么fn就称为闭包函数。
        //fn外面的作用域可以访问fn内部的局部变量。
        //闭包的主要作用:延伸了变量的作用范围。
        function fn() {
            var num = 10;

            // function fn2() {
            //     console.log(num); // 10
            // }
            //
            // return fn2;
            return function () {
                console.log(num);
            }
        }

        var f = fn();
        f();
        /*
        * 类似于
        *       var f = function fn2(){
        *           console.log(num);
        *       }
        */


    </script>
</body>

</html>

5.4 闭包案例

  • 示例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>闭包应用之循环注册点击事件</title>
</head>

<body>
    <ul class="nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>鲱鱼罐头</li>
        <li>大猪蹄子</li>
    </ul>

    <script>
        //点击li输出当前li的索引号
        //1. 利用动态添加属性的方式
        var lis = document.querySelector('.nav').querySelectorAll('li');
        /*lis.forEach(function (value, index) {
            value.setAttribute('data-index', index);
            value.onclick = function () {
                var index = this.getAttribute('data-index');
                console.log(index);
            }
        })*/
        //2. 利用闭包的方式得到当前小li的索引号
        lis.forEach(function (value, index) {
            //利用for循环创建了4个立即执行函数
            //立即执行函数也称为小闭包,因为立即函数里面的任何一个函数都使用使用index变量。
            (function (index) {
                value.onclick = function () {
                    console.log(index);
                }
            })(index);
        })
    </script>
</body>

</html>
  • 示例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>闭包应用之定时器中的闭包</title>
</head>

<body>
    <ul class="nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>鲱鱼罐头</li>
        <li>大猪蹄子</li>
    </ul>

    <script>
        // 3秒后,打印所有的li元素的内容
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            (function (i) {
                setTimeout(function () {
                    console.log(lis[i].innerHTML);
                }, 3000);
            })(i);
        }
    </script>
</body>

</html>
  • 示例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>闭包应用之打车价格</title>
</head>

<body>
    <script>
        // 闭包应用--计算打车价格
        // 打车起步价为13(3公里),之后,每多一公里,增加5元,用户输入公里数,可以计算打车价格。
        // 如果拥堵,总价格多收10元拥堵费。
        var car = (function () {
            var start = 13; //起步价
            var total = 0; //总价
            return {
                //正常的总价
                price: function (n) {
                    if (n <= 3) {
                        total = start;
                    } else {
                        total = (n - 3) * 5 + start;
                    }
                    return total;
                },
                //拥堵之后的费用
                yd: function (flag) {
                    return flag ? total += 10 : total;
                }
            }
        })();

        var total = car.price(1);
        console.log(total);
        total = car.yd(true);
        console.log(total);
    </script>
</body>

</html>

第六章:递归

6.1 什么是递归?

  • 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解,函数内部自己调用自己,这个函数就是递归函数。
  • 递归函数的作用和循环效果一样。
  • 由于递归很容易发生(栈溢出)错误,所以必须加退出条件return

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>什么是递归函数</title>
</head>

<body>
    <script>
        // 递归函数:函数内部自己调用自己,这个函数就是递归函数
        var num = 1;

        function fn() {
            console.log('我要打印6句话')
            if (num == 6) {
                return; //递归里面必须加退出条件
            }
            num++;
            fn();
        }

        fn();

    </script>
</body>

</html>

6.2 递归案例

  • 示例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>利用递归求阶乘</title>
</head>

<body>
    <script>
        /* 求 1 * 2 *3 ... * n   阶乘 */
        function fn(n) {
            if (n == 1) {
                return 1;
            }
            return n * fn(n - 1);
        }

        var num = fn(3);
        console.log(num);
    </script>
</body>

</html>
  • 示例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>利用递归求斐波那契数列</title>
</head>

<body>
    <script>
        /* 利用递归函数求斐波拉契数列(兔子序列)1 1 2 3 5 8 13 21 */
        /* 用户输入一个数字n,就可以求出这个数字对应的兔子序列了。 */
        /* 我们只需要知道用户输入的n的前面两项(n-1)和 (n-2)就可以计算出n对应的序列值 */
        function fn(n) {
            if (n == 1 || n == 2) {
                return 1;
            }
            return fn(n - 1) + fn(n - 2);
        }

        console.log(fn(4));;
    </script>
</body>

</html>
  • 示例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>利用递归遍历数据</title>
</head>

<body>
    <script>
        var data = [{
            id: 1,
            name: '家电',
            goods: [{
                id: 11,
                name: '冰箱'
            }, {
                id: 12,
                name: '洗衣机'
            }]
        }, {
            id: 2,
            name: '服饰'
        }]

        /* 输入id号,就可以返回对应的数据对象 */
        function fn(id, data) {
            var o = null;
            data.forEach(function (item, index) {
                if (item.id == id) {
                    o = item;
                } else if (item.goods && item.goods.length != 0) { //如果里层有goods数组,并且数组的长度不为0
                    o = fn(id, item.goods);
                }
            });
            return o;
        }

        console.log(fn(1, data));;
        console.log(fn(2, data));;
        console.log(fn(11, data));
        console.log(fn(12, data));
        console.log(fn(13, data));
    </script>
</body>

</html>

6.3 深拷贝和浅拷贝

  • 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用。
  • 深拷贝拷贝多层,每一级别的数据都会拷贝。

  • 示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>深拷贝和浅拷贝</title>
</head>

<body>
    <script>
        /*
        * 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用。
        */
        var obj = {
            id: 1,
            name: '许大仙',
            msg: {
                age: 18
            },
            color: ['red', 'green', 'blue']
        }

        var o = {}
        for (var key in obj) {
            o[key] = obj[key];
        }

        console.log(o);
        o.msg.age = 20;
        console.log(obj.msg.age); //20

        /*
            ES6新增方法:进行浅拷贝 Object.assign(target, ...sources)
        */
        var o1 = {};
        Object.assign(o1, obj);
        console.log(o1);
        /*
        * 深拷贝拷贝多层,每一级别的数据都会拷贝。
        */
        var o2 = {}

        function deepCopy(newObj, oldObj) {
            for (var key in oldObj) {
                //判断数据类型是否是简单类型还是复杂类型
                //1. 获取属性值
                var item = oldObj[key];
                //2. 判断属性值是否是数组
                if (item instanceof Array) {
                    newObj[key] = [];
                    deepCopy(newObj[key], item);
                } else if (item instanceof Object) { //3. 判断属性值是否是对象
                    newObj[key] = {};
                    deepCopy(newObj[key], item);
                } else { //4. 属于简单数据类型
                    newObj[key] = item;
                }
            }
        }

        deepCopy(o2, obj);
        console.log(o2);
    </script>
</body>

</html>