④\W与$之间的位置
同样还是用学习教程
文件夹中的种子,稍稍改造一下,当执行这行代码之后,会输出啥?
‘[[xxx_love_study_1.mp4]]’.replace(/\B/g, ‘❤️’)
…
没错,满满的都是爱啊!!!,都快看不清名字了。
❤️[❤️[x❤️x❤️x❤️_❤️l❤️o❤️v❤️e❤️_❤️s❤️t❤️u❤️d❤️y❤️_❤️1.m❤️p❤️4]❤️]❤️
画图解释如下
image.png
(?=p)
符合p子模式前面的那个位置。换句话说是,有一个位置,紧跟其后需要满足p子模式。也有一个学名叫正向先行断言。
还是这个例子xxx_love_study_1.mp4
,要在xxx(xxx可以指代任何你喜欢的那个TA)前面塞一个❤️,怎么写呢?
是这样吗?不是的,这样会导致你的xxx都不见了,那还要❤️做什么呢?
‘xxx_love_study_1.mp4’.replace(‘xxx’, ‘❤️’) // ❤️_love_study_1.mp4
利用(?=p)就可以很方便这这件事(可以想想和上面有什么不同?)
‘xxx_love_study_1.mp4’.replace(/(?=xxx)/g, ‘❤️’) // ❤️xxx_love_study_1.mp4
画图理解
image.png
(?!p)
(?=p)反过来的意思,可以理解为(?=p)匹配到的位置之外的位置都是属于(?!p)的,它也有一个学名叫负向先行断言。
‘xxx_love_study_1.mp4’.replace(/(?!xxx)/g, ‘❤️’)
// (?=xxx)的输出
❤️xxx_love_study_1.mp4
// (?!xxx)的输出
x❤️x❤️x❤️_❤️l❤️o❤️v❤️e❤️_❤️s❤️t❤️u❤️d❤️y❤️_❤️1❤️.❤️m❤️p❤️4❤️
仔细对比一下,是不是除了(?=xxx)匹配到最前面那个位置,其他位置都是(?!xxx)匹配到的啦。
(?<=p)
符合p子模式后面(注意(?=p)表示的是前面)的那个位置。换句话说是,有一个位置,其前面的部分需要满足p子模式。
依然是这个例子:我们要在xxx(xxx可以指代任何你喜欢的那个TA)的后面塞一个❤️,怎么写呢?
‘xxx_love_study_1.mp4’.replace(/(?<=xxx)/g, ‘❤️’) //xxx❤️_love_study_1.mp4
画图解释
image.png
(?<!p)
(?<=p)反过来的意思,可以理解为(?<=p)匹配到的位置之外的位置都是属于(?<!p)的,
‘xxx_love_study_1.mp4’.replace(/(?<!xxx)/g, ‘❤️’)
// (?<=xxx)的输出
xxx❤️_love_study_1.mp4
// (?<!xxx)的输出
❤️x❤️x❤️x_❤️l❤️o❤️v❤️e❤️_❤️s❤️t❤️u❤️d❤️y❤️_❤️1❤️.❤️m❤️p❤️4❤️
仔细对比一下,是不是除了(?<=xxx)匹配到后面那个位置,其他位置都是(?<!xxx)匹配到的啦。
栗子详解
学习完位置相关的知识,我们来做一下开头的几个题目试试
题目1:数字的千分位分割法
将123456789转化为123,456,789
观察题目的规律就是从后往前,每三个数字前加一个逗号,(需要注意的是开头不需要加逗号,)
。是不是很符合 (?=p)的规律呢?p可以表示每三个数字,要添加的逗号所处的位置正好是(?=p)匹配出来的位置。
第一步,尝试先把后面第一个逗号弄出来
let price = ‘123456789’
let priceReg = /(?=\d{3}$)/
console.log(price.replace(priceReg, ‘,’)) // 123456,789
第二步,把所有的逗号都弄出来
要把所有的逗号都弄出来,主要要解决的问题是怎么表示三个数字一组
,也就是3的倍数。我们知道正则中括号可以把一个p模式变成一个小整体,所以利用括号的性质,可以这样写
let price = ‘123456789’
let priceReg = /(?=(\d{3})+$)/g
console.log(price.replace(priceReg, ‘,’)) // ,123,456,789
第三步,去掉首位的逗号,
上面已经基本上实现需求了,但是还不够,首位会出现,那怎么把首位的逗号去除呢?想想前面是不是有一个知识正好满足这个场景?没错(?!p),就是他了,两者结合就是从后往前每三个数字的位置前添加逗号,但是这个位置不能是^首位。
let price = ‘123456789’
let priceReg = /(?!^)(?=(\d{3})+$)/g
console.log(price.replace(priceReg, ‘,’)) // 123,456,789
题目2:手机号3-4-4分割
将手机号18379836654转化为183-7983-6654
有了上面数字的千分位分割法,做这个题相信会简单很多,也就是从后往前找到这样的位置:
每四个数字前的位置,并把这个位置替换为-
let mobile = ‘18379836654’
let mobileReg = /(?=(\d{4})+$)/g
console.log(mobile.replace(mobileReg, ‘-’)) // 183-7983-6654
题目3:手机号3-4-4分割扩展
将手机号11位以内的数字转化为3-4-4格式
回想一下这样的场景,有一个表单需要收集用户的手机号,用户是一个个数字输入的,我们需要在用户输入11位手机号的过程中把其转化为3-3-4格式。即
123 => 123
1234 => 123-4
12345 => 123-45
123456 => 123-456
1234567 => 123-4567
12345678 => 123-4567-8
123456789 => 123-4567-89
12345678911 => 123-4567-8911
这样用(?=p)就不太合适了,例如1234就会变成-1234。想想前面的知识点有适合处理这种场景的吗?是的(?<=p)
第一步, 将第一个-弄出来
const formatMobile = (mobile) => {
return String(mobile).replace(/(?<=\d{3})\d+/, ‘-’)
}
console.log(formatMobile(123)) // 123
console.log(formatMobile(1234)) // 123-
将第二个-弄出来
将第一个-弄出来之后,字符的长度多了一位,原本1234567(这个位置插入-)8,要变成往后移一位
const formatMobile = (mobile) => {
return String(mobile).slice(0,11)
.replace(/(?<=\d{3})\d+/, ($0) => ‘-’ + $0)
.replace(/(?<=[\d-]{8})\d{1,4}/, ($0) => ‘-’ + $0)
}
console.log(formatMobile(123)) // 123
console.log(formatMobile(1234)) // 123-4
console.log(formatMobile(12345)) // 123-45
console.log(formatMobile(123456)) // 123-456
console.log(formatMobile(1234567)) // 123-4567
console.log(formatMobile(12345678)) // 123-4567-8
console.log(formatMobile(123456789)) // 123-4567-89
console.log(formatMobile(12345678911)) // 123-4567-8911
题目4:验证密码的合法性
密码长度是6-12位,由数字、小写字符和大写字母组成,但必须至少包括2种字符
题目由三个条件组成
① 密码长度是6-12位
② 由数字、小写字符和大写字母组成
③ 必须至少包括2种字符
第一步写出条件①和②和正则
let reg = /1{6,12}$/
第二步,必须包含某种字符(数字、小写字母、大写字母)
let reg = /(?=.*\d)/
// 这个正则的意思是,匹配的是一个位置,这个位置需要满足任意数量的符号,紧跟着是个数字
,注意它最终得到的是个位置,而不是数字或者是数字前面有任意的东西
console.log(reg.test(‘hello’)) // false
console.log(reg.test(‘hello1’)) // true
console.log(reg.test(‘hel2lo’)) // true
// 其他类型同理
第三步,写出完整的正则
必须包含两种字符,有下面四种排列组合方式
① 数字和小写字母组合
② 数字和大写字母组合
③ 小写字母与大写字母组合
④ 数字、小写字母、大写字母一起组合(但其实前面三种已经覆盖了第四种了)
// 表示条件①和②
// let reg = /((?=.\d)((?=.[a-z])|(?=.*[A-Z])))/
// 表示条件条件③
// let reg = /(?=.[a-z])(?=.[A-Z])/
// 表示条件①②③
// let reg = /((?=.\d)((?=.[a-z])|(?=.[A-Z])))|(?=.[a-z])(?=.*[A-Z])/
// 表示题目所有条件
let reg = /((?=.\d)((?=.[a-z])|(?=.[A-Z])))|(?=.[a-z])(?=.*[A-Z])2{6,12}$/
console.log(reg.test(‘123456’)) // false
console.log(reg.test(‘aaaaaa’)) // false
console.log(reg.test(‘AAAAAAA’)) // false
console.log(reg.test(‘1a1a1a’)) // true
console.log(reg.test(‘1A1A1A’)) // true
console.log(reg.test(‘aAaAaA’)) // true
console.log(reg.test(‘1aA1aA1aA’)) // true
2. 字符串匹配原来这么简单
===============
两种模糊匹配
正则如果只有精确匹配,那么便完全没有了意义
横向
一个正则可匹配的字符串的长度不是固定的,可以是多种情况,通过量词+、*、?、{m,n},可实现横向匹配
let reg = /ab{2,5}c/g
let str = ‘abc abbc abbbc abbbbc abbbbbc abbbbbbc’
str.match(reg) // [ ‘abbc’, ‘abbbc’, ‘abbbbc’, ‘abbbbbc’ ]
纵向
一个正则匹配的字符串,具体到某一位字符时,可以不是某个确定的字符串,可以有多种可能,实现方式是字符组( 其实多选分支|也可以实现 )
let reg = /a[123]b/g
let str = ‘a0b a1b a2b a3b a4b’
str.match(reg) // [ ‘a1b’, ‘a2b’, ‘a3b’ ]
字符组
不要被名字给糊弄了,虽然他叫做字符组,但其实只是代表一个字符的可能性
范围表示法
[123456abcdefABCDEF] => [1-6a-fA-F]
排除字符组
某位字符可以是任何东西,但是就是不能是xxx, 使用^符号
问题:如何要表示除了某个单词之外的任意东西呢?
[^abc]
常见简写形式
\d // 数字
\D // 非数字
\w // [0-9a-zA-Z_]
\W // [^0-9a-zA-Z_]
\s // [\t\v\n\r\f]
\S // [^\t\v\n\r\f]
.
量词
–
量词 & 简写
1. {m,} // 至少出现m次
2. {m} // 出现m次
3. ? // 出现0次或者1次,等价于{0,1}
4. + // 至少出现1次,等价于{1,}
5. * // 出现人一次,等价于{0,}
贪婪匹配 VS 惰性匹配
正则本身是贪婪的,会尽可能的多匹配符合模式的字符
let regex = /\d{2,5}/g
let string = ‘123 1234 12345 123456’
// 贪婪匹配
// string.match(regex) // [ 123, 1234, 12345, 12345 ]
// 惰性匹配
let regex2 = /\d{2,5}?/g
// string.match(regex) // [ 12, 12, 34, 12, 34, 12, 34, 56 ]
量词后面加一个?,即变成了惰性匹配
贪婪量词 惰性量词
{m,n} {m,n}?
{m,} {m,}?
? ??
+ +?
* *?
多选分支
一个模式可以实现横向和纵向的模糊匹配,而多选分支可以支持多个子模式任选其一,形式是(p1|p2|p3)
let regex = /good|nice/g
let string = ‘good idea, nice try.’
// string.match(regex) // [ ‘good’, ‘nice’ ]
// 注意,用/good|goodbye/去匹配’goodbye’ 匹配到的是good
// 因为分支结构是惰性的,前面的匹配上了,后面的就不再尝试了
案例分析
1.匹配id
// 1
let regex = /id=“.*?”/ // 想想为什么要加? 不加的话 连后面的class都会匹配到
let string = ‘
’;console.log(string.match(regex)[0]);
// 2
let regex = /id=“[^”]*"/
let string = ‘
’;console.log(string.match(regex)[0]);
2.匹配16进制的颜色值
// 要求匹配如下颜色
/*
#ffbbad
#Fc01DF
#FFF
#ffE
*/
let regex = /#([a-fA-F\d]{6}|[a-fA-F\d]{3})/g
let string = “#ffbbad #Fc01DF #FFF #ffE”;
console.log(string.match(regex))
// [“#ffbbad”, “#Fc01DF”, “#FFF”, “#ffE”]
3.匹配24小时制时间
/*
要求匹配
23:59
02:07
*/
// 解析:
// 第一位:可以是0、1、2
// 第二位:当第一位位0或者1的时候,可以是0到9、第一位是2的时候,只可以是0到3
// 第三位:固定是冒号:
// 第四位:可以是0到5
// 第五位:0到9
let regex = /^([01]\d|2[0-3]):[0-5]\d$/
console.log(regex.test(‘23:59’)) // true
console.log(regex.test(‘02:07’))// true
// 衍生题,可以是非0
let regex = /^(0?\d|1\d|2[0-3]):(0?|[1-5])\d/
console.log( regex.test(“23:59”) ) // true
console.log( regex.test(“02:07”) ) // true
console.log( regex.test(“7:09”) ) // true
4.匹配日期
/*
要求匹配
yyyy-mm-dd格式的日期
注意月份、和日的匹配
*/
let regex = /\d{4}-(0\d|1[0-2])-(0[1-9]|[12]\d|3[01])/
console.log( regex.test(“2017-06-10”) ) // true
console.log( regex.test(“2017-11-10”) ) // true
3. 括号的神奇作用
===========
括号的作用是提供了分组(括号内的正则是一个整体,即提供子表达式),便于我们引用它
分组
如何让量词作用于一个整体?
let reg = /(ab)+/g
let string = ‘ababa abbb ababab’
console.log(string.match(reg)) // [“abab”, “ab”, “ababab”]
分支结构
分支结构有点像编程里面或的概念||
/*
匹配
I love JavaScript
I love Regular Expression
*/
let reg = /I love (JavaScript|Regular Expression)/
console.log(reg.test(‘I love JavaScript’)) // true
console.log(reg.test(‘I love Regular Expression’)) // true
分组引用
通过括号创建子表达式,可以进行数据提取和强大的替换操作,也可以通过js来引用分组内容
提取数据
/*
提取年月日
2021-08-14
*/
let reg = /(\d{4})-(\d{2})-(\d{2})/
console.log(‘2021-08-14’.match(reg))
// [“2021-08-14”, “2021”, “08”, “14”, index: 0, input: “2021-08-14”, groups: undefined]
// 第二种解法,通过全局的$1…$9读取 引用的括号数据
let reg = /(\d{4})-(\d{2})-(\d{2})/
let string = ‘2021-08-14’
reg.test(string)
console.log(RegExp.$1) // 2021
console.log(RegExp.$2) // 08
console.log(RegExp.$3) // 14
数据替换
/*
将以下格式替换为mm/dd/yyy
2021-08-14
*/
// 第一种解法
let reg = /(\d{4})-(\d{2})-(\d{2})/
let string = ‘2021-08-14’
// 第一种写法
let result1 = string.replace(reg, ‘$2/$3/$1’)
console.log(result1) // 08/14/2021
// 第二种写法
let result2 = string.replace(reg, () => {
return RegExp.$2 + ‘/’ + RegExp.$3 + ‘/’ + RegExp.$1
})
console.log(result2) // 08/14/2021
// 第三种写法
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
总结:
-
函数式编程其实是一种编程思想,它追求更细的粒度,将应用拆分成一组组极小的单元函数,组合调用操作数据流;
-
它提倡着 纯函数 / 函数复合 / 数据不可变, 谨慎对待函数内的 状态共享 / 依赖外部 / 副作用;
Tips:
其实我们很难也不需要在面试过程中去完美地阐述出整套思想,这里也只是浅尝辄止,一些个人理解而已。博主也是初级小菜鸟,停留在表面而已,只求对大家能有所帮助,轻喷🤣;
我个人觉得: 这些编程范式之间,其实并不矛盾,各有各的 优劣势。
理解和学习它们的理念与优势,合理地 设计融合,将优秀的软件编程思想用于提升我们应用;
所有设计思想,最终的目标一定是使我们的应用更加 解耦颗粒化、易拓展、易测试、高复用,开发更为高效和安全;
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
.(img-IUALT4FT-1712126098953)]
[外链图片转存中…(img-o6YXPk0I-1712126098954)]
[外链图片转存中…(img-yTGfhsbK-1712126098954)]
[外链图片转存中…(img-50ZAl3dz-1712126098954)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-tkOGdhLb-1712126098954)]
总结:
-
函数式编程其实是一种编程思想,它追求更细的粒度,将应用拆分成一组组极小的单元函数,组合调用操作数据流;
-
它提倡着 纯函数 / 函数复合 / 数据不可变, 谨慎对待函数内的 状态共享 / 依赖外部 / 副作用;
Tips:
其实我们很难也不需要在面试过程中去完美地阐述出整套思想,这里也只是浅尝辄止,一些个人理解而已。博主也是初级小菜鸟,停留在表面而已,只求对大家能有所帮助,轻喷🤣;
我个人觉得: 这些编程范式之间,其实并不矛盾,各有各的 优劣势。
理解和学习它们的理念与优势,合理地 设计融合,将优秀的软件编程思想用于提升我们应用;
所有设计思想,最终的目标一定是使我们的应用更加 解耦颗粒化、易拓展、易测试、高复用,开发更为高效和安全;
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算