函数进阶和正则表达式

函数进阶

在这里插入图片描述
在这里插入图片描述

函数的定义方式

  1. 函数声明方式function关键字(命名函数)
    // 1 自定义函数
        function fn() {

        };
  1. 函数表达式(匿名函数)
  var fun = function() {

        };
  1. 利用new function(‘参数1’,‘参数2’,‘参数3’)
   // 3 利用new function('参数1','参数2','参数3')必须带引号
        var f = new Function('a', 'b', 'console.log(a+b)');
        f(1, 2); //3

注意:

  1. Function里面的参数必须是字符串格式
  2. 第三种方式执行效率低,也不方便书写,使用较少
  3. 所有函数都是Function的实例(对象)
  4. 函数也属于对象
    原型三角关系在这里插入图片描述
函数的调用方式
  1. 普通函数
 function fn() {
            console.log('人生巅峰');

        }
        fn();
        fn.call();
  1. 对象的方法
   var o = {
            sayhi: function() {
                console.log('人生的的巅峰');

            }
        }
        o.sayhi();
  1. 构造函数
  function Fn() {};
        new Fn();
  1. 绑定事件函数
btn.onclick = function() {};
  1. 定时器函数
  setTimeout(function() {}, 1000);
  1. 立即执行函数
  (function() {
            console.log(312);

        })();
函数内this的指向

在这里插入图片描述

改变函数内部的this指向

常用的有bind()、call()、apply()三种方法
1.call()方法
call()方法调用一个对象,简单理解为调用函数的方式,但是它可以改变函数的this指向,call可以调用函数 可以改变函数内的this指向 call的主要作用可以实现继承

  function Son(uname, age, sex) {
            // 把Father这个构造函数调用过来了 并且将父亲中构造函数的this指向Son
            Father.call(this, uname, age, sex);
        }

2 apply()方法
简单理解为调用函数的方式,但是可以改变函数的this指向
在这里插入图片描述

  1. 也是调用函数 第二个可以改变函数内部this的指向
  2. 但是它的第二个参数必须是数组(伪数组也可以
  3. apply的主要应用 比如说我们可以用apply借助于数学内置对象求最大值和最小值
 var a = [1, 23, 1324, 3454];
        //此处求最大值不用改变this的指向
        // var max = Math.max.apply(null, a);
        // 尽量不要上面那样写 最好指向Math 把this的指向指向Math
        var max = Math.max.apply(Math, a);
        var min = Math.min.apply(Math, a);
        console.log(max);
        console.log(min);

3 bind()方法
bind()方法,但是能改变函数内部this指向
在这里插入图片描述

  • bind()不会调用原来的函数,但是可以给它传送给一个变量从而进行调用
  • 可以改变原来函数内部this的指向 返回的是原函数改变之后this之后产生的新函数
  • 如果有的函数不需要立即调用但是又想改变这个函数内部的this指向此时用bind方法最合适
    我们有一些按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮
var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                this.disabled = true; //指向的是btn这个按钮
                setTimeout(function() {
                    this.disabled = false;//不能写成btn[i],for循环会执行下去 ,但是这里有延时所以i的下标会改变
                }.bind(this), 2000)
                //这个this指向的是btn[i]
            }
        }

延时函数钟this指向的是window,所以要改变this的指向但是又要延时调用所以需要用到bind让延时函数中this指向按钮

call、apply、bind的总结

相同点:
都可以改变函数内部this指向
不同点:

  1. call和apply会调用函数,并且改变函数内部this指向
  2. call和apply的传递参数不一样,call传递参数为普通形式,apply传递的必须是数组形式
  3. bing不会调用函数,可以改变函数内部this指向
    应用场景:

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

严格模式

ES5新增的严格模式,之前写的都是正常模式
在这里插入图片描述
在这里插入图片描述

严格模式中的变化

1.变量规定
变量名必须先声明再使用
不能随意删除定义好的变量
2.this的指向问题
在这里插入图片描述
3.函数变化
1.严格模式下函数里面的参数不允许有重名
2.不允许再非函数的代码块内声明函数 比如再if条件里面声明是不可以的

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        'use strict';
        // 1变量名必须先声明再使用
        // num = 10;
        // console.log(num);
        var num = 10;
        console.log(num);
        // delete num;
        //2 不能随意删除定义好的变量
        function fn() {
            console.log(this);
        }
        fn();
        //3 严格模式下全局函数的this指向的是window,此时this指向的是uundefineed,如果赋值就报错
        function Star() {
            this.sex = '男';
        }
        // Star();
        //4 严格模式下,如果构造函数不加new调用,this不再指向window 
        var ldh = new Star();
        //5 定时器里面的this指向window
        console.log(ldh.sex);
        setTimeout(function() {
                console.log(this);
            }, 2000)
            //6 严格模式下函数里面的参数不允许有重名
        function fn(a, a) {
            console.log(a + a);

        }
        fn(1, 2);
        // 7 不允许再非函数的代码块内声明函数 比如再if条件里面声明是不可以的
        var flag = true;
        if (flag) {
            function fn() {

            }
        }
    </script>
