JavaScript高级,ES6 笔记 第四天(防抖、节流、深拷贝,事件总线)_es6防抖_独憩的博客-程序员宅基地

技术标签: 前端  es6  javascript  

目录

throw

try  catch  finally

浅拷贝

深拷贝

 1、递归方式

2.loadsh

3.JSON(推荐)

call

apply

bind

 节流

 防抖

视频案例

事件总线


throw

抛出自定义错误

<!DOCTYPE html>
<html>
<body>

<p>Please input a number between 5 and 10:</p>

<input id="demo" type="text">
<button type="button" onclick="myFunction()">Test Input</button>
<p id="message"></p>

<script>
function myFunction() {
    var message, x;
    message = document.getElementById("message");
    message.innerHTML = "";
    x = document.getElementById("demo").value;
    try {
        if(x == "") throw "is Empty";
        if(isNaN(x)) throw "not a number";
        if(x > 10) throw "too high";
        if(x < 5) throw "too low";
    }
    catch(err) {
        message.innerHTML = "Input " + err;
    }
}
</script>

</body>
</html>

try  catch  finally

try里面放可能会出错误的代码    catch是当发生错误时执行的,且有参数,代表错误本身,finally表示不管有没有错误都会执行

<body>
  <p>123</p>

  <script>
    try{
      let ss = document.querySelector('.p');//Cannot read properties of null (reading 'style')
      ss.style.color = 'red'
    }catch(err){
      console.log(err.message);

    }finally{
      alert('sdasda')
    }

    
  
  </script>
  
</body>

浅拷贝

浅拷贝与深拷贝都只针对  引用类型

浅拷贝拷贝的是值(其实也是地址)

    <script>
        const obj={
            age :18
        }

        let o = {...obj}
        let o1={}
        Object.assign(o1,obj)
        o1.age=20
        console.log(o1);//age =20
        console.log(obj);//age =18

    </script>
</body>

但是这样有问题:

        const obj={
            age :18,
            obj1:{
                sex:1

            }
        }

        let o = {...obj}
        let o1={}
        Object.assign(o1,obj)
        o1.obj1.sex = 2
        console.log(o1);
        console.log(obj);//sex都为2

欠拷贝只直接拷贝最外一层的值,而对于obj1来说,最外层的值是他的地址,所以就直接把地址给了o1,改变o1时也会改变obj

深拷贝

直接拷贝对象 不是地址

 1、递归方式

设置函数,当originValue里面还有引用数据类型时,再次调用自己,只是将originValue改为里面的引用数据类型

    <script>
    const obj = {
      uname: 'pink',
      age: 18,
      hobby: [{ name: 2222, age: 2222 }, '足球',new Set([123, 22, 333])],
      family: {
        baby: '小pink'
      },
      run: function () {
        console.log(1);
      },
      sete: new Set([123, 22, 333]),
      asas:Symbol('asd')
    }
    obj.self = obj

    function deepcopy1(originValue,map = new WeakMap()){
      if(typeof originValue === 'symobol'){
        return new Symbol(originValue.desciption)
      }

      if(!(originValue instanceof Object)){
        return originValue
      }
      if(typeof originValue === 'function'){//函数不需要深拷贝
        return originValue
      }

      if(originValue instanceof Set){
        const newSet = new Set()
        for(let item of originValue){
          newSet.add(deepcopy1(item,map))
        }
        return newSet
      }

      if(map.get(originValue)){//当我遍历到self,self需要我自身,也就是obj,这里直接把map中的obj拿给他
        return map.get(originValue)
      }

      let newobj = originValue instanceof Array?[]:{}  //如果originValue是单纯的对象或数组
      map.set(originValue,newobj)
      for(let key in originValue){
        newobj[key] = deepcopy1(originValue[key],map)
      }
      return newobj
    }
    let ss = deepcopy1(obj)
    console.log(ss);



      </script>

这里比较难的地方在于    obj.self = obj  ,也就是在obj中创造了self指向自己

在深拷贝时,传入map = new WeakMap()用于记录已经生成的应用对象,当拷贝到self时,这个时候又需要创造一个obj对象,这时可以将map中存储好的obj直接拿出来赋值给它

2.loadsh

先去下载库,并引入js

Lodash 简介 | Lodash 中文文档 | Lodash 中文网

调用cloneDeep()

        var oo = _.cloneDeep(obj)
        console.log(oo);

3.JSON(推荐)

