JavaScript忍者秘籍 正则、代码模块化、DOM操作、事件循环

第十章 正则

创建正则表达式
1. 使用正则表达式字面量 -优先

const pattern = /^\d{5}-\d{4}$/;

2. 通过创建RegExp对象的实例

const pattern = new RegExp("^\d{5}-\d{4}$");

正则表达式的编译阶段发生在表达式创建时期,表达式经过js引擎的解析,转换成内部代码。执行阶段发生在使用编译之后的正则表达式进行匹配字符串的时期。

修饰符
i:对大小写不敏感
g:查找所有匹配项,在查询到第一个匹配项后继续寻找其他匹配项
m:允许多行匹配
y:开启粘连匹配,执行粘连匹配时,试图从最后一个匹配配置开始
u:允许使用unicode点转义符

操作符
1. 匹配字符集
[abcdef]:匹配a-f中的任意字符 也可表示为[a-f]
[^abc]:匹配除a-c中的任意字符
2. 转义 \
3. 起止符号 
/^test/ :以test开头
/test$/ :以test结尾
4. 重复出现
/t?est/:可出现0次或1次,可以同时匹配 test、est
/t+est/:可出现1次或多次,可匹配 test、ttest、tttest...
/t*est/:可出现0次或1次或多次
/a{5}:指定重复次数,可匹配aaaaa
/a{4,10}:指定循环次数范围4-10次,可匹配aaaa、aaaaa...
/a{4,}:指定开放区间,匹配4次或以上
5. 分组 ()
/(ab)+/:匹配一个或多个连续的ab
6. or |
/(ab)+|(cd)+/
7. 反向引用
反向引用分组中捕获的内容,使用反斜线加数字表示引用,数字从1开始,第一个分组捕获的\1,第二个为\2...
/<(\w+)>(.+)<\/\1>/  匹配<strong>whatever</strong>


第十一章 代码模块化

小的、组织良好的代码比庞大的代码更容易理解、容易维护。小的、耦合相对松散的片段称之为模块。
模块是比对象和函数更大的代码单元。
优点: 
1. 避免琐碎细节干扰
2. 在应用程序的不同部分更容易复用模块的功能
3. 极大提高应用程序的开发效率

模块级别的要求
1. 定义模块接口,通过接口调用模块
2. 隐藏模块的内部实现

在js es6 之前,使用对象、闭包和立即执行函数实现模块
通过立即执行函数,隐藏模块执行细节,通过添加对象和闭包,定义模块接口。

模块模式
使用函数扩展模块,使用对象实现接口
缺点:扩展的模块无法共享原有模块的内部属性export、import -ES6
ES6模块的主要思想是 必须显式的使用标识符导出模块,才能从外部访问模块。其他标识符,甚至在最顶级作用域中定义的标识符(全局作用域),只能在模块中使用。
export 从模块外部指定标识符

//Ninja.js
//export关键字导出变量或函数
const ninja = "Yoshi";
export const message = "Hello";
export function sayHiToNinja() { //指定导出名称sayHiToNinja
    return message + " " + ninja;
}

//最后一行导出
const ninja = "Yoshi";
const message = "Hello";
function sayHiToNinja() {
    return message + " " + ninja;
}
export { message, sayHiToNinja };//将所有模块标识符导出

import 导入模块标识符
//使用关键字import从模块中导入标识符
import { message, sayHiToNinja} from "ninja.js"; //导入指定名称
import * as ninjaModule from "ninja.js" //导入全部
import importedNinja from "ninja.js" //导入模块默认导入的内容,并指定名称importedNinja

export default 默认导出
export default class Ninja{} 

export与import是使用重命名
export {sayHi as sayHello} ;//关键字设置别名
import {sayHello} from "Greetings.js"

第十二章 DOM操作
向DOM中注入HTML步骤
1. 将HTML字符串转换为DOM

HtmlToDom

2. 将DOM插入到文档中

DOM属性操作
访问元素的特性值 getAttribute、setAttribute
注意:页面元素上放置的自定义特性不能自动被元素属性标识,访问这些自定义属性值需要使用getAttribute或setAttribute样式属性

elem.style.xxx ; //仅可以访问元素本身style属性中的值,不能获取继承样式(<style></style>中标识的)

const div = document.querySelector("div");
div.style["fontSize"];
div.style.fontSize;
const divStyle = getComputedStyle(div); //使用内置的getComputedStyle方法获取描述对象,可以获取继承样式!!
divStyle.getPropertyValue("font-size");

以上三种style属性获取方法均可

注意: 
元素style特性指定的样式优先级永远高于继承的样式,即便继承的样式包含!important

高度、宽度
offsetHeight、offsetWidth:实际元素的高度和宽度,包含了元素的padding值

如何获取隐藏元素尺寸?
1. 将元素display属性设置block。以便获取offsetHeight、offsetWidth值,但元素会变成可见
2. 将visiblity设置为hidden。使元素不可见
3. 将position设置为absolute。以便将元素移出正常的可视区。
4. 获取元素尺寸
5. 恢复更改属性

布局抖动
改变一个元素的特性(或修改内容)时,不一定只影响该元素,还可能导致级联变化。
解决办法:
1. 批量DOM读取和写入,以免布局抖动
2. 第三方库,FastDom等
 

