08 - 正则的三个使用场景
正则表达式的创建和使用
创建正则表达式
1.使用正则表达式字面量
const reg = /[a-z]\d+[a-z]/i
- 优点
- 简单方便
- 不需要考虑二次转义
- 缺点
- 子内容无法重复使用
- 过长的正则导致可读性差
2.使用RegExp构造函数
const alphabet = '[a-z]';
const reg = new RegExp(`${alphabet}\\d+${alphabet}`, 'i'); //内容,修饰符
- 优点
- 子内容可以重复使用
- 可以通过控制子内容的粒度提高可读性
- 缺点
- 二次转义非常容易导致bug
const reg = new RegExp(`\d+`); //reg = /d+/
reg.test('1'); // false
reg.test('ddd'); // true
正则表达式的常见用法
1.RegExp.prototype.test()
const reg = /[a-z]\d+[a-z]/i;
reg.test('a1a'); // true
reg.test('1a1'); // false
reg.test(Symbol('a1a')); // TypeError
2.RegExp.prototype.source 和 RegExp.prototype.flags
const reg = /[a-z]\d+[a-z]/ig;
reg.source; // "[a-z]\d+[a-z]"
reg.flags; // "gi"
-
source返回当前正则表达式的模式文本的字符串
-
flags返回当前正则表达式的修饰符的字符串,按字母升序排序(gimsuy)
3.RegExp.prototype.exec() 和 String.prototype.match()
const reg = /[a-z]\d+[a-z]/i;
reg.exec('a1a'); // ["a1a", index: 0, input: "a1a", groups: undefined]
reg.exec('1a1'); // null
'a1a'.match(reg); // ["a1a", index: 0, input: "a1a", groups: undefined]
'1a1'.match(reg); // null
-
RegExp.prototype.exec 要求输入字符串,遇到非字符串类型会尝试转换
-
String.prototype.match 要求输入正则表达式,遇到其它类型会先尝试转成字符串,再以字符串为 source 创建正则表达式
-
匹配成功,返回匹配结果
-
匹配失败,返回 null
-
当正则表达式含有 g 修饰符时,RegExp.prototype.exec 每次只返回一个匹配结果,数据格式和不含 g 修饰符相同。
-
String.prototype.match 会返回所有的匹配结果,数据格式会变为字符串数组。
-
由于 String.prototype.match 返回的数据格式不固定,因此大多数情况都建议使用 RegExp.prototype.exec
4.RegExp.prototype.lastIndex
const reg = /(a)/g;
const str = 'a1a';
reg.lastIndex; // 0
reg.exec('a1a'); // ["a", "a", index: 0, input: "a1a", groups: undefined]
reg.lastIndex; // 1
reg.exec('a1a'); // ["a", "a", index: 2, input: "a1a", groups: undefined]
reg.lastIndex; // 3
reg.exec('a1a'); // null
reg.lastIndex; // 0
-
当前正则表达式最后一次匹配成功的结束位置(也就是下一次匹配的开始位置)
-
注意:lastIndex 不会自己重置,只有当上一次匹配失败才会重置为 0 ,因此,当你需要反复使用同一个正则表达式的时候,请在每次匹配新的字符串之前重置 lastIndex!
5.String.prototype.replace()、String.prototype.search()、String.prototype.split()
'a1a'.replace(/a/, 'b'); // 'b1a'
'a1a'.replace(/a/g, 'b'); // 'b1b'
'a1a'.search(/a/); // 0
'a1a'.search(/a/g); // 0
'a1a'.split(/a/); // ["", "1", ""]
'a1a'.split(/a/g); // ["", "1", ""]
正则与数值
数值判断
/[0-9]+/
-
不是全字符匹配,存在误判,如 /[0-9]+/.test(‘a1’) === true
-
[0-9]、\d匹配数字
-
[a-z] 匹配小写字母
-
[\u4e00-\u9fa5] 匹配汉字
-
+ 匹配一个或多个
/^\d+$/
-
不能匹配带符号的数值,不能匹配小数
-
^ 匹配字符串开始位置,当结合 m 修饰符时,匹配某一行开始位置
-
$ 匹配字符串结束位置,当结合 m 修饰符时,匹配某一行结束位置
/^[+-]?\d+(\.\d+)?$/
-
不能匹配无整数部分的小数,捕获组会带来额外开销
-
() 捕获组
-
? 作为限定符时表示匹配0到1个
-
. 匹配除换行符外任意字符
/^[+-]?(?:\d*\.)?\d+$/
-
不能匹配无小数部分的数值(2.),不能匹配科学计数法
-
(?:)
非捕获组 -
* 匹配0个或多个
完整的数值正则
这个 token 是 CSS 的 token,在 javascript 中,要多考虑一种情况
+'2.'; // 2
+'2.e1'; // 20
/^[+-]?(?:\d+\.?|\d*\.\d+)(?: e[+-]?\d+)?$/i
- | 创建分支,或
- i 修饰符 匹配时忽略大小写
数值的解析
const reg = /[+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?(?=px|\s|$)/gi;
- x(?=y) 仅匹配被y跟随的x
- x(?!y) 仅匹配不被y跟随的x
- (?<=y)x x只有在y后面才匹配
- (?<!y)x x只有不在y后面才匹配
数值转货币格式
const reg = /(\d)(?=(\d{3})+(,|$))/g;
function formatCurrency(str) {
return str.replace(reg, '$1,');
}
- {n} 表示重复n次,n必须是非负整数
- {n, m} 表示重复n到m次,n <= m
- {n,} 表示重复n次以上
- $n 用于replace时,表示第n个捕获组,n可以从1到9
- $& 表示本次完整的匹配
const reg = /\d(?=(?:\d{3})+(?:,|$))/g;
function formatCurrency(str) {
return str.replace(reg, '$&,');
}
在es2018以上环境,可以使用反向环视
const reg = /(?<=\d)(?=(?:\d{3})+(?:,|$))/g;
function formatCurrency(str) {
return str.replace(reg, ',');
}
- 环视中的圆括号也会生成捕获组,所以都要采用
(?:)
的非捕获组形式
正则与颜色
16进制表示法
color: #rrggbb;
color: #rgb;
color: #rrggbbaa;
color: #rgba;
正则
const hex = '[0-9a-fA-F]';
const reg = new RegExp(`^(?:#${hex}{6}|#${hex}{8}|#${hex}{3,4})$`);
rgb/rgba表示法
color: rgb(r, g, b);
color: rgb(r%, g%, b%);
color: rgba(r, g, b, a);
color: rgba(r%, g%, b%, a);
color: rgba(r, g, b, a%);
color: rgba(r%, g%, b%, a%);
正则
const num = '[+-]?(?:\\d*\\.)?\\d+(?:e[+-]?\\d+)?';
const comma = '\\s*,\\s*';
const reg = new RegExp(`rgba?\\(\\s*${num}(%?)(?:${comma}${num}\\1){2}(?:${comma}${num}%?)?\\s*\\)`);
- \n 反向引用,表示引用第n个捕获组
- \s 匹配空白符
- rgb(r, g, b, a)和rgba(r, g, b)是合法的
- 当捕获组内的内容是可选的时候,一定要把问号写在捕获组内
- 如果可选内容的圆括号不可省略,如(a|b|c)?,应该多嵌套一层:((?:a|b|c)?)
hsl/hsla表示法、颜色名表示法
/* hsl & hsla */
color: hsl(h, s%, l%);
color: hsla(h, s%, l%, a);
color: hsla(h, s%, l%, a%);
/* keywords */
color: red;
color: blue;
/* …… */
正则处理颜色
const hex = '[0-9a-z]';
const hexReg = new RegExp(`^#(?<r>${hex})\\k<r>(?<g>${hex})\\k<g>(?<b>${hex})\\k<b>(?<a>${hex}?)\\k<a>$`, 'i');
function shortenColor(str) {
return str.replace(hexReg, '#$<r>$<g>$<b>$<a>');
}
console.log(shortenColor('#336600')); // '#360'
console.log(shortenColor('#19b955')); // '#19b955'
console.log(shortenColor('#33660000')); // '#3600'
- (?<key>)
- 具名捕获组
- 反向引用时语法为 \k<key>
- replace时,使用$<key>访问具名捕获组
- 应用exec时,具名捕获组可以通过 execResult.groups[key] 访问
正则与URL
完整URL规范
解析URL
const protocol = '(?<protocol>https?:)';
const host = '(?<host>(?<hostname>[^/#?:]+)(?::(?<port>\\d+))?)';
const path = '(?<pathname>(?:\\/[^/#?]+)*\\/?)';
const search = '(?<search>(?:\\?[^#]*)?)';
const hash = '(?<hash>(?:#.*)?)';
const reg = new RegExp(`^${protocol}\/\/${host}${path}${search}${hash}$`);
function execURL(url) {
const result = reg.exec(url);
if (result) {
result.groups.port = result.groups.port || '';
return result.groups;
}
return {
protocol: '', host: '', hostname: '', port: '',
pathname: '', search: '', hash: '',
};
}
解析search和hash
function execUrlParams(str) {
str = str.replace(/^[#?&]/, '');
const result = {};
if (!str) {
return result;
}
const reg = /(?:^|&)([^&=]*)=?([^&]*?)(?=&|$)/y;
let exec = reg.exec(str);
while (exec) {
result[exec[1]] = exec[2];
exec = reg.exec(str);
}
return result;
}
- *? ?跟在任意限定符之后,表示非贪婪模式
- y 粘连修饰符,全局匹配
- y 修饰符每次匹配的结果必须是连续的
- y 修饰符在 match 时只会返回第一个匹配结果
09/10 - Node.js基础入门
Node.js与JavaScript的区别
- 基于异步 I/O 相关接口
- 基于 node_modules 和 require 的模块依赖
- 提供 C++ addon API 与系统交互
Node.js可以干什么
- Web 服务端:Web Server、爬虫
- CLI 命令行脚本:webpack
- GUI 客户端软件:VSCode、网易云音乐
- IoT, 图像处理, 实时通讯,加密货币…
模块
- 内置模块:编译进 Node 中,例如 http fs net process path 等
- 文件模块:原生模块之外的模块,和文件(夹)一一对应
- 模块类型:.js, .json, .node, .mjs, …
模块路径查找
- 绝对路径
- 相对路径
- 和当前路径处理为绝对路径
- 模块/文件夹
- 原生模块,直接读取缓存
- [$NODE_PATH, ~/.node_modules,
./node_modules, …/node_modules, …] - 解析 package.json,查找 main 属性,没有则使用 index.js
- 如果未找到,则报错
模块缓存
- 模块加载后会将返回值缓存起来
- 下次加载时直接读取缓存结果,避免文件 I/O 和解析时间
- 导出对象缓存在 Module._cache 对象上
NPM(Node Package Manager)
包管理
- 一个package.json文件应该存在于包顶级目录下
- 二进制文件应该包含在bin目录下
- JavaScript代码应该包含在lib目录下
- 文档应该在doc目录下
- 单元测试应该在test目录下
基于Node.js的Web开发
Koa 无规范约束,不利于团队开发,中间件繁多,质量参差不齐,选择困难
Thinkjs
Node.js调试
- 日志调试
- 断点调试
- node --inspect
- vscode
- ndb
Node开发角色转换
-
前端
- 跟浏览器打交道,兼容性问题
- 组件化
- 加载速度、JS 执行性能、渲染性能
- 错误监控
- XSS、CSRF 等安全漏洞
-
服务端
- 数据库、Redis 等周边服务
- 性能、内存泄露、CPU、机器管理
- 服务监控、错误监控、流量监控、报警
- SQL注入、目录遍历等安全漏洞