vue事件处理

一、DOM 事件流 想必看过《红宝书》的前端 er 对标题都不会特别陌生,我们来看看定义:当一个 HTML 元素产生一个事件时,该事件会在树形结构的 DOM 上面沿着元素节点路径进行传播,事件所经过的路径结点都会收到该事件,这个传播过程可称为 DOM 事件流。 二、DOM 事件流模型 DOM 事件流分为 捕获型事件流 和 冒泡型事件流。两种事件流分别对应三阶段 DOM 事件流模型中的捕获阶段和冒泡阶段: 捕获阶段:事件从最外面的祖先节点依次传递到最里面的后代节点 目标阶段:真正的目标节点正在处理事件的阶段 冒泡阶段:事件又从最里面的后代节点逐层传出到最外面的祖先节点 三、Vue 之事件处理 Vue 中提供了事件绑定的语法糖,我们可以很简单地在标签中直接使用 @click="handleClick($event)" 就可以绑定点击事件。 而且在 Vue 里面,Vue 的事件触发默认为冒泡过程监听,意思就是上面 @click="handleClick($event)" 中点击事件的执行是冒泡过程触发的。 四、Vue 之事件修饰符 4.1 .capture 捕获监听器 <div @click="log(1)" @click.capture="log(1)" style="background-color: #00f"> <div @click="log(2)" @click.capture="log(2)" style="background-color: #66f"> <div @click="log(3)" @click.capture="log(3)" style="background-color: #ccf"> <div @click="log(4)" @click.capture="log(4)" style="background-color: #fff"> 点击这里 </div> </div> </div> </div> 控制台打印结果为:1 2 3 4 4 3 2 1 ...

October 5, 2024

Map与Set