1


第十三章 事件循环

事件循环不仅包括事件队列,而是具有至少两个队列,除了事件,还要保持浏览器执行的其他操作——任务。
宏任务队列
包括创建主文档对象、解析HTML、执行主线js代码、更改当前url以及各种事件如页面加载、输入、网络事件和定时器事件等。从浏览器角度看,宏任务代表一个个离散的、独立工作单元,运行完任务后,浏览器可以继续其他调度,如重新渲染页面的UI或执行垃圾回收。
微任务队列
微任务是更小的任务,更新应用程序的状态,但必须在浏览器任务继续执行其他任务之前执行,浏览器任务包括重新渲染也页面的UI。包括promise回调函数、DOM发送变化等。

事件循环的两个基本原则:
1. 一次处理一个任务
2. 一个任务开始后直到运行结束,不会被其他任务中断

宏队列、微队列


事件循环首先检查宏任务队列,如果宏任务等待,立即执行宏任务,直到任务完成或队列为空,事件循环将移动去处理微任务队列。如果微任务队列有等待任务,则开始执行微任务,直至队列为空。
微任务、宏任务之间的区别:
单次循环迭代中,最多处理一个宏任务(其余在队列中等待),而队列中的所有微任务都会被处理。
微任务队列处理完成并清空时,事件循环会检查是否更新ui渲染。事件循环结束,继续检查宏队列,开始下面的循环。
浏览器通常会尝试每秒渲染60次页面,以达到每秒60帧的速度,这个速度是检验是否平滑流畅的标准。

仅宏任务队列

仅宏任务队列


宏任务、微任务队列
 

宏任务、微任务队列

计时器
计时器能延迟一段代码的执行,延迟时长至少是指定的时长ms。

创建计时器
setTimeout(func,delay):启动计时器,在指定的延迟时间结束时执行一次回调函数,返回标识计时器的唯一值
clearTimeout(id):指定定时器尚未触发时,取消定时器
setInterval(func,delay):启动计时器,按照指定的延迟间隔不断执行回调函数,直至取消。返回标识计算器的唯一值
clearInterval(id):取消计时器

setTimeout与setInterval的区别

setTimeout(function repeatMe() { setTimeout(repeatMe, 10); }, 10);
setInterval(() => { }, 10); 

理论上都是每10s调用一次,但是setIimeout内的代码在前一个回调函数执行完成后,至少延迟10ms执行(取决于事件队列的状态,等待时间只会大于10ms);而setInterval会尝试每10ms执行回调函数,不关心前一个回调函数是否执行。
当超过时间结束时,无法保证超时回调精准执行。不是像间隔函数那样每10ms触发一次,它是重新安排每10ms后执行。

setTimeout(func(),0); 

设置0为超时时间,并不意味着在0ms时执行回调,而是意味着通知浏览器尽快执行回调,但与其他微任务不同,在回调之前可以执行页面渲染,允许浏览器更新ui,使得web应用程序交互性更强。

事件处理方式顺序:
netscape与microsoft做出了相反的选择,netscape事件模型中,事件处理器从顶部元素开始,直到事件目标元素;而microsoft则先从目标元素开始,按DOM树向上冒泡,所以w3委员会设立了标准,同时包含这两种方式。即:
1. 捕获:首先被顶部元素捕获,并依次向下传递
2. 冒泡:目标元素捕获后,事件处理转向冒泡,从目标元素向顶部元素冒泡

捕获与冒泡

捕获与冒泡

const outerContainer = document.getElementById("outerContainer");
const innerContainer = document.getElementById("innerContainer");

document.addEventListener("click", () => {
  report("Document click");
});//如果没有指定第三个参数,默认启用冒泡模式

outerContainer.addEventListener("click", () => {
  report("Outer container click");
}, true); //第三个参数传入true,启用捕获模式

innerContainer.addEventListener("click", () => {
  report("Inner container click");
}, false); //第三个参数传入false,启用冒泡模式

捕获、冒泡图

点击innerContainer会出现如下调用顺序:
Outer container click
Inner container click
Document click

简要图


一个事件可以触发多次事件处理器的执行,每个事件处理器可以是捕获或冒泡模式。事件首先通过捕获,从顶部元素传递到目标元素!!!到达目标元素时,激活冒泡模式,从目标元素传回到顶部元素。
1.捕获模式,从window开始向下寻找具有捕获模式的单击处理器元素——找到outer 
2.继续寻找捕获模式的单机处理器元素 ——未查询到匹配元素
3.处理捕获模式outer的下一个元素inner转为冒泡模式 ——找到inner
4.冒泡模式,从inner向上寻找具有冒泡模式的单击处理器元素 ——找到window

为什么添加任务到任务队列中必须在事件循环之外?
若添加任务到任务队列的过程是事件循环的一部分,当js代码执行时发生的事件都将被忽略。

为什么每一次事件循环的迭代不应超过16ms?
为了实现应用程序平稳运行,浏览器试图每秒大约渲染60次,因为渲染在事件循环结束时执行,每个迭代就不应该超过16ms或更长事件,不然就会造成卡顿、缓慢等问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值