JSON 语法 | 菜鸟教程

    <script>
        const obj = {
          uname: 'pink',
          age: 18,
          hobby: ['乒乓球', '足球'],
          family: {
            baby: '小pink'
          }
        }

        // JSON.stringify(obj)
        //将对象转换为JSON字符串
        let o =JSON.parse(JSON.stringify(obj))//将JSON字符串转换为对象



      </script>

但是JSON有个问题,如果 对象有  函数  或者  symbol  则无法解析

call

可以改变this指向,在调用的时候用,第一个值就是设置指向

        let obj={

        }

        function fn(x,y){
            console.log(this);
        }
        fn.call(obj,1,2)//obj

apply

也是改变this指向 ,类似,传参必须为数组,这个用的比较多

        let obj={
        }
        function fn(x,y){
            console.log(this);//obj
            console.log(x+y);//3
        }
        fn.apply(obj,[1,2])

值得注意的是,当不匹配时:

        let obj={

        }

        function fn(x){
            console.log(this);//obj
            console.log(x);//1
        }
        fn.apply(obj,[1,2])

不会将数组全部传入

提供了另外一种求最大值的方式

        let aa = [1,4,5,8,9]
        //Math.max(1,4,7,98)

        console.log(Math.max(...aa));
        console.log(Math.max.apply(null,aa));

bind

前面两种都必须要调用函数,这个不需要,返回值是函数,其实就是改变原函数的this得到了一个新函数

        let aa = [1,4,5,8,9]
        function fn(){
            console.log(this);
        }
        fn.bind(null)//无输出

        let fun = fn.bind(aa);
        fun()//a

小应用,点击按钮禁用,两秒开启

    <button>点击禁用,两秒开启</button>
    <script>
        let btn = document.querySelector('button');
        btn.addEventListener('click',function(){
            this.disabled = true
            setTimeout(function(){
                this.disabled = false
            }.bind(this),2000)
        })

      </script>

这个地方把定时器的this改为了btn,因为bind(this)里面这个this还是btn

 节流

在一定的时间内只能执行一次,不管触发了多少次,在这个时间内只执行一次

例子:div滑动时,200ms内只改变span一次

        var start = 0

        document.querySelector('div').addEventListener('mousemove',function(){
            let now = Date.now()
            if(now-start>=200){
                    document.querySelector('span').innerHTML =i++
                    start = now
                }

        })

每次调用函数时都将此时的时间赋给now,当now与start差别大于200毫秒时才会改变i,然后将现在的时间赋给start以进行下次函数

上面用的是时间差值的方法,也可以采用定时器的方法

这里再加了一个immediate 变量,具体原理参照  防抖

<body>
  <input type="text">
  <button>cancel</button>

  <button class="throttle">throttle</button>

  <script>
    let ipt = document.querySelector('input')

    let mythrottle = function(fn,interval,immediate =true){
      let timeout = null
      let immediate_done = false
      let _throttle = function(){
        if (!immediate_done && immediate) {
            fn.apply(this)
            immediate_done = true
            return
          }

        if(!timeout){
          timeout = setTimeout(()=>{
          fn.apply(this)
          timeout = null
        },interval)
        }
      }
      return _throttle
    }

    ipt.addEventListener('input',mythrottle(function(){
      console.log(this.value);
    },1000))
  </script>

 防抖

触发后n秒内只执行一次,但是如果这段时间内还触发了,则会重新计算时间

也就是说,在一定时间内我不停的进行高速的事件触发,但是只会在最后一次触发结束后进行执行

应用场景:输入框,一般是等我输入完了才会去匹配

例子:在div滑动时 停下来一秒钟才会改变span

        document.querySelector('div').addEventListener('mousemove',function(){
            if(timeget) clearTimeout(timeget)
            timeget = setTimeout(function(){
                document.querySelector('span').innerHTML =i++

            },1000)
        })

例子:再次采用函数实现,需求是对输入框,输入结束一段时间后反应,这里还提供了应该取消接口,可以在这段时间结束前 结束响应

    <input type="text">
    <button>cancel</button>

    <script>
        let mydebounce = function(fn,delay){
            let timeout = null 
            let _debounce = function(){
                if(timeout) clearTimeout(timeout)
                timeout = setTimeout(()=>{
                    fn.apply(this)
                },delay)
            }
            _debounce.cancel = function(){
                if(timeout) clearTimeout(timeout)
            }
            return _debounce
        }

        let ipt = document.querySelector('input')
        let btn = document.querySelector('button')
        let debounce = mydebounce(function(){
            console.log(this.value);
        },2000)
        ipt.addEventListener('input',debounce)
        btn.addEventListener('click',debounce.cancel)
    </script>
</body>