每当去学习ES6的时候,我都会不由自主的打开阮一峰老师的《ES6入门》去学习和查找用法,相信大多数同学也都和我一样看过阮老师写的文章。 当然大家也都知道ES6里常用的API就那么些,不多不少,用在项目中也是刚刚好。 不过在每次读到Set和Map数据结构那一章的时候,总是有点不知所措,因为我不明白实现这样的数据结构,目的是什么,意义又是什么呢 Set和Map主要的应用场景在于数组去重和数据存储,幸运的是在读了关于数据结构和算法之类的书籍后,恍然大悟的发现 原来Set是一种叫做集合的数据结构,Map是一种叫做字典的数据结构 那么下面就来了解下这两种数据结构,最后来亲手实现的一个ES6中的Set和Map吧 集合 集合是由一组无序且唯一(即不能重复)的项组成的,可以想象成集合是一个既没有重复元素,也没有顺序概念的数组 ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值 Set 本身是一个构造函数,用来生成 Set 数据结构 这里说的Set其实就是我们所要讲到的集合,先来看下基础用法 const s = new Set(); [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)); for (let i of s) { console.log(i); // 2 3 5 4 } // 去除数组的重复成员 let array = [1,2,1,4,5,3]; [...new Set(array)] // [1, 2, 4, 5, 3] 具体用法如果还有不清楚的,这里我会在后面一一细说。现在还是来看一下以ES6中Set类(数据结构)为基础实现的集合吧 Set实例的属性和方法 Set的属性: size:返回集合所包含元素的数量 Set的方法: 操作方法 add(value):向集合添加一个新的项 delete(value):从集合中移除一个值 has(value):如果值在集合中存在,返回true,否则false clear(): 移除集合里所有的项 遍历方法 keys():返回一个包含集合中所有键的数组 values():返回一个包含集合中所有值的数组 entries:返回一个包含集合中所有键值对的数组(感觉没什么用就不实现了) forEach():用于对集合成员执行某种操作,没有返回值 创建一个集合 function Set(arr = []) { // 可以传入数组 let items = {}; this.size = 0; // 记录集合中成员的数量 } module.exports = Set; 这里用{}对象来表示集合,也是因为对象不允许一个键指向两个不同的属性,保证了集合里的元素都是唯一的 ...

October 2, 2024

高阶函数

高阶函数是指那些可以接受函数作为参数或者返回函数作为结果的函数。这种特性使得JavaScript的函数变得异常灵活和强大,为开发者提供了丰富的工具和手段来编写更加简洁、可重用和模块化的代码。 本文将详细介绍JavaScript高阶函数的概念、特性、常见用法以及在实际开发中的应用。 一、高阶函数的概念 高阶函数是函数式编程中的一个核心概念。在JavaScript中,函数是一等公民,这意味着函数可以像其他数据类型(如数字、字符串、对象等)一样被传递和操作。高阶函数正是利用了这一特性,使得函数可以作为参数传递给其他函数,或者作为其他函数的返回值。 二、高阶函数的特性 函数作为参数:高阶函数可以接受一个或多个函数作为参数。这使得我们可以将特定的行为(即函数)作为参数传递给高阶函数,从而实现更加灵活和可重用的代码。 函数作为返回值:高阶函数可以返回一个函数作为结果。这种特性允许我们创建可以动态生成新函数的函数,这些新函数可以根据需要执行不同的行为。 闭包:闭包是高阶函数的一个重要特性。闭包允许函数访问其外部作用域中的变量,即使该函数在其外部作用域之外执行。这使得高阶函数可以捕获和存储状态,从而创建更加复杂和强大的函数行为。 三、高阶函数的常见用法 数组方法:JavaScript中的数组方法(如map、filter、reduce等)都是高阶函数的典型例子。这些方法接受一个函数作为参数,并对数组中的每个元素执行该函数,从而返回一个新的数组或单一结果。 map方法:用于创建一个新数组,其结果是该数组中的每个元素调用一个提供的函数后返回的结果。 filter方法:创建一个新数组,其包含通过所提供函数实现的测试的所有元素。 reduce方法:对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。 函数组合:函数组合是指将多个函数组合在一起形成一个新的函数。通过高阶函数,我们可以轻松地将多个函数组合在一起,从而创建一个更加复杂和强大的函数。这种技术在实际开发中非常有用,特别是当我们需要处理一系列转换或操作时。 柯里化:柯里化是一种将接收多个参数的函数转换为一系列接收单一参数的函数的技术。虽然柯里化本身不是高阶函数,但高阶函数在实现柯里化时扮演着重要角色。通过高阶函数,我们可以创建一个接受原始函数作为参数的柯里化函数,并返回一个新的函数来处理单一参数。 创建高阶函数:我们可以编写自己的高阶函数来实现特定的行为。例如,我们可以编写一个高阶函数来接受一个函数和一个数组作为参数,并返回该数组中应用该函数后的结果。 四、高阶函数在实际开发中的应用 1. 数据转换 高阶函数非常适合用于数据的转换和映射。例如,我们可以使用Array.prototype.map方法将一个数组中的每个元素转换为另一种形式。 // 定义一个高阶函数,它接受一个转换函数和一个数组作为参数 const transformArray = (transformFn, array) => array.map(transformFn); // 定义一个简单的转换函数,将数字转换为它的平方 const square = x => x * x; // 使用高阶函数和转换函数来处理数组 const numbers = [1, 2, 3, 4, 5]; const squaredNumbers = transformArray(square, numbers); console.log(squaredNumbers); // 输出: [1, 4, 9, 16, 25] 在这个例子中,transformArray是一个高阶函数,它接受一个转换函数transformFn和一个数组array作为参数。然后,它使用Array.prototype.map方法来遍历数组,并对每个元素应用转换函数。 2. 事件处理 在Web开发中,事件处理是一个常见的任务。通过高阶函数,我们可以创建更加灵活和可重用的事件处理器。 // 定义一个高阶函数,它接受一个事件处理函数和一个事件类型作为参数 const createEventHandler = (handlerFn, eventType) => event => { if (event.type === eventType) { handlerFn(event); } }; // 定义一个简单的事件处理函数,用于打印事件信息 const logEvent = event => console.log(`Event type: ${event.type}, Target: ${event.target}`); // 创建一个点击事件处理器 const handleClick = createEventHandler(logEvent, 'click'); // 假设我们有一个DOM元素,我们可以为它添加事件监听器 // const button = document.querySelector('button'); // button.addEventListener('click', handleClick); // 为了演示,我们直接调用处理器并传递一个模拟的事件对象 const mockEvent = { type: 'click', target: document.body }; handleClick(mockEvent); // 输出: Event type: click, Target: [object HTMLBodyElement] 在这个例子中,createEventHandler是一个高阶函数,它接受一个事件处理函数handlerFn和一个事件类型eventType作为参数,并返回一个新的函数。这个新的函数会检查传入的事件对象的类型是否与指定的事件类型匹配,如果匹配,则调用事件处理函数。 ...

October 1, 2024

单页面架构

传统的多页面应用构建方式: 纯服务端渲染,前后端不分离,使用jsp,jade,’ejs’,’tempalte’等技术在后台先拼接成对应的HTML结构,然后转换成字符串,在每个对应的路由返回对应的数据(文件)即可 Jade模版服务端渲染,代码实现: const express= require('express') const app =express() const jade = require('jade') const result = *** const url path = *** const html = jade.renderFile(url, { data: result, urlPath })//传入数据给模板引擎 app.get('/',(req,res)=>{ res.send(html)//直接吐渲染好的`html`文件拼接成字符串返回给客户端 }) //RestFul接口 app.listen(3000,err=>{ //do something }) 使用jQuery等传统库绘制的前端页面 传统前后端不分离,服务端渲染的优缺点: 优点: SEO友好,因为返回给前端的是渲染好的HTML结构,里面的内容都可以被爬虫抓取到。 对于一些应用性能等要求不高的项目,比如某个公司的静态网页,内容很少的情况下,直接一把梭就好,不用再搭建工程化的环境等 对于后端程序员(全干工程师)来说,不用去特意学习前端框架,公司也不用特意去招聘前端 兼容性好,传统服务端渲染多页面应用吐出来的都是字符串,HTML结构 缺点: 如果项目很大,不利于维护,据我所知,目前很多云计算公司,还有不少都是使用非单页面应用,例如一个几十万行的项目是用jQuery写的,如果注释和文档不是非常齐全,那么真的会无从下手 性能和用户体验,不能跟单页面应用相比 后期迭代,升级空间不大,目前大部分写得比较好的库,都建立vue,react等框架基础上,他们都有一套自己的运行机制,有自己的生命周期,并且不像传统的应用,还加上了一层虚拟DOM以及diff算法 现在类似Ant-Design-pro这样的开箱即用的库已经很多,单页面应用的学习和开发成本已经很低很低,如果还在使用传统的技术去开发新的应用,对于开发人员多内心来说也是一种折磨。 这里并不是说多页面应用不好,只能说各有各自的好,单页面应用如果通过大量的极致优化手段,是可以从不少方面跟原生一拼。 目前的单页面应用: 只有一张Web页面的应用,是一种从Web服务器加载的富客户端,单页面跳转仅刷新局部资源 ,公共资源(js、css等)仅需加载一次,常用于PC端官网、购物等网站 其实只有一个空的DIV标签,其他都是js动态生态的内容 单页面应用实现步骤: 代码实现: 首先是一个静态模板文件 index.html <!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> <div id="root"></div> </body> <script> </script> </html> 在vue react框架的入口文件中指定对应的渲染元素: import React from 'react; import ReactDOM from 'react-dom'; ReactDOM.render( <App/>, document.querySelector("#root") ) 引入react-router或者 react-router-dom,dva等路由跳转的库 配置路由跳转 <HashRouter>//这里使用HashRouter <ErrorBoundary>//React错误边界 <Switch> <Route path="/login" component={Login} /> <Route path="/home" component={Home} /> <Route path="/" component={NotFound} />//404路由或者重定向都可以 </Switch> </ErrorBoundary> </HashRouter> 单页面应用所谓路由跳转,其实最终结果就是: 浏览器的url地址发生变化,但是其实并没有发送请求,也没有刷新整个页面 根据我们配置的路由信息,每次点击切换路由,会切换到不同的组件显示,类似于选项卡功能的实现,但是同时url地址栏会变化 分为HashRouter和BrowserRouter两种模式 自己实现一个粗略的路由跳转: 自己实现传统的Hash模式跳转: hash 就是指 url 后的 # 号以及后面的字符。例如www.baidu.com/#segmentfault,那么#segmentfault就是hash值 ...

August 7, 2025

性能优化

本文篇幅较长,将从 编译阶段 -> 路由阶段 -> 渲染阶段 -> 细节优化 -> 状态管理 -> 海量数据源,长列表渲染 方向分别加以探讨。 一 不能输在起跑线上,优化babel配置,webpack配置为项 1 真实项目中痛点 当我们用create-react-app或者webpack构建react工程的时候,有没有想过一个问题,我们的配置能否让我们的项目更快的构建速度,更小的项目体积,更简洁清晰的项目结构。 随着我们的项目越做越大,项目依赖越来越多,项目结构越来越来复杂,项目体积就会越来越大,构建时间越来越长,久而久之就会成了一个又大又重的项目,所以说我们要学会适当的为项目‘减负’,让项目不能输在起跑线上。 2 一个老项目 拿我们之前接触过的一个react老项目为例。我们没有用dva,umi快速搭建react,而是用react老版本脚手架构建的,这对这种老的react项目,上述的问题都会存在,下面让我们一起来看看。 我们首先看一下项目结构。 再看看构建时间。 为了方便大家看构建时间,我简单写了一个webpack,plugin ConsolePlugin ,记录了webpack在一次compilation所用的时间。 const chalk = require('chalk') /* console 颜色 */ var slog = require('single-line-log'); /* 单行打印 console */ class ConsolePlugin { constructor(options){ this.options = options } apply(compiler){ /** * Monitor file change 记录当前改动文件 */ compiler.hooks.watchRun.tap('ConsolePlugin', (watching) => { const changeFiles = watching.watchFileSystem.watcher.mtimes for(let file in changeFiles){ console.log(chalk.green('当前改动文件:'+ file)) } }) /** * before a new compilation is created. 开始 compilation 编译 。 */ compiler.hooks.compile.tap('ConsolePlugin',()=>{ this.beginCompile() }) /** * Executed when the compilation has completed. 一次 compilation 完成。 */ compiler.hooks.done.tap('ConsolePlugin',()=>{ this.timer && clearInterval( this.timer ) const endTime = new Date().getTime() const time = (endTime - this.starTime) / 1000 console.log( chalk.yellow(' 编译完成') ) console.log( chalk.yellow('编译用时:' + time + '秒' ) ) }) } beginCompile(){ const lineSlog = slog.stdout let text = '开始编译:' /* 记录开始时间 */ this.starTime = new Date().getTime() this.timer = setInterval(()=>{ text += '█' lineSlog( chalk.green(text)) },50) } } 构建时间如下: ...

December 17, 2024

表单验证

HTML 表单是用于收集用户输入数据的重要元素,通常用于提交到服务器进行处理。它包括一系列控件,如文本输入框、按钮、复选框、单选按钮、文件上传等。表单通过 form 标签定义,用户可以通过这些控件与网页进行交互。 1. 基础结构 表单通过 <form> 标签创建,表单的 action 属性定义数据提交的目标 URL,method 属性定义提交方式(如 GET 或 POST)。 <form action="/submit" method="POST"> <input type="text" name="username"> <input type="submit" value="Submit"> </form> 2. 表单控件 HTML 表单提供了多种用户输入控件: 文本输入 (<input type="text">, <textarea>) 密码输入 (<input type="password">) 单选框和复选框 (<input type="radio">, <input type="checkbox">) 下拉列表 (<select>) 按钮 (<button>, <input type="submit">) 文件上传 (<input type="file">) 隐藏输入 (<input type="hidden">) 3. 表单的属性 action:指定表单数据提交到的 URL。 method:定义提交方式,常用 GET(将数据附在 URL 后)和 POST(将数据放入 HTTP 请求体中)。 enctype:指定数据的编码类型,常用于文件上传(如 multipart/form-data)。 autocomplete:启用或禁用浏览器的自动完成功能。 4. 表单校验 HTML5 引入了许多新的表单校验功能,能够减少 JavaScript 校验代码的复杂度。校验功能包括: ...

October 14, 2024

浮动、清除浮动和BFC

浮动、清除浮动和BFC是前端开发中常见的概念,它们对于页面布局及美化至关重要。 浮动 在网页设计中,浮动是一种常见的布局技术,可以让元素脱离文档流,我们来看看它有什么作用吧! 使文字环绕 代码示例如下: html结构: <div class="box"></div> <div class="text">一段文字</div> css样式: .box{ width: 200px; height: 200px; background-color: coral; } 若不加浮动,则页面是这样的效果: 但若是在box加上float: left;(向左浮动),则能使文字环绕box这个盒子: .box{ width: 200px; height: 200px; background-color: coral; float: left; } 让块级元素同行显示 块级元素本身是占据一整行的,但浮动能让多个块级元素处于同一行,示例如下: 若不加浮动,则多个块级元素各自占据一行: html结构: <ul> <li>1</li> <li>2</li> <li>3</li> </ul> css样式: *{ margin: 0; padding: 0; } ul li{ list-style:none; width: 200px; height: 100px; font-size: 16px; } li:nth-child(1){/*子容器选择器 */ background-color: rgb(227, 149, 149); } li:nth-child(2){ background-color: rgb(205, 139, 197); } li:nth-child(3){ background-color: rgb(145, 212, 227); } 页面如图: ...

October 10, 2024

自定义指令

如何自定义指令? 其实关于这个问题官方文档上已经有了很好的示例的,我们先来温故一下。 除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子,如下: 当页面加载时,该元素将获得焦点 (注意: autofocus 在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能: // 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus() } }) 如果想注册局部指令,组件中也接受一个 directives 的选项: directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() } } } 然后你可以在模板中任何元素上使用新的 v-focus property,如下: <input v-focus> 指令内部提供的钩子函数 一个指令定义对象可以提供如下几个钩子函数 (均为可选): bind: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 inserted: 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 update: 所在组件的 VNode 更新时调用,**但是可能发生在其子 VNode 更新之前。**指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。 componentUpdated: 指令所在组件的 VNode 及其子VNode全部更新后调用。 unbind: 只调用一次,指令与元素解绑时调用。 钩子函数参数 指令钩子函数会被传入以下参数: ...

October 5, 2024

WeakMap 和 WeakSet

1. 什么是 WeakSet WeakSet 结构与 Set 类似,也是不重复的值的集合,具备 WeakSet.prototype.add(value) :向 WeakSet 实例添加一个新成员。 WeakSet.prototype.delete(value) :清除 WeakSet 实例的指定成员。 WeakSet.prototype.has(value) :返回一个布尔值,表示某个值是否在 WeakSet 实例之中。 这三个方法。但是,它与 Set 有两个区别。 区别一:WeakSet 的成员只能是对象和Symbol类型,而不能是其他类型的值,我们用代码演示一下 从代码中可以看到,想像Set一样直接往 WeakSet 中添加原始类型化会报错,只能添加对象和Symbol 区别二(这是核心): WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。 (这句解释来自ECMAScript 6 入门,第一眼看到这句话这话会觉得有点晦涩,我们来解释一下) 弱引用:垃圾回收机制有一套自己的回收算法,我们都知道一个函数执行完成后该函数在调用栈中创建的执行上下文会被销毁,这里说的销毁,其实指的就是执行上下文中环境变量、词法变量中的数据存储所占据的内存空间被垃圾回收机制所回收,那么垃圾回收机制不考虑 WeakSet 对该对象的引用是不是就意味着垃圾回收机制不会回收 WeakSet 对象里面的数据所占据的内存呢?不!不是的!代码是最好的解释 我们用 ws 中存放一个对象,然后再将该对象置为null,(这里要说明一下,一个变量被置为null,就意味着这个变量的内存可以被回收了)看着这个打印结果有没有突然明白了点什么,对!没错!只要 WeakSet 结构中的对象不再需要被引用,那么 WeakSet 就直接为空了,这不就意味着WeakSet中的数据所占据的内存被释放了吗。好的,你或许还会有疑问,难道不用 WeakSet 存储数据,结果就不是这样的吗,来打消你的疑虑 不使用 WeakSet 存放数据,当变量obj为null时,fistName依旧是有值的,对比这段代码,我们可以清晰的看出, WeakSet中 - 垃圾回收机制会自动回收该对象所占用的内存 2. 这里有’BUG' 可能已经有小伙伴拔出了他40米长的大剑了,上图二中的代码其实有问题,真实的打印应该是 各位手中的长剑收一收,先别急,其实我们前面聊的没有问题,为什么这里真是打印能出来结果呢?这个问题在国外某技术论坛上也是一度出现在 WeakSet 话题榜首位,解释为:因为浏览器的垃圾回收机制是不受我们控制的,我们无法知道垃圾回收机制什么时候执行,之所以打印能出现内容,其实是因为打印的时候垃圾回收机制没有启动。所以我们可以认为,上图二中的打印在垃圾回收启动之后,它就是空的 我们可以这样来证明它: 如果这样你觉得依旧不够睡服你自己的话,我们把这段代码搬到node中: ...

October 2, 2024

异步编程

浏览器中的JavaScript程序是典型的事件驱动型程序,即它们会等待用户触发后才真正的执行,而基于的JavaScript的服务器通常要等待客户端通过网络发送请求,然后才能执行。这种异步编程在JavaScript是很常见的,下面就来介绍几个异步编程的重要特性,它们可以使编写异步代码更容易。 本文将按照异步编程方式的出现时间来归纳整理: 一、什么是异步 下面先来看看同步和异步的概念: 同步: 在执行某段代码时,在没有得到返回结果之前,其他代码暂时是无法执行的,但是一旦执行完成拿到返回值,即可执行其他代码。也就是说,在此段代码执行完未返回结果之前,会阻塞之后的代码执行,这样的情况称为同步。 异步: 当某一代码执行异步过程调用发出后,这段代码不会立刻得到返回结果。而是在异步调用发出之后,一般通过回调函数处理这个调用之后拿到结果。异步调用发出后,不会影响阻塞后面的代码执行,这样的情况称为异步。 下面来看一个例子: // 同步 function syncAdd(a, b) { return a + b; } syncAdd(1, 2) // 立即得到结果:3 // 异步 function asyncAdd(a, b) { setTimeout(function() { console.log(a + b); }, 1000) } asyncAdd(1, 2) // 1s后打印结果:3 这里定义了同步函数 syncAdd 和异步函数 asyncAdd,调用 syncAdd(1, 2) 函数时会等待得到结果之后再执行后面的代码。而调用 asyncAdd(1, 2) 时则会在得到结果之前继续执行,直到 1 秒后得到结果并打印。 我们知道,JavaScript 是单线程的,如果代码同步执行,就可能会造成阻塞;而如果使用异步则不会阻塞,不需要等待异步代码执行的返回结果,可以继续执行该异步任务之后的代码逻辑。因此,在 JavaScript 编程中,会大量使用异步。 那为什么单线程的JavaScript还能实现异步呢,其实也没有什么魔法,只是把一些操作交给了其他线程处理,然后采用了事件循环的机制来处理返回结果。 二、回调函数 在最基本的层面上,JavaScript的异步编程式通过回调实现的。回调的是函数,可以传给其他函数,而其他函数会在满足某个条件时调用这个函数。下面就来看看常见的不同形式的基于回调的异步编程。 1. 定时器 一种最简单的异步操作就是在一定时间之后运行某些代码。如下面代码: setTimeout(asyncAdd(1, 2), 8000) setTimeout()方法的第一个参数是一个函数,第二个参数是以毫秒为单位的时间间隔。asyncAdd()方法可能是一个回调函数,而setTimeout()方法就是注册回调函数的函数。它还代指在什么异步条件下调用回调函数。setTimeout()方法只会调用一次回调函数。 2. 事件监听 给目标 DOM 绑定一个监听函数,用的最多的是 addEventListener: document.getElementById('#myDiv').addEventListener('click', (e) => { console.log('我被点击了') }, false); 通过给 id 为 myDiv 的一个元素绑定了点击事件的监听函数,把任务的执行时机推迟到了点击这个动作发生时。此时,任务的执行顺序与代码的编写顺序无关,只与点击事件有没有被触发有关。 ...

October 1, 2024