正则表达式
正则表达式是一种用来描述字符串匹配模式的表达式。它由普通字符(例如字母、数字、符号)和特殊字符(元字符)组成,通过这些字符的组合可以描述字符串的特定模式。
正则表达式可以用于多种文本处理任务,包括搜索、替换、验证和提取字符串。
语法
定义规则:
const 变量名 = /表达式/
是否匹配:
test()用于检测正则表达式与指定字符串是否匹配的方法
regexp.test(str)
regexp
是一个正则表达式对象,用于匹配字符串。str
是要与正则表达式进行匹配的字符串。
test()
方法返回一个布尔值:
- 如果字符串
str
符合正则表达式regexp
的规则,则返回true
。 - 如果字符串
str
不符合正则表达式regexp
的规则,则返回false
var str = "Hello, world!";
var regexp = /Hello/;
var result = regexp.test(str);
console.log(result); // 输出 true
exec()用于在字符串中执行一个搜索匹配的方法,它返回一个数组(或者 null,如果没有找到匹配)。
regexp.exec(str)
regexp
是一个正则表达式对象,用于在字符串str
中执行搜索。str
是要在其中执行搜索的字符串。
exec()
方法返回一个数组:
- 如果找到了匹配,返回一个数组,其中第一个元素是匹配的子字符串,接下来的元素是与捕获组匹配的字符串(如果有的话)。
- 如果没有找到匹配,返回
null
。
var str = "Hello, world!";
var regexp = /Hello/;
var result = regexp.exec(str);
console.log(result); // 输出 ["Hello"], index: 0, input: "Hello, world!"
量词
在正则表达式中,量词用于指定模式重复出现的次数。它们允许我们匹配特定数量的字符或模式。
以下是一些常见的正则表达式量词:
-
*
:匹配前面的模式零次或多次。- 例如,
a*
可以匹配空字符串、"a"、"aa"、"aaa" 等。
- 例如,
-
+
:匹配前面的模式一次或多次。- 例如,
a+
可以匹配 "a"、"aa"、"aaa" 等,但不能匹配空字符串。
- 例如,
-
?
:匹配前面的模式零次或一次。- 例如,
a?
可以匹配空字符串或 "a"。
- 例如,
-
{n}
:匹配前面的模式恰好出现 n 次。- 例如,
a{3}
只能匹配 "aaa"。
- 例如,
-
{n,}
:匹配前面的模式至少出现 n 次。- 例如,
a{2,}
可以匹配 "aa"、"aaa"、"aaaa" 等。
- 例如,
-
{n,m}
:匹配前面的模式至少出现 n 次,但不超过 m 次。- 例如,
a{2,4}
可以匹配 "aa"、"aaa"、"aaaa",但不能匹配 "a" 或 "aaaaa"。
- 例如,
元字符
元字符是具有特殊含义的字符,用于匹配文本的模式。
.
:匹配除换行符以外的任意字符。
^:匹配字符串的开头。
$:匹配字符串的结尾。
*:匹配前面的元素零次或多次。
+
:匹配前面的元素一次或多次。
?:匹配前面的元素零次或一次。
\
:转义字符,用于匹配特殊字符本身,如\.
匹配句点。
[]
:字符集,匹配括号内的任意一个字符。
[abc]
:匹配字符 a、b 或 c。[a-z]
:匹配任意小写字母。[^0-9]
:匹配任意非数字字符。
|
:逻辑或,用于匹配两者之一。
cat|dog
:匹配 "cat" 或 "dog"。
()
:分组,将多个项组合为一个单元。
(ab)+
:匹配 "ab"、"abab"、"ababab" 等。
修饰符
i
:不区分大小写匹配。
- 例如,
/hello/i
可以匹配 "hello"、"Hello"、"HELLO" 等。
g
:全局匹配,即匹配所有符合条件的字符串,而不仅仅是第一个。
- 例如,
/hello/g
可以匹配 "hello"、"hello, world! hello" 中的所有 "hello"。
m
:多行匹配,使 ^
和 $
分别匹配一行的开头和结尾,而不仅仅是整个输入字符串的开头和结尾。
- 例如,
/^hello/m
可以匹配以 "hello" 开头的每一行。
练习:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.error{
color: red;
}
.right{
color: green;
}
</style>
</head>
<body>
<input type="text" name="username">
<span></span>
<script>
const input = document.querySelector('[name=username]')
input.addEventListener('change',phone)
function phone(){
const reg = /^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\9[0-35-9])\d{8}$/
input.nextElementSibling.innerHTML = '';
input.nextElementSibling.classList.remove('error', 'right');
if(reg.test(input.value)){
input.nextElementSibling.innerHTML = '输入正确';
input.nextElementSibling.classList.add('right');
}else{
input.nextElementSibling.innerHTML = '请输入正确的手机号码';
input.nextElementSibling.classList.add('error');
}
}
</script>
</body>
</html>
作用域
局部作用域
局部作用域分为函数作用域和块作用域。
函数作用域:
函数作用域指的是在函数内部声明的变量(包括函数参数),其作用域范围仅限于该函数内部,无法从函数外部访问。这意味着函数内部的变量在函数执行完毕后就会被销毁,不再存在于内存中。
函数作用域具有以下特点:
-
变量提升(Variable Hoisting):在 JavaScript 中,函数内部声明的变量会被提升到函数作用域的顶部。这意味着你可以在函数内部任何位置使用变量,即使在变量声明之前,因为变量声明会被提升到作用域顶部。
-
作用域嵌套(Nested Scopes):JavaScript 允许在一个函数内部定义另一个函数,形成嵌套的作用域关系。内部函数可以访问外部函数的变量,但外部函数不能访问内部函数的变量。
-
闭包(Closure):由于函数作用域的特性,函数内部的变量可以被函数外部的其他函数或代码访问,
形成了闭包。闭包在 JavaScript 中经常用于创建私有变量和实现模块化等功能。
function outerFunction() { var outerVariable = 'I am in outer function'; function innerFunction() { var innerVariable = 'I am in inner function'; console.log(outerVariable); // 可以访问外部函数的变量 console.log(innerVariable); // 可以访问内部函数的变量 } innerFunction(); } outerFunction();
块作用域
块作用域指的是变量的作用域范围限定在块级代码块(由一对花括号 {}
包裹)内部,而不再仅限于函数内部。在块作用域中声明的变量只在该代码块内部有效,并且在代码块外部无法访问。
使用块作用域有助于减少变量的污染和提供更好的代码封装性。通常,使用 let
和 const
关键字来声明块级作用域的变量。
function example() {
if (true) {
var functionScoped = 'I am in function scope';
let blockScoped = 'I am in block scope';
const alsoBlockScoped = 'I am also in block scope';
}
console.log(functionScoped); // 可以在块外部访问,因为是函数作用域
console.log(blockScoped); // 报错,无法在块外部访问,因为是块级作用域
console.log(alsoBlockScoped); // 报错,无法在块外部访问,因为是块级作用域
}
example();
全局作用域
全局作用域是指在整个 JavaScript 程序中都可以访问的作用域范围。在全局作用域中声明的变量和函数可以被代码中的任何部分访问,其生命周期在整个程序执行期间都有效。
在浏览器环境中,全局作用域通常指的是 window 对象,即全局对象。所有在全局作用域下声明的变量和函数实际上都成为了 window 对象的属性,可以通过 window.variableName
或 window.functionName()
的方式进行访问。
var globalVariable = 'I am in global scope';
function globalFunction() {
console.log('I am a global function');
}
console.log(globalVariable); // 可以直接访问全局变量
globalFunction(); // 可以直接调用全局函数
作用域链
作用域链(Scope Chain)是指在 JavaScript 中变量和函数的查找顺序。当代码在一个函数内部访问一个变量时,JavaScript 引擎会按照作用域链从内到外依次查找变量,直到找到为止。作用域链的形成是由代码中函数的嵌套结构所决定的。
在 JavaScript 中,每个函数都有自己的作用域,而作用域链是由这些嵌套的函数作用域所连接而成的。当在一个函数内部引用一个变量时,JavaScript 引擎首先查找当前函数的作用域内是否有该变量,如果没有,它会沿着作用域链向上查找,直到找到为止。如果最终在全局作用域中也找不到该变量,那么会抛出 ReferenceError。
var a = 10;
function outerFunction() {
var b = 20;
function innerFunction() {
var c = 30;
console.log(a); // 在 innerFunction 中查找变量 a,沿着作用域链找到了全局变量 a
console.log(b); // 在 innerFunction 中查找变量 b,直接在 outerFunction 中找到了变量 b
console.log(c); // 直接在 innerFunction 中找到变量 c
}
innerFunction();
}
outerFunction();
垃圾回收装置
垃圾回收(Garbage Collection)是一种自动内存管理机制,用于在程序运行时识别和回收不再被程序使用的内存资源,以避免内存泄漏和提高程序的性能。
以下是垃圾回收机制的一些关键概念和工作原理:
-
标记清除(Mark and Sweep):这是最常见的垃圾回收算法之一。它通过标记所有活动对象,然后清除未标记的对象来回收内存。这个过程包括标记阶段和清除阶段。
-
引用计数(Reference Counting):另一种常见的垃圾回收算法,它通过跟踪每个对象的引用计数来判断对象是否可回收。当引用计数为零时,表示对象不再被使用,可以被回收。
-
分代垃圾回收(Generational Garbage Collection):这是一种基于对象生存周期的策略,将内存中的对象分为不同的代,根据对象的存活时间进行不同频率的回收操作,提高了回收效率。
-
内存碎片整理(Memory Defragmentation):随着程序的运行,内存可能会产生碎片化,影响内存的分配和使用效率。一些垃圾回收算法会对内存进行整理,将存活对象集中放置,减少内存碎片。
-
停顿时间(Pause Time):垃圾回收会暂停程序的执行来进行内存回收操作,这可能引起程序的停顿。优秀的垃圾回收算法会尽量减少停顿时间,以提高程序的响应性能。
JS闭包
闭包(Closure)是指能够访问其词法作用域外部变量的函数。换句话说,闭包是内部函数可以访问外部函数作用域中变量的函数。在 JavaScript 中,由于函数是一等公民,函数可以作为参数传递、赋值给变量,也可以在另一个函数内部定义,从而形成闭包。
闭包通常发生在一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量。这样内部函数就形成了对外部函数作用域的闭包,即使外部函数已经执行完毕,内部函数依然可以访问和操作外部函数的变量。
变量提升
变量提升: 在 JavaScript 中,使用 var 关键字声明的变量会发生变量提升。这意味着无论变量的声明在代码中的哪个位置,都会被提升到当前作用域的顶部,但是变量的赋值不会被提升。
console.log(myVar); // 输出 undefined
var myVar = 5;
函数提升: 与变量提升类似,使用 function 关键字声明的函数也会发生函数提升。这意味着函数的声明会被提升到当前作用域的顶部,使得函数可以在声明之前被调用。
myFunc(); // 输出 "Hello"
function myFunc() {
console.log("Hello");
}
函数参数
动态参数
arguments
是一个特殊的对象,它包含了在函数调用时传递的所有参数。它可以在函数内部使用,无需提前声明。
arguments
对象类似于数组,可以通过索引访问其中的参数值。它具有 length
属性来表示传递的参数数量。
function getSum(){
let sum = 0
for(let i = 0;i < arguments.length;i++{
sum += arguments[i]
}
console.log(sum)
}
getSum(2,3,4)//9
剩余参数
函数剩余参数指的是通过在函数声明中使用 ... 前缀的形式,将多余的参数收集到一个数组中。这样做的好处是可以让函数接收任意数量的参数,并能够方便地在函数体内进行处理。
function sum(...numbers) {
console.log(numbers)
}
console.log(sum(1, 2, 3, 4, 5));
console.log(sum(1, 2));
展开运算符
展开运算符(Spread Operator)是 ES6 引入的一种语法,使用三个点 (...) 将一个数组或类数组对象展开成独立的元素序列。
求最大值
const arr = [1, 2, 3]
console.log(Math.max(...arr))
链接数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5, 6];
console.log(arr2); // 输出:[1, 2, 3, 4, 5, 6]