再加一个立即执行功能,即在输入时,第一次输入马上响应,后面输入则等待一段时间,然后再次输入又马上响应

        let mydebounce = function(fn,delay,immediate=false){
            let timeout = null 
            let immediate_done = false
            let _debounce = function(){
                if(timeout) clearTimeout(timeout)
                if(!immediate_done && immediate){
                    fn.apply(this)
                    immediate_done = true
                    return
                }
                timeout = setTimeout(()=>{
                    fn.apply(this)
                    immediate_done = false
                },delay)
            }
            _debounce.cancel = function(){
                if(timeout) clearTimeout(timeout)
                immediate_done = false
            }
            return _debounce
        }

定义了 immediate表示是否需要这个功能,immediate_done判断是否完成,当输入时,immediate为1,immediate_done为0时,会马上执行fn函数,然后使得immediate_done为1。在执行完时间间隔后,又将immediate_done为0

还可以将_debounce函数返回为promise,好处是在fn传入时有返回值的情况下,可以拿到这个返回值

      let res = null
      let _debounce = function () {
        return new Promise((resolve, reject)=>{
          if (timeout) clearTimeout(timeout)
          if (!immediate_done && immediate) {
            res = fn.apply(this)
            resolve(res)
            immediate_done = true
            return
          }

          timeout = setTimeout(() => {
            res = fn.apply(this)
            resolve(res)
            immediate_done = false
          }, delay)
        })
      }

 若fn有返回值,就可以通过.then得到

    let ipt = document.querySelector('input')
    let btn = document.querySelector('button')

    let debounce = mydebounce(function () {
      console.log(this.value);
      return(111)
    }, 1000)


    ipt.addEventListener('input', debounce)
    btn.addEventListener('click', debounce.cancel)

    debounce().then(res=>{
      console.log(res);
    })

 还可以使用lodash

节流

_.throttle(fn,300)

在300ms内只能执行一次fn函数

        function fn(){
            document.querySelector('span').innerHTML =i++
        }
        document.querySelector('div').addEventListener('mousemove',_.throttle(fn,300))

        

 防抖

 _.debounce(fn,300)

停下来300ms后才执行fn函数

        function fn(){
            document.querySelector('span').innerHTML =i++
        }
        document.querySelector('div').addEventListener('mousemove',_.debounce(fn,300))

视频案例

    const video = document.querySelector('video')
    video.onloadeddata = function(){
        video.currentTime  = localStorage.getItem('time')||0
    }
    video.ontimeupdate = _.throttle(function(){
        localStorage.setItem('time',video.currentTime)
    },1000)

得到video视频

每次打开页面都把存储中的time拿出来,赋值给视频的currentTime,即跳转到对应的时间

视频的时间发生改变时,添加防抖,每隔1s更新存储中的time值

实现每次页面打开可以返回上次视频播放的地方

事件总线

当项目中有很多组件,很多文件,当一个文件中的某一个事件触发,希望另外一个文件得到响应,就需要用到事件总线

这里我们自己用js实现

一般来说这个事件总线是一个对象,里面有emit函数,用于发送时间,也有on函数,用于接受事件

  <button>aaaa</button>
  <script>
    class EventBus{
      constructor(){
        this.eventMap = {}//这个map用于存储on绑定的函数
      }
      on(eventname,fn){
        let fns = this.eventMap[eventname]
        if(!fns){//第一次如果没有函数数组时,自己创造一个
          fns=[]
          this.eventMap[eventname] = fns
        }
        fns.push(fn)//将回调函数放入对应的函数数组
      }

      emit(eventname,...args){
        let fns = this.eventMap[eventname]
        if(!fns) return
        fns.forEach(fn => {
          fn(...args)
        });
      }
    }
    let eventBus = new EventBus()

    eventBus.on('btnclick',()=>{
      console.log(1111);
    })

    eventBus.on('btnclick',()=>{
      console.log(2222);
    })

    let btn = document.querySelector('button')
    btn.addEventListener('click',function(){
      console.log('自己得到');
      eventBus.emit('btnclick')
    })
  </script>

因为on函数,对于一种事件的响应函数可能不止一个,所以需要eventMap来存储函数,其数据结构为{ eventname:[fn1,fn2]}

在emit时,先取得事件对应的函数数组,再以此执行

还可以写一个删除函数函数

      off(eventname,fn){
        let fns = this.eventMap[eventname]
        if (!fns) return
        for(let i=0;i<fns.length;i++){
          if(fn === fns[i]){
            fns.splice(i,1)
            break 
          }
        }
        //如果fns已经被清空了,那直接将事件删除
        if(fns.length==0){
          delete this.eventMap[eventname]
        }
      }

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_54517101/article/details/127158005