</body>

</html>
高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或者将函数作为返回值输出
在这里插入图片描述
此时fn为一个高阶函数,函数是一种数据类型,同样可以作为参数吗,传递给另外一参数使用,最典型的就是作为回调函数

闭包

1.变量作用域
在这里插入图片描述
2.闭包
有权访问另一个函数作用域中变量的函数
简单理解就是,一个作用域可以访问另一个函数内部的变量
闭包的作用:延伸了变量的作用范围
3.应用
实现点击小li输出索引号:

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul class="nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>罐头</li>
        <li>猪蹄</li>
    </ul>
    <script>
        //闭包应用-点击li输出索引号
        //1 我们可以利用动态添加属性
        // var lis = document.querySelector('.nav').querySelectorAll('li');
        // for (var i = 0; i < lis.length; i++) {
        //     lis[i].index = i;
        //     lis[i].onclick = function() {
        //         console.log(this.index);
        //     }
        // }
        //2 利用闭包的方式得到当前小li索引号
        for (var i = 0; i < lis.length; i++) {
            //通过立即执行函数利用for循环创建四个立即执行函数
            // 立即执行函数称为小闭包,因为立即执行函数里面的任何一个函数都可以使用它的i这个变量
            (function(i) {
                // console.log(index);
                lis[i].onclick = function() {
                    console.log(i);
                }
            })(i);
            // 把i作为实参传递给index
        }
    </script>
</body>

</html>

实现点击小li输出索引号,利用的是每执行一次循环然后创建一个立即执行函数,立即执行函数也称为小闭包,里面的函数都能使用i这个变量,但是有个问题要注意的是内存泄漏。用动态创建的话直接创建点击事件,因为function这个函数是异步任务,执行完for循环这个同步任务在执行所以就会打印4

打印所有小li:

var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            // 立即执行函数里面的任何函数都可以使用i这个变量
            (function(i) {
                setTimeout(function() {
                    console.log(lis[i].innerHTML);
                }, 3000)
            })(i);
            // 三秒之后先打印第0个,在循环第1个
        }

易错案例分析:

<!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()()); //The Window
        var f = object.getNameFunc();
        // 类似于
        var f = function() {
            return this.name;
        }
        f();
        // 相当于匿名函数
        // function(){this}() 立即执行函数this指向的是window

        // 思考题 2:

        var name = "The Window";  
        var object = {    
            name: "My Object",
            getNameFunc: function() {
                var that = this; //this指向函数调用者Object
                return function() {
                    return that.name;
                    // 有闭包的产生,因为that使用了getNameFunc这个函数的局部变量,变量所在的函数getNameFunc就是闭包函数
                };
            }
        };
        console.log(object.getNameFunc()()); //My Object
        var f = object.getNameFunc();
        // 后面的小括号相当于调用
        var f = function() {
            return that.name;
        };
        f();
    </script>
</body>

</html>

闭包是什么
闭包是一个函数(一个作用域可以访问另外一个函数的局部变量)
闭包的作用
延伸变量的作用域范围

递归

什么是递归?
如果一个函数内部可以调用其本身,那么这个函数称为递归函数,通俗的说,函数内部自己调用自己,递归函数的作用和循环效果一样,由于递归很容易发生栈溢出,所以必须加一个退出条件return
利用递归输出id号对应的数据:

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        var data = [{
            id: 1,
            name: '家电',
            goods: [{
                id: 11,
                gname: '冰箱'
            }, {
                id: 12,
                gname: '洗衣机'
            }]
        }, {
            id: 2,
            name: '服饰'
        }];
        //我们想要输入id号,就可以返回数据对象
        function getID(json, id) {
            var o = {};
            //保存筛选完之后的数据
            json.forEach(function(item) {
                // console.log(item); //2个数组元素
                if (item.id == id) {
                    o = item;
                    // 这个中间值返回给getID,否则getID没有返回值
                    return item;
                    //2 我们想要得到里层的数据11-12 可以利用递归函数
                    //里面有goods这个数组并且数组的长度不为0
                } else if (item.goods && item.goods.length > 0) {
                    o = getID(item.goods, id);

                }
            });
            return o;
        }
        console.log(getID(data, 1));
        console.log(getID(dat 11));
    </script>
