24、JavaScript 日期、数学与正则表达式全解析

JavaScript日期、数学与正则表达式解析

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[结束];

以上流程图展示了表单验证的流程,从输入信息开始,依次验证姓名、邮箱和电话,根据验证结果给出相应的提示,最后结束流程。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值