智能推荐

Lua收到C++传入的参数类型有一种userdata_lua接收参数_AlbertS的博客-程序员宅基地

前言前两天测试Lua接收C++函数的参数时,发现number、boolean、string都没有什么问题,但是userdata和lightuserdata有什么区别呢?于是测试了一下,我们来看看结果...

机器学习——常用的回归模型性能评价指标_從疑開始的博客-程序员宅基地

对于回归而言,更注重的是模型拟合的曲线,相对于真实曲线的误差。主要包括:拟合优度/R-Squared,校正决定系数(Adjusted R-square),均方误差(MSE),均方根误差(RMSE),误差平方和(SSE),平均绝对误差(MAE),平均绝对百分比误差(MAPE)。1、均方误差(MSE)均方误差是指:观测值与真值偏差的平方和与观测次数的比值公式:MSE相当于模型中的损失函数,线性回归过程中尽量让该损失函数最小。那么模型之间的对比也可以用它来比较。MSE可以评价模型的预测精度,MSE的值越

struts2中css,js等资源无效 非路径问题(新手问题)_AAA_1362035315的博客-程序员宅基地

一个小小的Strust2例子然后发现css,js,图片用不了,debugger下发现无法访问这些资源(404错误),妈的,那个例子明明可以的,起码从书上的图片看。发现是web.xml中的过滤器的问题,代码是这样的:web.xml&lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;web-app xmlns="http:/...

等价类的相关练习_某报表处理系统要求用户输入处理报表的日期_心系五道口的博客-程序员宅基地

1.某报表处理系统,要求用户输入处理报表的日期。系统规定日期由年、月的六位数字字符组成,前四位代表年,后两位代表月。设日期限制在1990年1月至1999年12月,即系统只能对该段时期内的报表进行处理。如果用户输入的日期不在此范围内,则显示输入错误。现用等价类划分法设计测试用例,来测试程序的日期检查功能。...

【一年总结】记我的大二生活_Dreamchaser追梦的博客-程序员宅基地

回顾大二这一学年,不同于大一的单调色彩,大二我所经历的是非常丰富多彩的,尽管也很会遇到一些不如意的事情,有心酸,有遗憾,有愤慨,有无奈;但是我也遇到了一些非常有趣的人和事,懂得了一些只有经历才能明白的道理。我清楚地知道生活不应只有苦难,更多还有来自平凡之人、之物的感动,欣喜,还有希望。一路顺风的人生并不完美,也不可能有这样的人生。技术的追求与探索、会长的责任与担当、爱情的憧憬、梦想的追逐、现实的愤慨与无奈、平凡之人、之物的感动.......这一切的一切共同拼接成了我的大二生活,有苦有乐,有起有伏

Doris建表_doris建表语句_贾斯汀玛尔斯的博客-程序员宅基地

Name: ‘CREATE TABLE’ Description:该语句用于创建 table。 语法:CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [database.]table_name(column_definition1[, column_definition2, …][, index_definition1[, ndex_definition12,]])[ENGINE = [olap|mysql|broker|hive]][key_desc][COM

随便推点

SpringBoot 配置文件加载顺序_夏沐_lk的博客-程序员宅基地

注:file:指项目根目录(项目名下)。classpath指类的根目录(src/main/resources)

十大滤波算法程序大全_atan滤波_gezhiwu1213的博客-程序员宅基地

1、限幅滤波法(又称程序判断滤波法)/*A、名称:限幅滤波法(又称程序判断滤波法)B、方法:    根据经验判断,确定两次采样允许的最大偏差值(设为A),    每次检测到新值时判断:    如果本次值与上次值之差    如果本次值与上次值之差>A,则本次值无效,放弃本次值,用上次值代替本次值。C、优点:    能有效克服因偶然因素引起的脉冲干扰。D、缺

实训Part4-code_Bian~的博客-程序员宅基地

文件结构 . ├── BlusterCritter │ ├── BlusterCritter.java │ └── BlusterCritterRunner.java ├── ChameleonKid │ ├── ChameleonKid.java │ └── ChameleonKidRunner.java ├── KingCrab │ ├── KingCrab.java │ └── KingCrabRunner.java ├── ModifiedChamele

C11新特性(部分)_Mi ronin的博客-程序员宅基地

1.类型推导2.nullptr指针空值3.基于范围的for循环4.typedef与using5.新增容器

推荐文章

热门文章

相关标签