</body>

</html>

一定要注意return item,返回一个中间结果给下面的getID函数

浅拷贝和深拷贝

浅拷贝:
1.浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用
2.深拷贝拷贝多层,每一级别的数据都会拷贝
3.Object.assign(target,…source)es6新增的方法可以实现浅拷贝
在这里插入图片描述

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 浅拷贝知识拷贝一层,更深层次对象级别的只拷贝引用
        // 深拷贝拷贝多层,每一级别的数据都会拷贝
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            }
        };
        // 浅拷贝遇到对象级别更深层次的拷贝的话,只是把地址拷贝给了o,指向同一个数据,修改任意一个都会修改另外一个
        var o = {};
        // for (var k in obj) {
        //     //k是属性名 obj[k]是属性值
        //     o[k] = obj[k];
        // }
        // console.log(o);
        // o.msg.age = 20;
        // // 修改之后都会修改
        // console.log(o);

        console.log('-------------------');
        Object.assign(o, obj);
        console.log(o);
        o.msg.age = 20;
        // 修改之后都会修改
        console.log(o);
        //把obj拷贝给o 不用for循环那么麻烦
    </script>
</body>

</html>

这个案例中是浅拷贝将obj拷贝给o,遇到对象更深层次的拷贝的话,只是把地址拷贝给了o,指向同一数据,修改任意一个数据都会导致改变
深拷贝:
将一个对象赋值给另一个对象时,如果遇到对象对其进行深层拷贝的时候,先将其中属性值赋值一份给新对象,然后再对其进行递归
在这里插入图片描述

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //深拷贝拷贝多层,每一层的数据都会拷贝
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            },
            color: ['blue', 'pink']
        };
        var o = {};
        //封装函数
        function deepcopy(newobj, oldobj) {
            for (var k in oldobj) {
                //判断属性值属于哪种数据类型
                //1 获取属性值 oldobj[k]
                var item = oldobj[k];
                //2 判断这个值是不是数组 要先判断是不是数组不能先判断是不是对象 因为数组也是对象 这样才能避免吧数组当成对象
                if (item instanceof Array) {
                    newobj[k] = [];
                    deepcopy(newobj[k], item);
                } else if (item instanceof Object) {
                    newobj[k] = {};
                    deepcopy(newobj[k], item);
                } else {
                    newobj[k] = item;
                }
                //3 判断这个值是否是对象
                // 4 如果都不是就是简单数据类型
            }
        }
        deepcopy(o, obj);
        console.log(o);
        var arr = [];
        console.log(arr instanceof Object); //true
        o.msg.age = 20;
        //修改值后对原来的obj没有影响,只是修改了现在o,深拷贝是将msg里面的属性和方法复制了一份
        console.log(o);
        console.log(obj);
        obj.msg.age = 123;
        console.log(obj);
    </script>
</body>

</html>

正则表达式

在这里插入图片描述

正则表达式概述

是用于匹配字符串中字符组合的模式,在js中,正则表达式也是对象
作用:
1.匹配
2.替换
3.提取
在这里插入图片描述
特点:

  1. 灵活性、逻辑性和功能性非常强
  2. 可以迅速地用极简单的方式达到字符串的复杂控制
  3. 实际开发中一般直接复制写好的正则表达式,但是要求会使用正则表达式并且根据实际情况修改正则表达式
正则表达式在js中的应用

创建正则表达式:
在这里插入图片描述
测试正则表达式:
在这里插入图片描述

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //正则表达式在js中的使用
        //1 利用RegExp对象来创建正则表达式
        var regexp = new RegExp(/123/);
        console.log(regexp);
        //2 利用字面量创建正则表达式
        var rg = /123/;
        //3 test用于检测字符串是否符合正则表达式要求的规范
        console.log(rg.test('fgh'));
        console.log(rg.test(1234));
    </script>

</body>

</html>

正则表达式的组成:
边界符:
在这里插入图片描述
在这里插入图片描述
设定次数

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //量词是设定某个模式出现的次数
        var reg = /^[a-z0-9]{6,16}$/;
        //前面出现的模式可以出现6-16次
        console.log(reg.test('dfghefrf67'));
        console.log(reg.test('ghjg'));
    </script>
</body>

</html>

