JavaScript 日期、数学与正则表达式全解析
日期处理
在 JavaScript 中,日期处理是常见需求。我们可以使用内置的
Date
对象方法获取日期的各个部分,如小时、分钟、秒和毫秒等,同时也有对应的 UTC 方法。示例代码如下:
d.getHours(); // 17
d.getMinutes(); // 0
d.getSeconds(); // 0
d.getMilliseconds(); // 0
// 对应的 UTC 方法
d.getUTCFullYear(); // 1815
d.getUTCMonth(); // 9 - October
d.getUTCDate(); // 10
如果使用 Moment.js,通常不需要单独处理这些组件,但了解它们的存在是有益的。
日期比较
对于简单的日期比较,比如判断日期 A 是否在日期 B 之后,我们可以使用 JavaScript 内置的比较运算符。因为
Date
实例将日期存储为数字,所以比较运算符可以直接作用于这些数字。示例如下:
const d1 = new Date(1996, 2, 1);
const d2 = new Date(2009, 4, 27);
console.log(d1 > d2); // false
console.log(d1 < d2); // true
日期运算
由于日期本质上是数字,我们可以通过相减来获取两个日期之间的毫秒数,进而计算天数差。同时,这一特性也使得使用
Array.prototype.sort
对日期进行排序变得容易。示例代码如下:
const msDiff = d2 - d1; // 417740400000 ms
const daysDiff = msDiff / 1000 / 60 / 60 / 24; // 4834.96 days
const dates = [];
// 创建一些随机日期
const min = new Date(2017, 0, 1).valueOf();
const delta = new Date(2020, 0, 1).valueOf() - min;
for (let i = 0; i < 10; i++) {
dates.push(new Date(min + delta * Math.random()));
}
// 降序排序
dates.sort((a, b) => b - a);
// 升序排序
dates.sort((a, b) => a - b);
Moment.js 提供了许多强大的方法来进行常见的日期运算,允许我们添加或减去任意时间单位,还支持方法链式调用。示例如下:
const m = moment(); // 现在
m.add(3, 'days'); // m 现在是未来三天
m.subtract(2, 'years'); // m 现在是过去两年零三天
m = moment(); // 重置
m.startOf('year'); // m 现在是今年 1 月 1 日
m.endOf('month'); // m 现在是今年 1 月 31 日
const m2 = moment()
.add(10, 'hours')
.subtract(3, 'days')
.endOf('month');
// m2 是如果你先前进 10 小时然后后退 3 天所处月份的月末
用户友好的相对日期
在很多情况下,以相对方式呈现日期信息(如“三天前”)会更加友好。Moment.js 使得这一操作变得简单:
moment().subtract(10, 'seconds').fromNow(); // 几秒前
moment().subtract(44, 'seconds').fromNow(); // 几秒前
moment().subtract(45, 'seconds').fromNow(); // 一分钟前
// 更多示例...
Moment.js 选择了一些合理的断点来切换显示不同的时间单位,这是获取用户友好相对日期的便捷方式。
数学处理
JavaScript 内置的
Math
对象包含了应用开发中常见的数学函数。在深入了解这些函数之前,我们需要知道 JavaScript 处理数字的方式:所有数字都是 IEEE 754 64 位浮点数,没有专门的整数类。对于大多数数学库中的函数,这简化了操作。
数字格式化
JavaScript 内置的数字格式化支持有限,但包括固定小数位数、固定精度和指数表示法,还支持以不同进制显示数字。
-
固定小数位数
:使用
Number.prototype.toFixed方法可以指定小数点后的位数,输出会进行四舍五入。示例如下:
const x = 19.51;
console.log(x.toFixed(3)); // "19.510"
console.log(x.toFixed(2)); // "19.51"
console.log(x.toFixed(1)); // "19.5"
console.log(x.toFixed(0)); // "20"
-
指数表示法
:使用
Number.prototype.toExponential方法可以将数字以指数形式显示,输出同样会进行四舍五入。示例如下:
const x = 3800.5;
console.log(x.toExponential(4)); // "3.8005e+4"
console.log(x.toExponential(3)); // "3.801e+4"
console.log(x.toExponential(2)); // "3.80e+4"
console.log(x.toExponential(1)); // "3.8e+4"
console.log(x.toExponential(0)); // "4e+4"
-
固定精度
:使用
Number.prototype.toPrecision方法可以指定数字的总位数,必要时会以指数形式输出。示例如下:
let x = 1000;
console.log(x.toPrecision(5)); // "1000.0"
console.log(x.toPrecision(4)); // "1000"
console.log(x.toPrecision(3)); // "1.00e+3"
// 更多示例...
-
不同进制
:使用
Number.prototype.toString方法并传入基数参数(范围为 2 到 36),可以将数字以不同进制显示。示例如下:
const x = 12;
console.log(x.toString()); // "12" (十进制)
console.log(x.toString(10)); // "12" (十进制)
console.log(x.toString(16)); // "c" (十六进制)
console.log(x.toString(8)); // "14" (八进制)
console.log(x.toString(2)); // "1100" (二进制)
如果内置方法无法满足需求,例如需要千位分隔符、特殊显示负数等,推荐使用 Numeral.js 库。
数学常量
常见的重要常量作为
Math
对象的属性可用,如下所示:
| 常量 | 描述 | 近似值 |
| ---- | ---- | ---- |
|
Math.E
| 自然对数的底数 | ~2.718 |
|
Math.PI
| 圆周率 | ~3.142 |
|
Math.LN2
| 2 的自然对数 | ~0.693 |
|
Math.LN10
| 10 的自然对数 | ~2.303 |
|
Math.LOG2E
| 以 2 为底
Math.E
的对数 | ~1.433 |
|
Math.LOG10E
| 以 10 为底
Math.E
的对数 | 0.434 |
|
Math.SQRT1_2
| 1/2 的平方根 | ~0.707 |
|
Math.SQRT2
| 2 的平方根 | ~1.414 |
代数函数
-
指数运算 :基本的指数运算函数是
Math.pow,还有平方根、立方根和 e 的幂等便捷函数,具体如下表所示:
| 函数 | 描述 | 示例 |
| ---- | ---- | ---- |
|Math.pow(x, y)| 计算 x 的 y 次幂 |Math.pow(2, 3)// 8 |
|Math.sqrt(x)| 计算 x 的平方根 |Math.sqrt(16)// 4 |
|Math.cbrt(x)| 计算 x 的立方根 |Math.cbrt(27)// 3 |
|Math.exp(x)| 计算 e 的 x 次幂 |Math.exp(1)// ~2.718 |
|Math.expm1(x)| 计算 e 的 x 次幂减 1 |Math.expm1(1)// ~1.718 |
|Math.hypot(x1, x2, ...)| 计算参数平方和的平方根 |Math.hypot(3, 4)// 5 | -
对数函数 :基本的自然对数函数是
Math.log,ES6 引入了Math.log10方便使用。具体函数如下表:
| 函数 | 描述 | 示例 |
| ---- | ---- | ---- |
|Math.log(x)| 计算 x 的自然对数 |Math.log(Math.E)// 1 |
|Math.log10(x)| 计算 x 以 10 为底的对数 |Math.log10(10)// 1 |
|Math.log2(x)| 计算 x 以 2 为底的对数 |Math.log2(2)// 1 |
|Math.log1p(x)| 计算 1 + x 的自然对数 |Math.log1p(Math.E - 1)// 1 | -
其他杂项函数 :包括绝对值、符号、向上取整、向下取整等常见操作,如下表所示:
| 函数 | 描述 | 示例 |
| ---- | ---- | ---- |
|Math.abs(x)| 计算 x 的绝对值 |Math.abs(-5.5)// 5.5 |
|Math.sign(x)| 返回 x 的符号 |Math.sign(-10.5)// -1 |
|Math.ceil(x)| 向上取整 |Math.ceil(2.2)// 3 |
|Math.floor(x)| 向下取整 |Math.floor(2.8)// 2 |
|Math.trunc(x)| 去除小数部分 |Math.trunc(7.7)// 7 |
|Math.round(x)| 四舍五入取整 |Math.round(7.2)// 7 |
|Math.min(x1, x2, ...)| 返回参数中的最小值 |Math.min(1, 2)// 1 |
|Math.max(x1, x2, ...)| 返回参数中的最大值 |Math.max(1, 2)// 2 |
伪随机数生成
Math.random
函数返回一个大于等于 0 且小于 1 的伪随机数。如果需要不同范围的伪随机数,可以使用以下公式:
| 范围 | 示例 |
| ---- | ---- |
| [0, 1) |
Math.random()
|
| [x, y) |
x + (y - x) * Math.random()
|
| 整数 [m, n) |
m + Math.floor((n - m) * Math.random())
|
| 整数 [m, n] |
m + Math.floor((n - m + 1) * Math.random())
|
如果需要可种子化的伪随机数,推荐使用 David Bau 的 seedrandom.js 包。
三角函数和双曲函数
三角函数和双曲函数在
Math
对象中都有对应方法,所有三角函数都使用弧度制。如果处理角度,需要先将其转换为弧度,可以使用以下辅助函数:
function deg2rad(d) { return d / 180 * Math.PI; }
function rad2deg(r) { return r / Math.PI * 180; }
三角函数和双曲函数的具体方法和示例如下表所示:
三角函数
| 函数 | 描述 | 示例 |
| ---- | ---- | ---- |
|
Math.sin(x)
| 计算 x 弧度的正弦值 |
Math.sin(Math.PI / 2)
// 1 |
|
Math.cos(x)
| 计算 x 弧度的余弦值 |
Math.cos(Math.PI)
// -1 |
|
Math.tan(x)
| 计算 x 弧度的正切值 |
Math.tan(Math.PI / 4)
// ~1 |
|
Math.asin(x)
| 计算 x 的反正弦值(结果为弧度) |
Math.asin(0)
// 0 |
|
Math.acos(x)
| 计算 x 的反余弦值(结果为弧度) |
Math.acos(0)
// ~1.57+ |
|
Math.atan(x)
| 计算 x 的反正切值(结果为弧度) |
Math.atan(0)
// 0 |
|
Math.atan2(y, x0)
| 计算从 x 轴到点 (x, y) 的逆时针角度(弧度) |
Math.atan2(0, 1)
// 0 |
双曲函数
| 函数 | 描述 | 示例 |
| ---- | ---- | ---- |
|
Math.sinh(x)
| 计算 x 的双曲正弦值 |
Math.sinh(0)
// 0 |
|
Math.cosh(x)
| 计算 x 的双曲余弦值 |
Math.cosh(0)
// 1 |
|
Math.tanh(x)
| 计算 x 的双曲正切值 |
Math.tanh(0)
// 0 |
|
Math.asinh(x)
| 计算 x 的反双曲正弦值 |
Math.asinh(0)
// 0 |
|
Math.acosh(x)
| 计算 x 的反双曲余弦值 |
Math.acosh(0)
// NaN |
|
Math.atanh(x)
| 计算 x 的反双曲正切值 |
Math.atanh(0)
// 0 |
正则表达式
正则表达式提供了强大的字符串匹配和替换功能。在深入学习正则表达式之前,我们先了解一下
String.prototype
的非正则搜索和替换功能,这些功能适用于简单的需求。
子字符串匹配和替换
如果只需要判断特定子字符串是否存在于大字符串中,可以使用
String.prototype
的以下方法:
const input = "As I was going to Saint Ives";
console.log(input.startsWith("As")); // true
console.log(input.endsWith("Ives")); // true
console.log(input.startsWith("going", 9)); // true -- 从索引 9 开始
console.log(input.endsWith("going", 14)); // true -- 将索引 14 视为字符串末尾
console.log(input.includes("going")); // true
console.log(input.includes("going", 10)); // false -- 从索引 10 开始
console.log(input.indexOf("going")); // 9
console.log(input.indexOf("going", 10)); // -1
console.log(input.indexOf("nope")); // -1
这些方法区分大小写,如果需要不区分大小写的比较,可以将输入转换为小写:
console.log(input.toLowerCase().startsWith("as")); // true
如果要查找并替换子字符串,可以使用
String.prototype.replace
方法:
const input = "As I was going to Saint Ives";
const output = input.replace("going", "walking");
构建正则表达式
在 JavaScript 中,正则表达式由
RegExp
类表示。我们可以使用
RegExp
构造函数或正则表达式字面量来创建正则表达式,推荐使用更方便的字面量语法:
const re1 = /going/; // 可以搜索 "going" 这个单词的正则表达式
const re2 = new RegExp("going"); // 等效的对象构造方式
使用正则表达式进行搜索
有了正则表达式后,我们有多种方法可以在字符串中进行搜索。以下是一些示例:
const input = "As I was going to Saint Ives";
const re = /\w{3,}/ig;
// 从字符串开始
console.log(input.match(re)); // ["was", "going", "Saint", "Ives"]
console.log(input.search(re)); // 5 (第一个三个字母的单词从索引 5 开始)
// 从正则表达式开始
console.log(re.test(input)); // true (input 包含至少一个三个字母的单词)
console.log(re.exec(input)); // ["was"] (第一次匹配)
console.log(re.exec(input)); // ["going"] (exec 会记住当前位置)
console.log(re.exec(input)); // ["Saint"]
console.log(re.exec(input)); // ["Ives"]
console.log(re.exec(input)); // null -- 没有更多匹配
在这些方法中,
RegExp.prototype.exec
提供的信息最多,但在实践中使用频率较低,而
String.prototype.match
和
RegExp.prototype.test
使用频率较高。
使用正则表达式进行替换
String.prototype.replace
方法也可以接受正则表达式,并且功能更强大。以下是一个简单的示例,替换所有四个字母以上的单词:
const input = "As I was going to Saint Ives";
const output = input.replace(/\w{4,}/ig, '****'); // "As I was **** to **** ****"
综上所述,JavaScript 在日期处理、数学计算和字符串匹配方面提供了丰富的功能和方法。通过合理运用这些功能,我们可以高效地完成各种开发任务。同时,对于更复杂的需求,还可以借助第三方库来实现。
JavaScript 日期、数学与正则表达式全解析
正则表达式的进阶应用
正则表达式的元字符
正则表达式的强大之处在于其元字符的使用。元字符是具有特殊含义的字符,用于定义匹配模式。以下是一些常见的元字符及其示例:
| 元字符 | 描述 | 示例 |
| ---- | ---- | ---- |
|
.
| 匹配除换行符以外的任意单个字符 |
/a.c/
可以匹配 “abc”、”adc” 等 |
|
*
| 匹配前面的元素零次或多次 |
/ab*c/
可以匹配 “ac”、”abc”、”abbbc” 等 |
|
+
| 匹配前面的元素一次或多次 |
/ab+c/
可以匹配 “abc”、”abbbc”,但不能匹配 “ac” |
|
?
| 匹配前面的元素零次或一次 |
/ab?c/
可以匹配 “ac”、”abc” |
|
^
| 匹配字符串的开头 |
/^As/
可以匹配以 “As” 开头的字符串 |
|
$
| 匹配字符串的结尾 |
/Ives$/
可以匹配以 “Ives” 结尾的字符串 |
|
[]
| 匹配方括号内的任意一个字符 |
/[aeiou]/
可以匹配任意一个元音字母 |
|
[^]
| 匹配不在方括号内的任意一个字符 |
/[^aeiou]/
可以匹配任意一个非元音字母 |
分组和捕获
正则表达式中的分组和捕获允许我们将匹配的部分提取出来。使用圆括号
()
可以创建分组,示例如下:
const input = "John Doe, 30 years old";
const re = /(\w+) (\w+), (\d+) years old/;
const matches = input.match(re);
console.log(matches[1]); // "John"
console.log(matches[2]); // "Doe"
console.log(matches[3]); // "30"
在这个例子中,我们使用了三个分组,分别捕获了名字、姓氏和年龄。
修饰符
正则表达式可以使用修饰符来改变匹配的行为。常见的修饰符有:
-
i
:忽略大小写
-
g
:全局匹配,查找所有匹配项
-
m
:多行匹配
示例如下:
const input = "As I was going to Saint Ives";
const re1 = /going/i; // 忽略大小写
console.log(input.match(re1)); // ["going"]
const re2 = /\w{3,}/ig; // 全局匹配且忽略大小写
console.log(input.match(re2)); // ["As", "was", "going", "Saint", "Ives"]
正则表达式的实际应用场景
- 验证电子邮件地址 :
const email = "test@example.com";
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
console.log(emailRegex.test(email)); // true
- 验证手机号码 :
const phone = "13800138000";
const phoneRegex = /^1[3-9]\d{9}$/;
console.log(phoneRegex.test(phone)); // true
综合应用示例
假设我们要开发一个简单的表单验证系统,需要验证用户输入的姓名、电子邮件和手机号码。以下是一个完整的示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Validation</title>
</head>
<body>
<form id="myForm">
<label for="name">Name:</label>
<input type="text" id="name" required>
<br>
<label for="email">Email:</label>
<input type="email" id="email" required>
<br>
<label for="phone">Phone:</label>
<input type="text" id="phone" required>
<br>
<input type="submit" value="Submit">
</form>
<script>
const form = document.getElementById('myForm');
form.addEventListener('submit', function (event) {
event.preventDefault();
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const phone = document.getElementById('phone').value;
const nameRegex = /^[a-zA-Z ]+$/;
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const phoneRegex = /^1[3-9]\d{9}$/;
if (!nameRegex.test(name)) {
alert('Please enter a valid name.');
return;
}
if (!emailRegex.test(email)) {
alert('Please enter a valid email address.');
return;
}
if (!phoneRegex.test(phone)) {
alert('Please enter a valid phone number.');
return;
}
alert('Form submitted successfully!');
});
</script>
</body>
</html>
在这个示例中,我们使用正则表达式来验证用户输入的姓名、电子邮件和手机号码。如果输入不符合要求,会弹出相应的提示框。
总结
JavaScript 在日期处理、数学计算和字符串匹配方面提供了丰富的功能和方法。通过内置的
Date
对象和
Math
对象,我们可以进行日期和数学运算,同时借助正则表达式可以实现强大的字符串匹配和替换功能。对于简单的需求,内置的方法已经足够,但对于更复杂的场景,第三方库如 Moment.js、Numeral.js 等可以提供更多的支持。
在实际开发中,我们应该根据具体需求选择合适的方法和工具。例如,在处理日期时,如果需要复杂的日期运算和格式化,使用 Moment.js 会更加方便;在进行数字格式化时,如果内置方法无法满足需求,可以考虑使用 Numeral.js。在使用正则表达式时,要熟练掌握元字符、分组和捕获等概念,以便更好地实现字符串匹配和验证。
希望通过本文的介绍,你对 JavaScript 的日期、数学和正则表达式有了更深入的理解,能够在实际项目中灵活运用这些知识。
graph TD;
A[开始] --> B[输入姓名、邮箱、电话];
B --> C{验证姓名};
C -- 不通过 --> D[提示输入有效姓名];
C -- 通过 --> E{验证邮箱};
E -- 不通过 --> F[提示输入有效邮箱];
E -- 通过 --> G{验证电话};
G -- 不通过 --> H[提示输入有效电话];
G -- 通过 --> I[提示表单提交成功];
D --> J[结束];
F --> J;
H --> J;
I --> J;
J[结束];
以上流程图展示了表单验证的流程,从输入信息开始,依次验证姓名、邮箱和电话,根据验证结果给出相应的提示,最后结束流程。
JavaScript日期、数学与正则表达式解析
超级会员免费看

被折叠的 条评论
为什么被折叠?



