360前端星计划学习笔记(五)正则的三个应用场景–王峰老师
360前端星计划学习笔记(六)NodeJS基础入门
字面量
const reg=/[a-z]\d+[a-z]/i
优点:①简单方便;②不需要考虑二次转义;
缺点:①子表达式无法复用②过长的正则可读性降低
使用RegExp构造函数
const alphabet='[a-z]'
const reg = new RegExp(`${alphabet}\\d+${alphabet}`,'i')
需要注意\d使用转义
优点:①子内容可以重复使用;②可以通过控制子内容的粒度提高可读性
缺点:二次转义的问题非常容易导致bug
const reg = new RegExp(`\d`)
reg.test('1')//false
reg.test('ddd')//true
RegExp.prototype.test()
const reg = /[a-z]\d+[a-z]/i;
reg.test('a1a'); // true
reg.test('1a1'); // false
reg.test(Symbol('a1a')); // TypeError
内容如果无法隐式转为字符串会抛出TypeError
RegExp.prototype.source和RegExp.prototype.flags
const reg = /[a-z]\d+[a-z]/ig;
reg.source; // "[a-z]\d+[a-z]"
reg.flags; // "gi"
RegExp.prototype.source
返回当前正则表达式的模式文本的字符串
RegExp.prototype.flags
es2015新增,返回当前正则表达式的修饰符的字符串,会对修饰符按照字母升序进行排序(gimsuy)
RegExp.prototype.exec()和String.prototype.match()
输入输出
输入:
RegExp.prototype.exec 要求输入字符串,遇到非字符串类型会尝试转换
String.prototype.match 要求输入正则表达式,遇到其它类型会先尝试转成字符串,再以字符串为 source 创建正则表达式
输出
匹配成功,返回匹配结果
匹配失败,返回 null
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
const reg1 = /(a)/g;
reg1.exec('a1a'); // ["a", "a", index: 0, input: "a1a", groups: undefined]
'a1a'.match(reg1); // ["a", "a"]
特殊:当正则表达式含有 g 修饰符时,RegExp.prototype.exec 每次只返回一个匹配结果,数据格式和不含 g 修饰符相同。
String.prototype.match 会返回所有的匹配结果,数据格式会变为字符串数组。
由于 String.prototype.match 返回的数据格式不固定,因此大多数情况都建议使用 RegExp.prototype.exec
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 的数字字符,常用的还有 a-z 匹配小写字母,\u4e00-\u9fa5 匹配汉字等,如果只是匹配数字,还可以使用字符集缩写 \d
+
限定符 一个或多个
该表达式缺点:不是全字符匹配,存在误判,如 /[0-9]+/.test(‘a1’) === true
^
匹配字符串开始位置,当结合 m 修饰符时,匹配某一行开始位置
$
匹配字符串结束位置,当结合 m 修饰符时,匹配某一行结束位置
该表达式缺点:
不能匹配带符号的数值,如+1,-2
不能匹配小数,如:3.14159
()
圆括号内是一个子表达式,当圆括号不带任何修饰符时,表示同时创建一个捕获组
?
?在正则中有多种含义,作为限定符时,表示匹配零到一个
\.
.可以匹配除换行符之外的任意字符,当结合s修饰符时,可以匹配包括换行符在内的任意字符
该表达式缺点:不能匹配无整数部分的小数,如.123会匹配失败;捕获组会有额外的开销
(?:)
创建一个非捕获组
*
限定符,匹配零个或多个
缺点:不能匹配无小数部分的数值;不能匹配科学计数法,如 1e2、3e-1、-2.e+4
/^[+-]?(?:\d+\.?|\d*\.\d+)(?:e[+-]?\d+)?$/i
const reg = /[+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?(?=px|\s|$)/gi;
function execNumberList(str) {
reg.lastIndex = 0;
let exec = reg.exec(str);
const result = [];
while (exec) {
result.push(parseFloat(exec[0]));
exec = reg.exec(str);
}
return result;
// let array=str.split(" ");
// res=array.map(item=>parseFloat(item,10));
// return res.length?res.join(','):null //不会用正则的我
}
console.log(execNumberList('1.0px .2px -3px +4e1px')); // [1, 0.2, -3, 40]
console.log(execNumberList('+1.0px -0.2px 3e-1px')); // [1, -0.2, 0.3]
console.log(execNumberList('1px 0')); // [1, 0]
console.log(execNumberList('-1e+1px')); // [-10]
(?=expression)
正向肯定环视 / 顺序肯定环视 / 先行断言
用于匹配符合条件的位置, 匹配到位置为止
类似的语法还有:
(?!expression) 正向否定环视 / 顺序否定环视 / 先行否定断言
(?<=expression) 反向肯定环视 / 逆序肯定环视 / 后行断言,es2018 新增
(?expression) 反向否定环视 / 逆序否定环视 / 后行否定断言,es2018 新增
g
全局匹配,用于取出目标字符串中所有符合条件的结果
需要注意的点:
数值转货币格式
//数值转货币
function formatCurrency(str) {
// ……
const reg=/(\d)(?=(\d{3})+(,|$))/g
return str.replace(reg,`$1,`)
}
// js方式
function formatCurrency1(str) {
// ……
let res=''
let count=0;
for(let i=str.length-1;i>=0;i--){
if(count==2&&i!=0){
res=','+str[i]+res;
count=-1;
}else{
res=str[i]+res
}
count++;
}
return res;
}
const reg = /(\d)(?=(?:\d{3})+(?:,|$))/g;
function formatCurrency(str) {
return str.replace(reg, '$1,');
}
{n}
限定符,表示重复 n 次,n 必须是非负整数
类似的语法还有:
{n, m} 表示重复 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, ',');
}
其它注意事项
环视中的圆括号也会生成捕获组,所以都要采用 (? 的非捕获组形式
其它注意事项**
环视中的圆括号也会生成捕获组,所以都要采用 (? 的非捕获组形式
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})$`);
其它注意事项
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 个捕获组
由于 r/g/b 必须同时为数值或百分比,所以 %? 只需要捕获一次,用 \1 来引用
\s字符集缩写,用于匹配空白
需要注意的点
更多颜色表示法:https://www.w3.org/TR/css-color/
//正则缩短16进制颜色值
const hex = '[0-9a-z]';
const hexReg = new RegExp(`^#(? ${hex})\\k(? ${hex})\\k(? ${hex})\\k(?${hex}?)\\k$`, 'i');
function shortenColor(str) {
return str.replace(hexReg, '#$$$$' );
}
console.log(shortenColor('#336600')); // '#360'
console.log(shortenColor('#19b955')); // '#19b955'
console.log(shortenColor('#33660000')); // '#3600'
(?)
const hex = '[0-9a-z]';
const hexReg = new RegExp(`^#(? ${hex})\\k(? ${hex})\\k(? ${hex})\\k(?${hex}?)\\k$`, 'i');
hexReg.exec('#33660000');
// ["#33660000", "3", "6", "0", "0", index: 0, input: "#33660000", groups: {r: "3", g: "6", b: "0", a: "0"}]
host: ‘www.360.cn’ 域名
hostname: ‘www.360.cn’, 域名不包括port
port: ‘’, 端口
pathname: ‘’, search前域名后
search: ‘’, ?。。。
hash: ‘’#。。。
//解析URL
const protocol = '(?https?:)' ;//具名捕获组
const host = '(?(?[^/#?:]+)(?::(?\\d+))?)' ;
const path = '(?(?:\\/[^/#?]+)*\\/?)' ;
const search = '(?(?:\\?[^#]*)?)' ;
const 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: '',
};
}
console.log(execURL('https://www.360.cn'));
console.log(execURL('http://localhost:8080/?#'));
console.log(execURL('https://image.so.com/view?q=360&src=srp#id=9e17bd&sn=0'));
console.log(execURL('this is not a url'));
注意事项
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;
}
console.log(execUrlParams('#')); // { }
console.log(execUrlParams('##')); // { '#': '' }
console.log(execUrlParams('?q=360&src=srp')); // { q: '360', src: 'srp' }
console.log(execUrlParams('test=a=b=c&&==&a=')); // { test: 'a=b=c', '': '=', a: '' }
?
可以跟在任何限定符之后,表示非贪婪模式(注意:这个例子其实不太恰当,使用贪婪模式效果是一样的)
const reg = /(?:^|&)([^&=]*)=?([^&]*?)(?=&|$)/y;
y
es6 新增,粘连修饰符,和 g 修饰符类似,也是全局匹配。区别在于:
其它注意事项
正则表达式如果可能匹配到空字符串,极有可能造成死循环,所以这段代码很重要:
if (!str) { return result; }
或者使用:lastIndex去判断值有没有变化,结果没往后走,如果没变就跳出。
作业:
function getUrlParam(str, key) {
//
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[key] || '';
}
console.log(getUrlParam('?nothing', 'test')); // ''
console.log(getUrlParam('#a=1&aa=2&aaa=3', 'a')); // '1'
console.log(getUrlParam('&b=1&a=1&b=2', 'b')); // '2'
console.log(getUrlParam('a=1&b=2&c=&d', 'c')); // ''
console.log(getUrlParam('&d==', 'd')); // '='
注意事项
存在多个重复的 key 时,要求只返回最后一条匹配的结果
挑战1:解法不止一种,你可以写出尽可能多的解法吗?
挑战2:可以写出尽可能短的正则表达式吗?
未完待续。。。
明确需求
考虑全面
反复测试
与JavaScript的区别:
基于异步I/O相关接口
基于node——moudules和require 模块依赖
提供C++ addon API
可以做什么?
Web
IOT
Puppeteer sandbox
require/exports
.js
.json
.node
.mjs
.cjs(common.js 导入导出形式)
require
同步拿到文件内容,对内容进行闭包包装,会传入exports,require,module等
通过在虚拟沙盒中执行,
读取缓存对象
包管理:
main:指定入口文件(默认根目录index.js文件)
scripts 脚本执行的快捷方式
五个依赖家族角色
bin: 可以指定:k-v 快捷执行对应的文件
samver-version 小版本:bug 补丁 中版本新特性 大版本 代码重构,和之前版本有冲突或不兼容
registry 代理,指定源
^1.2.2允许中小版本的更新
~0.7.0(0.7.x)接收小版本更新 可以接收0.7.6 不能接收0.8.0
*
接收任意版本
不带任何,只该版本
速度问题
安全问题(投毒,删了依赖其他包无法使用等)
const http=require('http')
const server = http.createServer((req.res)=>{
res.end("Hello world");
})
server.listen(3000)
中间件挂载
用户请求像流水线一样被中间件一个一个执行修改,
源码:4个文件
use方法
原理:middleware提供接口,处理
企业级目录规范
数据校验:Logic机制专门用来支持数据校验(用户校验,权限校验)
文件和Action与controller 一一对应
todo:实际建立工程
https://zhuanlan.zhihu.com/p/41315709
日志调试 console.log()
断点调试
node --inspect NodeJS 6.3+ 在chrome\\inspect
vscode F5
ndb(好处,可以接在任意命令启动调试,在ndb可以编写修改,可以存储到文件,可以边调试边修改)
前端
服务端
+- ↩︎
+- ↩︎