用户名验证:在这里插入图片描述
在这里插入图片描述

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        span {
            color: #ccc;
            font-size: 14px;
        }
        
        .right {
            color: green;
        }
        
        .error {
            color: red;
        }
    </style>
</head>

<body>
    <input type="text" class="uname"><span>请输入用户名</span>
    <script>
        //量词是设定某个模式出现的次数
        var reg = /^[a-z0-9]{6,16}$/;
        //前面出现的模式可以出现6-16次
        // console.log(reg.test('dfghefrf67'));
        // console.log(reg.test('ghjg'));
        var span = document.querySelector('span');
        var uname = document.querySelector('.uname');
        uname.addEventListener('blur', function() {
            if (reg.test(this.value)) {
                console.log('正确');
                span.innerHTML = '用户名输入正确';
                span.className = 'right';
            } else {
                console.log('错误');
                span.innerHTML = '用户名输入错误';
                span.className = 'error';
            }
        })
    </script>
</body>

</html>

括号总结:

  1. 中括号表示字符匹配,匹配方括号中的任意一个字符
  2. 方括号是量词符,表示重复次数
  3. 小括号表示优先级
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        //中括号 字符集合,匹配方括号中的任意字符
        var reg=/[asd]/;//a||s||d
        //大括号 量词符 里面表示重复次数
        var reg=/^[das]{3,6}$/;
        var reg=/^abc{3}$/;//只是把c重复了三次
        //小括号 表示优先级
var reg=/^(abc){3}$/;//abc重复三次
    </script>
</body>
</html>

预定义类
在这里插入图片描述
座机号验证

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //座机号验证: 全国座机号码 两种格式 010-12345678 或者0530-1234567
        //正则里面的或者是一个|
        // var reg = /^\d{3}-\d{8}$/ | /^\d{4}-\d{7}$/;
        var reg = /^\d{3,4}-\d{7,8}$/;
        console.log(reg.test('010-12345678'));
    </script>
</body>

</html>

品优购中的reg.js

window.onload = function() {
    // var reg=/^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/;正则表达式测试给出的13几/14几/15几/18几
    var regtel = /^1[3|4|5|7|8]\d{9}$/; //手机号的正则表达式
    var regqq = /^[1-9]\d{4,}$/; //1-9第一位,后面的位数增加
    var regnc = /^[\u4e00-\u9fa5]{2,8}$/;//昵称验证
    var regmsg = /^\d{6}$/;//验证码
    var regpwd = /^[a-zA-Z0-9_-]{6,16}$/;//密码验证
    var tel = document.querySelector('#tel');
    var qq = document.querySelector('#qq');
    var nc = document.querySelector('#nc');
    var msg = document.querySelector('#msg');
    var pwd = document.querySelector('#pwd');
    var surepwd = document.querySelector('#surepwd');
    regexp(tel, regtel);
    regexp(qq, regqq);
    regexp(nc, regnc);
    regexp(msg, regmsg);
    regexp(pwd, regpwd);

    function regexp(ele, reg) {
        //ele表示哪个元素
        ele.onblur = function() {
            if (reg.test(this.value)) {
                console.log('正确');
                //把下一个兄弟span的类名改成success
                this.nextElementSibling.className = 'success';
                this.nextElementSibling.innerHTML = '<i class="success_icon"></i>恭喜您输入正确';
            } else {
                console.log('不正确');
                this.nextElementSibling.className = 'error';
                this.nextElementSibling.innerHTML = '<i class="error_icon"></i>格式不正确,请重新输入';
            }
        }
    }
    surepwd.onblur = function() {
        if (this.value == pwd.value) {
            this.nextElementSibling.className = 'success';
            this.nextElementSibling.innerHTML = '<i class="success_icon"></i>恭喜您输入正确';
        } else {
            this.nextElementSibling.className = 'error';
            this.nextElementSibling.innerHTML = '<i class="error_icon"></i>两次密码输入不一致';
        }
    }
}

替换
第一个参数可以是正则表达式
在这里插入图片描述

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <textarea name="" id="msg" cols="30" rows="10"></textarea><button>提交</button>
    <div></div>
    <script>
        //替换replace
        var str = 'andy和red';
        var newstr = str.replace('andy', 'gjhgjg');
        console.log(newstr);
        var text = document.querySelector('textarea');
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.onclick = function() {
            div.innerHTML = text.value.replace(/激情/g, '**');
        }
    </script>
</body>

</html>

如果有更多的敏感词可以在激情后面加|

发布了17 篇原创文章 · 获赞 2 · 访问量 1667

猜你喜欢

转载自blog.csdn.net/weixin_43514149/article/details/105332390