Day.25 「从 事件绑定 与 定时器 认识回调函式!」 —— JavaScript 定时器 & Callback

「从 事件绑定 与 定时器 认识回调函式!」 —— JavaScript 定时器 & Callback

我们前面已经了解了事件绑定与事件冒泡了,但是使用 物件元素.绑定事件 有不方便的地方

  • 只能同时为一个元素的一个事件绑定一个响应函式
  • 不能绑定多个,如果绑定多个,後面会覆盖前面的
btn.onclick = function () {
  console.log("我是第一个绑定事件");
}

btn.onclick = function () {
  console.log("我是第二个绑定事件");  // 这个绑定事件会覆盖前面的绑定事件
}

/*
 "我是第二个绑定事件"
*/

addEventListener()

这时可以使用最广泛使用的事件监听
事件监听的参数

  • 第一个参数:要触发的事件的字串,注意不要添加 on,例如:要绑定 onclick 事件,参数就写 "click"
  • 第二个参数:触发事件时的回调函式
  • 第三个参数:是否再捕获阶段触发,布林值,通常情况下都是 false
btn.addEventListener("click", function(){
  console.log("我是第一个绑定事件");
}, false);

btn.addEventListener("click", function(){
  console.log("我是第二个绑定事件");    // 这样就会直接"添加"绑定事件,而不会覆盖
}, false);

/*
 "我是第一个绑定事件"
 "我是第二个绑定事件"
*/

与普通的事件绑定不一样,事件监听是用添加事件来绑定的,所以就不会覆盖前面绑定的事件

定时器

而有时候我们并不想透过事件监听来触发事件,而是设定时间,在开启网页後,一段时间触发函式。
这时就要利用 JavaScript 定时器,就可以达到效果。

setTimeout(function(){
  console.log("时间到!")
}, 2000)

而除了 setTimeout 时间到只触发一次的定时器,还有持续一段时间的 setInterval 定时器可以用。

回调函式(Callback Function)

而我们在绑定事件定时器所使用的匿名函式就是所谓的回调函式

btn.addEventListener("click", function(){
  console.log("我是 DOM 事件的回调函式");
}, false);

setTimeout(function(){
  console.log("我是定时器的回调函式")
}, 2000)

回调函式的特点

  • 我们自己设置的,这好像废话
  • 我们没有主动调用,让函式变成另一个函式的参数
  • 但它自己会自动调用,让函式控制参数函式的执行时机

所以上面看到的 addEventListener()setTimeout() ,显而易见的都是函式!
而我们自己定义的函式就做为参数,等时机到了执行。

常见的回调函式?

常见的回调函式有四个

  • DOM 事件回调函式
  • 定时器回调函式
  • AJAX 请求回调函式
  • 生命周期回调函式

用函式控制执行函式的时机

首先我们先用定时器,计 0 秒马上 console

function A () {
  setTimeout(function(){
    console.log("我是函式 A");
  }, 0)
}

function B () {
  console.log("我是函式 B");
}
function C () {
  console.log("我是函式 C,最後一个函式");
}

A();
B();
C();

/*
  "我是函式 B"
  "我是函式 C,最後一个函式"
  "我是函式 A"
*/

结果很神奇,没想到 0 秒马上 console 的函式,竟然最後才出现!
那是因为 JavaScript 在遇到定时器的时候,会先跳过这段函式,先继续执行後面的程序码,在执行定时器(此时已经有毫秒的延迟),这样的好处当然就是不用苦苦地等计时器结束,让可以先运作的运作完,让使用者不会有等待的感觉,而这个现象称作非同步

而後面的函式又与计时器的这个函式有关的话!就会使用 Callback Function 来处理~

function A (FnB, FnC) {
  setTimeout(function(){
    console.log("我是函式 A");
    FnB( FnC );
  }, 0)
}

function B (Fn) {
  console.log("我是函式 B");
  Fn();
}
function C () {
  console.log("我是函式 C,最後一个函式");
}

A(B, C);

/*
  "我是函式 A"
  "我是函式 B"
  "我是函式 C,最後一个函式"
*/

这时在确定执行完 A 後,才会接续执行 BC

回调函式的优缺点

优点

  • 能够确保执行的时机
  • 更好维护

例如要添加 D 函式就可以直接使用参数带入

function A (FnB, FnC) {
  setTimeout(function(){
    console.log("我是函式 A");
    FnB( FnC );
  }, 0)
}

function B (Fn) {
  console.log("我是函式 B");
  Fn();
}
function C () {
  console.log("我是函式 C,最後一个函式");
}
function D (Fn) {
  console.log("我是函式 D")
  Fn();
}

D( A(B, C));

/*
  "我是函式 D"
  "我是函式 A"
  "我是函式 B"
  "我是函式 C,最後一个函式"
*/

缺点

相信学程序到一定程度的人,多少都有听过宛如波动拳的 Callback Hell 吧!

function (n, fnA, fnB, fnC, fnD, fnE) {
    if (fnA(n)) {
        if (fnB(n)) {
            if (fnC(n)) {
                if (fnD(n)) {
                    if (fnE(n)) {
                      console.log("fnE");
                    } else {
                      console.log("fnD");
                    }
                } else {
                  console.log("fnC");
                }
            } else {
              console.log("fnB");
            }
        } else {
          console.log("fnA");
        }
    } else {
      console.log("null");
    }
}

总结

回调函式(Callback Function)是个很常见的写法,但要小心使用,使用不当後续会很痛苦,而後面又有推出 PromiseAsync / Await ,就解决了同步与非同步的问题!

参考资料


<<:  认识与了解WebDAV

>>:  【从实作学习ASP.NET Core】Day22 | 前台 | 商品留言板

Leetcode 挑战 Day 02 [9. Palindrome Number]

9. Palindrome Number Palindrome Number中文意思即是回文数字,这...

switch-case 与select

Golang switch-case 与select 如题来看一下switch-case 与sele...

Day 26 - Vue 与 HTTP请求 (1)

前一天中我们讲解了如何利用Vue CLI快速建立专案,再进入到专案开发之前,还是有一些知识需要恶补的...

[ Vue ] 使用 Vitawind 1.2 来 建置 Vite + Tailwind JIT 专案

建立 Vite 专案 在你要放置专案的地方执行这个指令来建立 vue 模板的 vite 专案 # n...

DAY 15:Factory Method Pattern,把复杂的逻辑拆分至小工厂中

工厂模式主要有三种不同的实作: Simple Factory Pattern Factory Met...