JavaScript: JavaScript 实用工具方法
前面学了类型转换,这课来学一批日常开发高频使用的工具方法。它们就像瑞士军刀——每一把都不复杂,但关键时刻缺了哪把都不行。
1. 常用工具方法详解
▶ 示例
JAVASCRIPT
// 本节代码示例
const message = "你好,世界!";
console.log(message);
这是最简单的示例,后续章节会详细解释。### (1) parseInt 和 parseFloat
从字符串开头解析数字,遇到非数字字符就停下:
HTML
<script>
console.log(parseInt("42px")); // 42
console.log(parseInt("3.14")); // 3(只取整数部分)
console.log(parseFloat("3.14")); // 3.14
console.log(parseFloat("3.14abc")); // 3.14
console.log(parseInt("abc42")); // NaN(开头就不是数字)
</script>
parseInt 第二个参数是进制:
HTML
<script>
console.log(parseInt("FF", 16)); // 255(十六进制)
console.log(parseInt("10", 2)); // 2(二进制)
</script>
坑:不传进制时,以 0x 开头的字符串会被当作十六进制解析。建议永远传第二个参数 10。
(2) isNaN vs Number.isNaN
| 方法 | 行为 |
|---|---|
isNaN(value) |
先转数字,再判断是否 NaN |
Number.isNaN(value) |
严格判断是否为 NaN(不转换) |
HTML
<script>
console.log(isNaN("hello")); // true(先 Number("hello") → NaN,然后 true)
console.log(Number.isNaN("hello")); // false("hello" 本身不是 NaN)
console.log(isNaN(undefined)); // true
console.log(Number.isNaN(undefined));// false
</script>
结论:用 Number.isNaN(),它不会偷偷做类型转换。
(3) toFixed
控制小数位数,返回字符串:
HTML
<script>
console.log((3.14159).toFixed(2)); // "3.14"
console.log((3.1).toFixed(4)); // "3.1000"(补零)
console.log((3.145).toFixed(2)); // "3.14" 或 "3.15"(浮点精度问题,不保证四舍五入)
</script>
注意 toFixed 返回字符串,计算时需要再用 Number() 转回来。
(4) toString
数字转字符串,支持指定进制:
HTML
<script>
console.log((255).toString()); // "255"
console.log((255).toString(16)); // "ff"
console.log((8).toString(2)); // "1000"
console.log((100).toString(8)); // "144"
</script>
布尔值和数组也有 toString:
HTML
<script>
console.log(true.toString()); // "true"
console.log([1,2,3].toString()); // "1,2,3"
</script>
(5) Number.isInteger
判断一个值是否为整数:
HTML
<script>
console.log(Number.isInteger(5)); // true
console.log(Number.isInteger(5.0)); // true(5.0 就是 5)
console.log(Number.isInteger(5.5)); // false
console.log(Number.isInteger("5")); // false(字符串不是整数)
console.log(Number.isInteger(NaN)); // false
</script>
(6) Math.trunc 和 Math.sign
Math.trunc 直接截掉小数部分(不做四舍五入):
HTML
<script>
console.log(Math.trunc(4.9)); // 4
console.log(Math.trunc(-4.9)); // -4
console.log(Math.trunc(4.1)); // 4
</script>
Math.sign 返回数字的符号:
HTML
<script>
console.log(Math.sign(5)); // 1
console.log(Math.sign(-5)); // -1
console.log(Math.sign(0)); // 0
console.log(Math.sign(-0)); // -0
console.log(Math.sign(NaN)); // NaN
</script>
(7) typeof 的局限
typeof 通常能判断类型,但有几个经典翻车:
HTML
<script>
console.log(typeof 42); // "number" ✅
console.log(typeof "hello"); // "string" ✅
console.log(typeof true); // "boolean" ✅
console.log(typeof undefined); // "undefined" ✅
console.log(typeof null); // "object" ❌ 这是历史 bug!
console.log(typeof []); // "object" ❌ 数组也是 object
console.log(typeof {}); // "object" ✅
console.log(typeof function(){}); // "function" ✅
</script>
typeof null === "object" 是 JavaScript 的"元老级 bug"——1995 年就存在了,改不了了。判断 null 要用 value === null。判断数组要用 Array.isArray()。
▶ 示例:价格计算器
HTML
📖 仅展示
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>价格计算</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.calc { max-width: 440px; margin: 16px auto; padding: 24px; background: #f9f9f9; border-radius: 12px; }
.row { margin: 12px 0; display: flex; align-items: center; gap: 10px; }
label { width: 80px; font-weight: bold; }
input { padding: 8px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; flex: 1; }
button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; background: #4a90d9; color: #fff; width: 100%; margin-top: 8px; }
button:hover { background: #357abd; }
.result { margin-top: 16px; padding: 16px; background: #fff; border-radius: 8px; border: 2px solid #5cb85c; }
.result div { margin: 4px 0; }
.total { font-size: 22px; font-weight: bold; color: #d9534f; }
</style>
</head>
<body>
<h2 style="text-align:center;">价格计算器</h2>
<div class="calc">
<div class="row">
<label>单价:</label>
<input type="text" id="price" value="29.90" placeholder="如 29.90" />
</div>
<div class="row">
<label>数量:</label>
<input type="text" id="qty" value="3" placeholder="如 3" />
</div>
<div class="row">
<label>折扣:</label>
<input type="text" id="discount" value="0.85" placeholder="如 0.85(八五折)" />
</div>
<button id="calc">计算</button>
<div class="result" id="result" style="display:none;"></div>
</div>
<script>
document.getElementById("calc").addEventListener("click", function() {
const priceStr = document.getElementById("price").value.trim();
const qtyStr = document.getElementById("qty").value.trim();
const discountStr = document.getElementById("discount").value.trim();
const price = parseFloat(priceStr);
const qty = parseInt(qtyStr, 10);
const discount = parseFloat(discountStr);
if (Number.isNaN(price) || price <= 0) {
alert("单价无效!请输入正数");
return;
}
if (Number.isNaN(qty) || qty <= 0 || !Number.isInteger(qty)) {
alert("数量无效!请输入正整数");
return;
}
if (Number.isNaN(discount) || discount <= 0 || discount > 1) {
alert("折扣无效!请输入 0~1 之间的小数");
return;
}
const subtotal = price * qty;
const total = subtotal * discount;
const saved = subtotal - total;
document.getElementById("result").style.display = "block";
document.getElementById("result").innerHTML = `
<div>单价:¥${price.toFixed(2)}</div>
<div>数量:${qty}</div>
<div>小计:¥${subtotal.toFixed(2)}</div>
<div>折扣:${(discount * 100).toFixed(0)}折(-${(saved).toFixed(2)}元)</div>
<div class="total">应付:¥${total.toFixed(2)}</div>
`;
});
</script>
</body>
</html>
▶ 示例:输入验证工具
HTML
📖 仅展示
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>输入验证</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.demo { max-width: 500px; margin: 16px auto; }
.row { margin: 12px 0; display: flex; align-items: center; gap: 10px; }
label { width: 80px; font-weight: bold; }
input { padding: 8px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; flex: 1; }
input.valid { border-color: #5cb85c; background: #f0fff0; }
input.invalid { border-color: #d9534f; background: #fff0f0; }
.msg { font-size: 13px; margin-left: 90px; }
.msg.valid { color: #5cb85c; }
.msg.invalid { color: #d9534f; }
.methods { margin-top: 20px; padding: 16px; background: #f5f5f5; border-radius: 8px; }
.methods div { margin: 4px 0; font-family: monospace; font-size: 14px; }
</style>
</head>
<body>
<h2 style="text-align:center;">输入验证演示</h2>
<div class="demo">
<div class="row">
<label>年龄:</label>
<input type="text" id="age" placeholder="输入年龄" />
</div>
<div class="msg" id="ageMsg"></div>
<div class="row">
<label>手机号:</label>
<input type="text" id="phone" placeholder="输入手机号" />
</div>
<div class="msg" id="phoneMsg"></div>
<div class="row">
<label>金额:</label>
<input type="text" id="amount" placeholder="输入金额" />
</div>
<div class="msg" id="amountMsg"></div>
<div class="methods" id="methods"></div>
</div>
<script>
function validateAge(value) {
const num = parseInt(value, 10);
if (value.trim() === "") return { valid: false, msg: "不能为空" };
if (Number.isNaN(num)) return { valid: false, msg: "不是有效整数" };
if (!Number.isInteger(Number(value))) return { valid: false, msg: "年龄必须是整数" };
if (num < 0 || num > 150) return { valid: false, msg: "年龄范围 0-150" };
return { valid: true, msg: `有效,你 ${num} 岁` };
}
function validatePhone(value) {
const trimmed = value.trim();
if (trimmed === "") return { valid: false, msg: "不能为空" };
if (!/^\d+$/.test(trimmed)) return { valid: false, msg: "只能包含数字" };
if (trimmed.length !== 11) return { valid: false, msg: "手机号应为11位" };
return { valid: true, msg: "格式正确" };
}
function validateAmount(value) {
const num = parseFloat(value);
if (value.trim() === "") return { valid: false, msg: "不能为空" };
if (Number.isNaN(num)) return { valid: false, msg: "不是有效数字" };
if (num <= 0) return { valid: false, msg: "金额必须大于0" };
return { valid: true, msg: `有效,金额 ¥${num.toFixed(2)}` };
}
function setupValidation(inputId, msgId, validator) {
const input = document.getElementById(inputId);
const msg = document.getElementById(msgId);
input.addEventListener("input", function() {
const result = validator(input.value);
input.className = result.valid ? "valid" : "invalid";
msg.className = "msg " + (result.valid ? "valid" : "invalid");
msg.textContent = result.msg;
updateMethods();
});
}
setupValidation("age", "ageMsg", validateAge);
setupValidation("phone", "phoneMsg", validatePhone);
setupValidation("amount", "amountMsg", validateAmount);
function updateMethods() {
const age = document.getElementById("age").value;
const phone = document.getElementById("phone").value;
const amount = document.getElementById("amount").value;
document.getElementById("methods").innerHTML = `
<strong>工具方法解析结果:</strong>
<div>parseInt("${age}", 10) → ${parseInt(age, 10)}</div>
<div>Number.isNaN(parseInt("${age}")) → ${Number.isNaN(parseInt(age, 10))}</div>
<div>parseFloat("${amount}") → ${parseFloat(amount)}</div>
<div>typeof "${phone}" → "${typeof phone}"</div>
`;
}
</script>
</body>
</html>
▶ 示例:typeof 与类型判断
HTML
📖 仅展示
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>typeof类型判断</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 700px; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
th { background: #4a90d9; color: #fff; }
.bug { background: #fff0f0; }
.ok { background: #f0fff0; }
.tip { background: #fff8e1; padding: 12px; border-radius: 6px; border-left: 4px solid #f0ad4e; margin: 16px 0; max-width: 700px; }
</style>
</head>
<body>
<h2>typeof 逐项检测</h2>
<div id="output"></div>
<script>
const values = [
[42, "数字"],
["hello", "字符串"],
[true, "布尔值"],
[undefined, "undefined"],
[null, "null ⚠️"],
[[1,2,3], "数组 ⚠️"],
[{a:1}, "对象"],
[function(){}, "函数"],
[NaN, "NaN"],
[Infinity, "Infinity"],
];
const rows = values.map(([val, label]) => {
const typeResult = typeof val;
const isBug = (val === null && typeResult === "object") ||
(Array.isArray(val) && typeResult === "object");
const correctWay = val === null ? "value === null" :
Array.isArray(val) ? "Array.isArray(value)" :
"typeof 即可";
return `<tr class="${isBug ? 'bug' : 'ok'}">
<td>${label}</td>
<td><code>${JSON.stringify(val)}</code></td>
<td><code>"${typeResult}"</code></td>
<td>${isBug ? "❌ 不准确" : "✅ 正确"}</td>
<td>${isBug ? correctWay : "—"}</td>
</tr>`;
});
document.getElementById("output").innerHTML = `
<table>
<tr><th>预期类型</th><th>值</th><th>typeof 结果</th><th>是否准确</th><th>正确判断方式</th></tr>
${rows.join("")}
</table>
<div class="tip">
💡 记住两个特例:<code>typeof null</code> 返回 <code>"object"</code>(历史 bug),
<code>typeof []</code> 也返回 <code>"object"</code>(数组不是独立类型)。
判断 null 用 <code>=== null</code>,判断数组用 <code>Array.isArray()</code>。
</div>
`;
</script>
</body>
</html>
❓ 常见问题
Q
toFixed 为什么不保证四舍五入?A 因为浮点数在二进制中无法精确表示。
(0.1 + 0.2).toFixed(1) 可能得到 "0.3" 也可能出问题。如果需要精确的四舍五入,用 Math.round(num * 100) / 100 再 toFixed。Q
parseFloat 和 Number() 有什么区别?A
parseFloat("42px") 返回 42(从开头解析到非数字停止),而 Number("42px") 返回 NaN(整体解析,含非数字就失败)。需要从混合文本中提取数字时用 parseFloat,需要严格验证时用 Number()。📖 小节
parseInt(str, radix)解析整数,指定进制避免意外结果;parseFloat(str)解析浮点数isNaN(value)判断是否为 NaN,NaN === NaN为 false,用Number.isNaN更严格- 显式转换:
Number(value)、String(value)、Boolean(value)语义明确比隐式转换更安全 num.toFixed(digits)保留指定位数小数返回字符串,适合金额显示- 类型检测技巧:
typeof检测基本类型,Array.isArray()检测数组,Object.prototype.toString.call()精确检测
📝 作业
- 写一个函数
parseChineseNumber(str),能解析"3.5元"→3.5、"100人"→100、"约200"→200(用parseFloat)。处理纯文字返回NaN的情况。 - 写一个函数
formatMoney(amount),接收数字,返回带千分位逗号和两位小数的字符串,如1234567.89→"1,234,567.89"(提示:用toFixed和toString配合数组方法)。 - 写一个函数
getType(value),弥补typeof的缺陷:对null返回"null",对数组返回"array",其余情况返回typeof的结果。