先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
正文
$
匹配字符的结束。比如必须以数字结束,可以这么写:
/\d$/
范围匹配
====
范围匹配是利用方括号[]
实现的。
方括号[]
用于范围匹配,也就是查找某个范围内的字符。比如[0-9]
代表匹配数字,而[a-z]
可以匹配小写字母a到z这26个字符中的任意一个。
如果要匹配不在方括号中的字符,可以在方括号中以^
开头,比如[^0-9]
,用于匹配非数字,等价于\D
。
主要元字符
=====
.
匹配除换行符\n
外的任意字符,如果要匹配任意字符,应该用/[.\n]*/
。
\s
匹配任意空字符,包括空格,制表符\t
,垂直制表符\v
,换行符\n
,回车符\r
,换页符\f
。\s
等价于[ \t\v\n\r\f]
,注意方括号内第一个位置有空格。
这里也说下换行符和回车符的区别:
-
换行符
\n
:光标下移一行,不回行首。 -
回车符
\r
:光标回到行首,不换行。
\S
\S
是\s
的反集 ,利用\s
和\S
的这种互为反集的关系,我们就可以匹配任意字符,写法如下:
/[\s\S]/
\d
\d
用于匹配数字,等价于[0-9]
。
\D
\D
是\d
的反集,也就是匹配非数字,等价于[^0-9]
。
\w
\w
用于匹配单词字符,包含0-9
,a-z
,A-z
以及下划线_
,等价于[A-Za-z0-9_]
。
\W
\W
是\w
的反集,用于匹配非单词字符,等价于[^A-Za-z0-9_]
。
\n
\n
是开发中经常遇到的换行符,而上面提到的\s
是包含\n
在内的。所以,能被\n
匹配的字符,也一定能被\s
匹配。
\b
\b
用于匹配单词的边界,即单词的开始或结束。
一开始其实我不太能理解\b
在正则表达式中的作用。
直到我自己试了一下这个案例
‘I love you’.match(/love/)
‘Iloveyou’.match(/love/)
这两个表达式都能匹配到结果"love"
。
但是有时候我们并不希望这样的字符串'Iloveyou'
被匹配,因为它没有单词间的空格。
所以\b
有了它存在的意义。看下面的例子:
‘I love you’.match(/\blove\b/)
‘Iloveyou’.match(/\blove\b/) // null
第一个表达式仍然可以正常匹配到结果,而第二个就无法匹配到结果了,这符合我们的预期。
有的人可能会说,那我可以用空格匹配啊。
‘I love you’.match(/ love /)
空格和\b
在这种场景下还是有一点不一样的,这体现在match
的结果上。
如果是用空格匹配,那么match
的结果数组中的第一项就是" love "
,是带了空格的,然而很多时候我们不希望在结果中得到空格,所以\b
存在的意义也就比较明显了。
\B
与\b
相反,代表非单词边界。也就是说,使用\B
匹配时,目标字符前或后不能是空格。
假设\B
在前,比如
/\Babc/.test(‘111 abc’) // false
假设\B
在后,比如
/abc\B/.test(‘abc 111’) // false
转义字符\
由于正则表达式中很多字符有特殊含义,比如(
, )
, \
, [
, ]
, +
,如果你真的要匹配它们,必须加上转义符\
。
///.test(‘/’); // true
或 |
实现或的逻辑是比较简单的,正则表达式提供了|
。
要注意的是,|
隔断的是其左右的整个子表达式,而不是单个普通字符。
所以,
/^ab|cd|ef$/.test(‘ab’) // true
/^ab|cd|ef$/.test(‘cd’) // true
/^ab|cd|ef$/.test(‘ace’) // false
还要注意的是,|
具有从左到右的优先级,如果左侧的匹配上了,右侧的就被忽略了,即便右侧的匹配看起来更“完美”。
/a|ab/.exec('ab')
得到的结果是
[“a”, index: 0, input: “ab”, groups: undefined]
量词
==
?
匹配前面的子表达式零次或一次
匹配前面的子表达式一次或多次
*
–
匹配前面的子表达式零次或任意次
{n,m}
匹配前一个普通字符或者子表达式最少n次,最多m次
{n,}
匹配前一个普通字符或者子表达式最少n次
{n}
匹配前一个普通字符或者子表达式n次
贪婪
–
贪婪匹配是尽可能多地匹配,如果能满足匹配条件,就尽可能侵占后面的匹配规则。
贪婪匹配是默认的,比如/\d?/
会尽可能地匹配1
个数字,/\d+/
和/\d*/
会尽可能地匹配多个数字。
举个例子,
‘123456789’.match(/^(\d+)(\d{2,})$/)
以上结果中捕获组的第一项是"1234567"
,第二项是"89"
。
为什么会这样呢?因为\d+
是贪婪匹配,尽可能地多匹配,如果没有后面的\d{2,}
,捕获组第一项会直接是"123456789"
。但是由于\d{2,}
的存在,\d+
会给\d{2,}
留个面子,满足它的最小条件,即匹配2个数字,而\d+
自己匹配7个数字。
非贪婪
非贪婪匹配是尽可能少地匹配,一般是在量词?
, +
, *
之后再加一个?
,表示尽可能少地匹配,把机会留给后面的匹配规则。
还是拿贪婪模式中那个例子举例,稍微改一下,\d+
换成非贪婪模式\d+?
。
‘123456789’.match(/^(\d+?)(\d{2,})$/)
捕获组的第一项是"1"
,第二项变成了"23456789"
。
为什么会这样呢?因为在非贪婪模式下,会尽可能少匹配,把机会留给后面的匹配规则。
分组
==
分组在正则中是一个非常有用的神器,用圆括号()
来包裹的内容就是一个分组,在正则中是这种表示形式:
/(\d*)([a-z]*)/
捕获组()
利用捕获组,我们能捕获到关键字符。
比如
var group = ‘123456789hahaha’.match(/(\d*)([a-z]*)/)
分组1用于匹配任意个数字,分组2用于匹配任意个小写字母。
那么我们在match
方法的返回结果中就可以取到这两个分组匹配的结果,group[1]
是"123456789"
,group[2]
是"hahaha"
。
我们还可以在RegExp
的静态属性$1~$9
取得前9
个分组匹配的结果。RegExp.$1
是"123456789"
,RegExp.$2
是"hahaha"
。但是RegExp.$1~$9
是非标准的,虽然很多浏览器都实现了,尽量不要在生产环境中使用。
这种捕获组的应用在字符串的replace
方法中也是类似,不过在调用replace
方法时,我们需要通过$1
, $2
, $n
这种形式去引用分组。
“123456789hahaha”.replace(/(\d*)([a-z]*)/, “$1”) // “123456789”
利用$1
,我们就可以把源字符串替换为分组1匹配到的字符串,也就是"123456789"
。
非捕获组(?😃
非捕获组是不生成引用的分组,它也由圆括号()
包裹起来,不过圆括号中起头的是?:
,也就是/(?:\d*)/
这种形式。
还是改造下之前的例子来看下:
var group = ‘123456789hahaha’.match(/(?:\d*)([a-z]*)/)
由于非捕获组不生成引用,所以group[1]
是"hahaha"
;同样地,RegExp.$1
也是"hahaha"
。
看到这里,我不禁也产生了疑问,既然我不需要引用非捕获组,那么非捕获组的意义何在?
思考了一阵后,我觉得非捕获组大概有这么一些优势和必要性:
-
与捕获组相比,非捕获组在内存上开销更小,因为它不需要生成引用
-
分组是为了方便加量词。我们虽然可以不生成引用,但是如果没有分组,就不太方便加给一组字符加量词。
‘1a2b3c…’.match(/(?:\d[a-z]){2,3}(.+)/)
引用\num
正则表达式中可以引用前面的具有引用的分组,通过\1
,\2
这种形式可以实现引用前面的子表达式。
比如,我要匹配一个字符串,要求符合这样的规则:
字符串由单引号或双引号开头和结束,中间内容可以是数字,单词。
那我要保证的是首尾要么是单引号,要么是双引号,所以我的pattern写法可以是:
var pattern = /^(["'])[a-z\d]*\1$/
pattern.test(“‘perfect123’”) // true
pattern.test(‘“1perfect2”’) // true
零宽断言
====
说实话,一开始看零宽断言的概念和解释时,我真的完全不懂在说什么。
-
零宽正向先行断言(?=)
-
零宽负向先行断言(?!)
-
零宽正向后行断言(?<=)
-
零宽负向后行断言(?<!)
后面把词汇拆开来看,加入自己的理解,就慢慢有点懂了。
-
零宽:zero width,断言作为必要条件进行匹配,但是不体现在匹配结果中。
-
正向:positive,断言中的字符必须被匹配。
-
负向:negative,断言中的字符不能被匹配。
-
先行:lookahead,必须满足前方的条件,条件在前方,前方等同于右侧。
-
后行:lookbehind,必须满足后方的条件,条件在后方,后方等同于左侧。
零宽正向先行断言(?=)
约束目标右侧必须存在指定的字符。
/123(?=a)/.test(‘123a’) // true
上面的例子约束了123
右侧必须有a
。
零宽负向先行断言(?!)
约束目标右侧不能存在指定的字符。
/123(?!a)/.test(‘123a’) // false
上面的例子约束了123
右侧不能有a
,否则结果为false
。
零宽正向后行断言(?<=)
约束目标左侧必须存在指定的字符。
/(?<=a)123/.test(‘a123’) // true
上面的例子约束了123
左侧必须有a
。
ES2018才支持零宽后行断言,具体见TC39 Proposals[3]
零宽负向后行断言(?<!)
约束目标左侧不能存在指定的字符。
/(?<!a)123/.test(‘a123’) // false
上面的例子约束了123
左侧不能有a
,否则结果为false
注:ES2018才支持此特性。
RegExp
======
说到正则表达式,就不得不提到RegExp
对象。下面我们从原型方法,静态属性,实例属性等几个方面来认识下RegExp
对象。
原型方法
RegExp.prototype.test
test()
是我们平时最常用的正则方法,test()
方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配,返回一个布尔值true
或false
。
如果正则表达式设置了全局标志g
,执行test()
会改变RegExp.lastIndex
属性,用于记录上次匹配到的字符的起始索引。连续执行test()
方法,后续的执行将会从lastIndex
处开始匹配字符串。这种情况下,如果test()
无法匹配到结果,lastIndex
就会重置为0
。
RegExp.prototype.exec
exec()
相较于test()
能得到更丰富的匹配信息,其结果是一个数组,数组的第0个元素是匹配到的字符串,第1~n个元素是圆括号()
分组捕获的结果。
结果数组是数组,数组也是对象类型数据,所以结果数组还有两个属性分别是index
和input
最后更多分享:前端字节跳动真题解析
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
xp对象。下面我们从原型方法,静态属性,实例属性等几个方面来认识下
RegExp`对象。
原型方法
RegExp.prototype.test
test()
是我们平时最常用的正则方法,test()
方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配,返回一个布尔值true
或false
。
如果正则表达式设置了全局标志g
,执行test()
会改变RegExp.lastIndex
属性,用于记录上次匹配到的字符的起始索引。连续执行test()
方法,后续的执行将会从lastIndex
处开始匹配字符串。这种情况下,如果test()
无法匹配到结果,lastIndex
就会重置为0
。
RegExp.prototype.exec
exec()
相较于test()
能得到更丰富的匹配信息,其结果是一个数组,数组的第0个元素是匹配到的字符串,第1~n个元素是圆括号()
分组捕获的结果。
结果数组是数组,数组也是对象类型数据,所以结果数组还有两个属性分别是index
和input
最后更多分享:前端字节跳动真题解析
- [外链图片转存中…(img-Hp3aRUwZ-1713421112030)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-SJflgytJ-1713421112030)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!