javascript高级(3)

创建函数的三种样式

1.function声明的普通函数(命名函数)
2.函数表达式(匿名函数)
3.new Function()

var fn =new Function('参数1','参数2',...,'函数体);

  • Function()里面的参数都是字符串格式
  • 第三种方式执行效率比较低,也不方便书写,因此较少使用
  • 所有的函数都是Fnction的实例对象
  • 函数也属于对象
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函数的定义和调用方式</title>
</head>
<body>
<script>
    // 自定义方式
 function fn() {
    }
    // 函数表达式
 var fn = function() {
    }
    // 利用new Function('参数1','参数2','函数体')
 var f = new Function('a','b','return a+b')
    console.log(f(1,2));
    console.dir(f);
    //用来判断f是否是一个对象
 console.log(f instanceof Object)
</script>
</body>
</html>

image.png

不同函数的调用方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button>按钮</button>
</body>
<script>
    // 函数的调用方式
 // 1.普通函数的调用
 function fn() {
        console.log('hello word')
    }
    fn()
    // 2.对象类的函数调用
 var obj = {
        say: function () {
            return 'hello word'
 }
    }
    console.log(obj.say())
    // 3.构造函数的调用
 function Star(name, age) {
        this.name = name;
        this.age = age;
    }
    var star1 = new Star('尧子陌', '18');
    console.log(star1.name)
    //4.事件函数的调用
 var bth = document.querySelector('button');
    bth.addEventListener('click', function () {
        console.log('事件函数')
    })
    // 5.定时器函数
 setInterval(function () {
        console.log('定时器函数')
    }, 5000)
</script>
</html>

image

自执行函数

顾名思义:自己调用的函数
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自执行函数</title>
</head>
<body>
<script>
    (function () {
        alert('hello word')
    })()
</script>
</body>
</html>

image

this的指向问题

函数内部的this指向,当我们调用的时候才确定

注意:自执行的函数中this指向的是window
image.png

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>函数的this指向</title>
    </head>
    <body>
        <button>按钮</button>
    </body>
    <script>
        // 函数的调用方式

        // 1.普通函数的调用
        function fn() {
            console.log(this) //windows

        }

        fn()
        // 2.对象类的函数调用
        var obj = {
            say: function() {
                console.log(this) //指向o这个对象
            }
        }
        obj.say()

        // 3.构造函数的调用
        function Star(name, age) {
            console.log(this) //构造函数中的this指向的是它的实例化 star
            this.name = name;
            this.age = age;

        }

        var star1 = new Star('尧子陌', '18');
        //4.事件函数的调用
        var bth = document.querySelector('button');
        bth.addEventListener('click', function() {
            console.log(this) //事件函数中的this 指向的是它的调用者bth按钮
        })

        // 5.定时器函数
        setInterval(function() {
            console.log(this) //定时器中的this 指向的是windows
        }, 5000)
    </script>
</html>

image.png

call()方法

call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

fun.call(thisArg, arg1, arg2, ...)

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数
  • 返回值就是函数的返回值,因为它就是调用函数
  • 因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承


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

    <head>
        <meta charset="UTF-8">
        <title>call()</title>
    </head>

    <body>

    </body>
    <script>
        // 改变函数内部的this指向之call()方法
        var o = {
            name: "andy"
        };

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

        fn.call(o, 1, 2) //call方法不仅可以改变函数内部的this指向,还可以传递参数


        //call()方法主要是为了实现继承


        function Father(name, age, sex) {
            console.log(this)
            this.name = name;
            this.age = age;
            this.sex = sex;

        }

        function Son(name, age, sex) {
            Father.call(this, name, age, sex)
        }

        var son = new Son('刘德华', '18', '男');
        console.log(son)
    </script>

</html>

 

image.png

### apply()方法

apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

fun.apply(thisArg, [argsArray])

  • thisArg:在fun函数运行时指定的 this 值
  • argsArray:传递的值,必须包含在数组里面
  • 返回值就是函数的返回值,因为它就是调用函数
  • 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值


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

<head>
    <meta charset="UTF-8">
    <title>改变函数内部的this指向之apply()</title>
</head>

<body>

</body>
<script>
    var o = {
        name: 'andy'

    }

    function fn(arr) {
        console.log(this); //此时的this指向的是0;
        console.log(arr)

    }

    fn.apply(o, ['pink']); //注意:第二个参数必须为数组

    // 可以用数组方法实现求数组的最大值
    var arr2 = [1, 66, 22, 55, 108];
    var max = Math.max.apply(Math, arr2);
    var min = Math.min.apply(Math, arr2);
    console.log(max, min)
</script>

</html>

image.png

bind()

bind() 方法不会调用函数。但是能改变函数内部this 指向

fun.bind(thisArg, arg1, arg2, ...)

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数
  • 返回由指定的 this 值和初始化参数改造的原函数拷贝
  • 因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind()
  
  <!DOCTYPE html>
  <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>改变函数的内部的this指向之bind()方法</title>

    <body>
        <button>按钮</button>
        <button>按钮</button>
        <button>按钮</button>
    </body>
    <script>
        // bind()方法是绑定 捆绑的意思
        var o = {
            name: "andy"
        }

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

        // 注意:bind()方法会改变函数内部的this,但不会调用原来的函数
        var f = fn.bind(o, 1, 2);
        f()

        //案例:当我们点击按钮之后,就禁用这个按钮,三秒之后再次开启这个按钮
        var bths = document.querySelectorAll('button');
        for (var i = 0; i < bths.length; i++) {
            bths[i].onclick = function() {
                this.disabled = true;
                setTimeout(function() {
                    this.disabled = false
                    // 这里的this指的是bth这个按钮
                }.bind(this), 2000)
            }
        }
    </script>
</html>

  

image

call() apply() bind()之间的区别

区别点:

  1. call 和 apply 会调用函数, 并且改变函数内部this指向.
  2. call 和 apply 传递的参数不一样, call 传递参数 aru1, aru2..形式 apply 必须数组形式[arg]
  3. bind 不会调用函数, 可以改变函数内部this指向.

主要应用场景:

  1. call 经常做继承.
  2. apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
  3. bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.

严格模式

严格模式的出现,是为了限制非标准严格模式下不标准的语法,并且为ES6做好铺垫
严格模式对正常的 JavaScript 语义做了一些更改:
  1. 消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
  2. 消除代码运行的一些不安全之处,保证代码运行的安全。
  3. 提高编译器效率,增加运行速度。
  4. 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比

如一些保留字如:class, enum, export, extends, import, super 不能做变量名

开启严格模式

'use strict' 在script的脚本内放入或在函数内部放入,便等同于开启严格模式

<!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>Document</title>
</head>
<body>
  <script type="text/javascript">
      // 为脚本开启严格模式
        'use strict'
  </script>
  
  <script type="text/javascript">
        function fn(){
            // 为函数开启严格模式
            'use strict'
        }
  </script>
</body>
</html>


严格模式的变化

  • 严格模式下变量必须先声明才能使用
  • 严格模式下普通函数中的this指向undefined
  • 严格模式下构造函数不加new调用,this指的是undefined
  • 严格模式下定时器中的this指向window
<!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>严格模式的变化</title>
   </head>
   <body>
       <script type="text/javascript">
           'use strict'
           // 1.变量必须先声明再使用
           var num = 10;
           console.log(10);
           // delete  num; 严格模式下禁用删除变量

           // 2.严格模式下的this指向undefined
           function fn() {
               console.log(this) //严格模式下this指的是undefined
           }
           fn()
           // 3.严格模式下构造函数不加new调用,this指的是undefined
           function Star() {
               console.log(this)
           }
           Star()

           //4.严格模式下定时器的this指向window
           setTimeout(function() {
               console.log(this)
           }, 2000)
       </script>
   </body>
</html>


image.png

高阶函数

高阶函数是对其它函数进行操作的函数,它接受函数作为参数或者将函数作为返回值输出


<!DOCTYPE html>
<html>
   <head>
       <meta charset="utf-8">
       <title>高阶函数</title>
   </head>
   <body>

   </body>
   <script>
       // 将函数作为参数
       function fn(callback) {
           callback && callback()
       }
       fn(function() {
           alert('hello word')
       })


       //将函数作为返回值输出
       function fn2() {
           return function() {}
       }
       console.log(fn2())
   </script>
</html>

image
此时的fn()及fn2便是高价函数

函数也是一种数据类型,可作为参数传递给另外一个函数作为参数使用,最典型的便是回调函数

 
<!DOCTYPE html>
<html>
   <head>
       <meta charset="utf-8">
       <title>高阶函数</title>
       <style>
           * {
               margin: 0;
               padding: 0;
           }
           .box {
               position: absolute;
               top:0;
               left: 0;
               width: 200px;
               height: 200px;
               background-color: blueviolet
           }
       </style>
   </head>
   <script src="jquery.min.js"></script>
   <body>
       <div class="box"></div>
       
   </body>
   <script>
       $('.box').animate({
           left:200,
           top:200
       },function(){
           $(this).css('background','red')
       })
   </script>
</html>

image

闭包

指有权访问另外一个函数作用域里面的变量的函数

作用:延伸变量的作用范围

<!DOCTYPE html>
<html lang="zh">
    <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>闭包</title>
    </head>
    <body>
        <div></div>
    </body>
    <script>
        // 所谓的闭包 简单理解:就是有权访问另一个函数的局部变量
        function fn() {
            let num = 10;

            function fun() {
                console.log(num) //10
            }

            fun()
        }

        fn()
    </script>
</html>

image.png

image.png

chrome中调试闭包

  1. 打开浏览器,按 F12 键启动 chrome 调试工具。
  2. 设置断点。
  3. 找到 Scope 选项(Scope 作用域的意思)。
  4. 当我们重新刷新页面,会进入断点调试,Scope 里面会有两个参数(global 全局作用域、local 局部作用域)。
  5. Closure 参数代表闭包函数

闭包函数的作用

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>闭包的作用</title>
    </head>
    <body>

    </body>
    <script>
        // 有权访问另一个函数的局部变量的函数 便叫做局部变量
        function fn() {
            var num = 10;
            return function() {
                console.log(num) //10
            }

        }

        var f = fn(); //fn()
        f(); //调用return后面的函数
    </script>
</html>

image.png

闭包之输出li当前所造的索引号

核心思路:利用立刻执行函数便可

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>闭包之点击i输出索引号</title>
    </head>
    <body>
        <ul class="nav">
            <li>one</li>
            <li>two</li>
            <li>three</li>
            <li>four</li>
        </ul>
    </body>
    <script>
        // 第一种方法:点击i输出当前i的索引号
        var lis = document.querySelector('.nav').querySelectorAll('li');

        for (var i = 0; i < lis.length; i++) {
            lis[i].index = i; //为每次循环的li设置索引号

            lis[i].addEventListener('click', function() {
                console.log(this.index)
            })
        }

        // 第二种方法:利用闭包输出当前的索引号

        for (var i = 0; i < lis.length; i++) {
            // 利用for循环创建四个立刻执行函数,因为立刻执行函数里面的原函数可以使用它的变量

            (function(i) {
                lis[i].addEventListener('click', function() {
                    console.log(i)
                })
            }(i))

        }
    </script>
</html>

image

闭包之定时器

要求:三秒之后自动显示li里面的内容

核心思路:利用for循环的每次循环生成的闭包函数即可


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>闭包之定时器</title>
    </head>
    <body>
        <ul class="nav">
            <li>one</li>
            <li>two</li>
            <li>three</li>
            <li>four</li>
        </ul>
    </body>
    <script>
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {

            (function(i) {
                setTimeout(function() {
                    console.log(lis[i].innerHTML)
                }, 5000)
            }(i))
        }
    </script>


</html>


image

闭包之打车价格

打车起步价13(3公里内), 之后每多一公里增加 5块钱. 用户输入公里数就可以计算打车价格

如果有拥堵情况,总价格多收取10块钱拥堵费

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>闭包之打车价格</title>
    </head>
    <body>
    </body>
    <script>
        let car = (function() {
            let star = 13; //打车起步价
            let total = 0; //总价 局部变量
            return {
                price: function(n) {
                    if (n <= 3) {
                        total = star
                    } else {
                        total = star + (n - 3) * 5
                    }
                    return total //注意:不要忘记将值返回
                },
                // 拥堵之后的费用
                yd: function(flag) {
                    return flag ? total + 10 : total;
                }

            }

        })();
        console.log(car.price(5));
        console.log(car.yd(true)) //参数为true 则说明堵塞加10元


        console.log(car.price(6));
        console.log(car.yd(false))
    </script>
</html>
                                                    

image.png

测试题

<!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>Document</title>
</head>

<body>
<script>
    // 思考题 1:

    var name = "The Window";
    var object = {
        name: "My Object",
        getNameFunc: function () {
            return function () {
                return this.name;
            };
        }
    };

    console.log(object.getNameFunc()())
    var f = object.getNameFunc();

    var f = function () {
        return this.name;
    }
    f();


    // 思考题 2:

    var name = "The Window";
    var object = {
        name: "My Object",
        getNameFunc: function () {
            var that = this;
            return function () {
                return that.name;
            };
        }
    };
    console.log(object.getNameFunc()())
</script>
</body>

</html>

image.png

递归函数

如果一个函数在内部可以调用其本身,这个函数就是递归函数

简单理解:函数内部自己调用自己

递归函数的作用和循环效果一样,很容易发生‘栈溢出’,所以必须要加退出条件return

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<script>
    var num = 1;

    function fn() {
        console.log('hello word')
        if (num == 6) {
            return //递归函数必须要有退出条件
        }
        num++
        fn()
    }

    fn()
</script>
</html>

image.png

利用递归求1~n的阶乘

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>利用递归求1-n的阶乘</title>
</head>
<body>

</body>
<script>
    function fn(n) {
        if(n==1){
            return 1
        }
        return  n *fn(n-1)

    }

    console.log(fn(3));
    // 假入用户输入的是3
   /* return 3*fn(2)
    return 3*fn(2*fn(1))
    return 3*(2*1)
    return 3*2
    return 6 */
</script>
</html>

image.png

利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21...


<!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>Document</title>
</head>

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

  
</script>
</body>

</html>

image.png

阅读 100

推荐阅读