《 鸿蒙ArkTS新手避坑指南:if/else陷阱、赋值等于混淆、Math/Date、异常处理等》
本文总结了ArkTS开发中的七个常见误区,涵盖分支判断、赋值与等于、内置对象使用等方面。详细解析了if/else if/else与多个独立if的区别,强调=、==、===的差异及使用场景,列出真假值完整列表。特别指出Math对象的使用要点,包括取整方法对比和常见陷阱。通过大量代码示例和边界情况说明,帮助开发者避免常见错误,掌握底层原理。
前言
在鸿蒙应用开发中,ArkTS 基于 TypeScript,很多初学者在写原生代码时,经常会遇到一些看似简单却容易踩坑的小问题。本文总结了分支判断、赋值与等于、常用内置对象、参数写法、异常处理、字符串操作、小数与类型转换七个方面的常见误区,每个知识点都拆解得非常细致,包含大量代码示例和边界情况说明,帮助新手彻底理解背后的原理。
一、分支语句:if 与 if-else 的区别详解
1.1 if 单独使用
if 语句单独使用时,条件为 true 就执行代码块,为 false 就跳过。
let age = 18;
if (age >= 18) {
console.log("已成年");
}
// 条件为 true,输出:已成年
let score = 60;
if (score >= 90) {
console.log("优秀");
}
// 条件为 false,不输出任何内容
特点:不处理条件为 false 的情况,直接跳过。
1.2 if-else 配对使用
if-else 提供了二选一的分支:条件为 true 执行 if 块,为 false 执行 else 块。
let age = 16;
if (age >= 18) {
console.log("已成年");
} else {
console.log("未成年");
}
// 输出:未成年
特点:一定会执行其中一个分支,不会跳过。
1.3 if-else if-else 多分支
用于多个条件按优先级判断,从上到下匹配,一旦匹配成功,后面的不再判断。
let score = 85;
if (score >= 90) {
console.log("优秀"); // 不执行
} else if (score >= 80) {
console.log("良好"); // ✅ 执行这个
} else if (score >= 70) {
console.log("中等"); // 不执行(前面已匹配)
} else {
console.log("需努力"); // 不执行
}
// 输出:良好
关键点:即使 score = 85 同时满足 score >= 70 和 score >= 80,也只会执行第一个匹配的 score >= 80 分支。条件按顺序判断,命中即停止。
1.4 多个独立 if 并列
多个独立的 if 语句,每个都会独立判断,可能执行多个。
let x = 8;
if (x > 0) {
console.log("正数"); // ✅ 执行
}
if (x > 3) {
console.log("大于3"); // ✅ 执行
}
if (x > 10) {
console.log("大于10"); // ❌ 不执行
}
if (x % 2 === 0) {
console.log("偶数"); // ✅ 执行
}
// 输出:正数 大于3 偶数
1.5 核心区别总结与避坑示例
| 写法 | 判断次数 | 最多执行分支数 | 适用场景 |
|---|---|---|---|
if-else if-else 链 |
直到匹配为止 | 1 个 | 单选:成绩等级、权限判断 |
多个独立 if |
全部判断 | 多个 | 多选:同时校验多个条件 |
避坑示例:
// ❌ 错误:想实现单选却用了多个独立 if
let grade = 85;
if (grade >= 60) {
console.log("及格");
}
if (grade >= 80) {
console.log("良好");
}
// grade = 85 时会同时输出 "及格" 和 "良好"(这是问题所在!)
// ✅ 正确:使用 else if
if (grade >= 80) {
console.log("良好");
} else if (grade >= 60) {
console.log("及格");
}
// grade = 85 只输出 "良好"
二、赋值与等于:=、==、 === 及真假值详解
2.1 赋值运算符 =
= 用于给变量赋值,不是比较。
let a = 5; // 把 5 赋给 a
let b = a; // 把 a 的值赋给 b
常见错误:在 if 条件中误用 = 而不是 ===。
let x = 10;
// ❌ 错误:这是赋值,不是比较
if (x = 5) { // 会把 5 赋给 x,整个表达式值为 5(true),条件永远为真
console.log("条件成立");
}
// ✅ 正确
if (x === 5) {
console.log("x 等于 5");
}
2.2 相等运算符 ==(松散相等)
== 会在比较前进行类型转换,再比较值是否相等。
console.log(5 == "5"); // true (字符串 "5" 转成数字 5)
console.log(0 == false); // true (false 转成 0)
console.log(null == undefined); // true (特殊规则)
console.log("" == false); // true (都转成 0)
console.log([] == false); // true (空数组转成 "" 再转 0)
2.3 全等运算符 ===(严格相等)
=== 不进行类型转换,类型不同直接返回 false。
console.log(5 === "5"); // false (类型不同:数字 vs 字符串)
console.log(0 === false); // false (数字 vs 布尔)
console.log(null === undefined); // false (类型不同)
console.log("" === false); // false
console.log(NaN === NaN); // 特别注意:此时结果为 false要用 isNaN()判断
2.4 == 和 === 的完整对比表
| 比较 | == 结果 | === 结果 |
|---|---|---|
5 == "5" |
✅ true | ❌ false |
0 == false |
✅ true | ❌ false |
null == undefined |
✅ true | ❌ false |
"" == false |
✅ true | ❌ false |
"abc" == "abc" |
✅ true | ✅ true |
5 == 5 |
✅ true | ✅ true |
NaN == NaN |
❌ false | ❌ false |
建议:始终使用 === 和 !==,避免因隐式类型转换导致难以排查的 bug。
2.5 真假值(Truthy 和 Falsy)完整列表
在 if、while、&&、|| 等需要布尔值的上下文中,值会被自动转换为布尔类型。
假值(Falsy) — 转为 false 的值(共 8 个):
| 值 | 说明 |
|---|---|
false |
布尔假 |
0 |
数字零 |
-0 |
负零 |
0n |
BigInt 零 |
"" |
空字符串 |
null |
空值 |
undefined |
未定义 |
NaN |
非数字 |
真值(Truthy) — 除以上 8 个值外,所有其他值都是 true:
// 这些容易被误认为 false,实际是 true
if ("0") console.log("执行"); // ✅ 非空字符串
if ("false") console.log("执行"); // ✅ 非空字符串
if ([]) console.log("执行"); // ✅ 空数组
if ({}) console.log("执行"); // ✅ 空对象
if (-1) console.log("执行"); // ✅ 非零数字
2.6 常见陷阱与最佳实践
陷阱 1:判断数组是否为空
let arr = [];
// ❌ 错误:空数组是 true,这个判断永远不会进入 else
if (arr) {
console.log("数组有元素"); // 实际上会执行这里!
} else {
console.log("数组为空");
}
// ✅ 正确
if (arr.length > 0) {
console.log("数组有元素");
} else {
console.log("数组为空");
}
陷阱 2:判断变量是否为 null 或 undefined
let value;
// ✅ 方式一:显式判断
if (value === null || value === undefined) {
console.log("值为空");
}
// ✅ 方式二:利用 == null(同时匹配 null 和 undefined)
if (value == null) {
console.log("值为空"); // value 为 null 或 undefined 时进入
}
// ❌ 注意:if (!value) 会把 0、""、false 也当作空
三、常用内置对象:Math 详解
3.1 Math 对象概述与常数
Math 是内置对象,提供数学常数和函数,不能作为函数调用,不能 new。
console.log(Math.PI); // 3.141592653589793
console.log(Math.E); // 2.718281828459045
console.log(Math.LN10); // 2.302585092994046
console.log(Math.SQRT2); // 1.4142135623730951
3.2 取整方法详解(floor/ceil/round/trunc)
| 方法 | 作用 | 示例 | 结果 |
|---|---|---|---|
Math.floor(x) |
向下取整(向小取) | Math.floor(3.9) |
3 |
Math.floor(-3.1) |
-4 | ||
Math.ceil(x) |
向上取整(向大取) | Math.ceil(3.1) |
4 |
Math.ceil(-3.9) |
-3 | ||
Math.round(x) |
四舍五入 | Math.round(3.4) |
3 |
Math.round(3.5) |
4 | ||
Math.round(-3.5) |
-3(注意:-3.5 四舍五入为 -3) | ||
Math.trunc(x) |
直接去掉小数部分 | Math.trunc(3.9) |
3 |
Math.trunc(-3.1) |
-3 |
// 实际应用:保留两位小数(截断方式,不是四舍五入)
let price = 3.4567;
let truncated = Math.floor(price * 100) / 100; // 3.45
let rounded = Math.round(price * 100) / 100; // 3.46
3.3 随机数生成(含随机整数封装)
Math.random() 返回 [0, 1) 的随机浮点数(包含 0,不包含 1)。
// 基础用法
let r = Math.random(); // 0.000... 到 0.999...
// 生成 [0, max) 的随机整数
function getRandomInt(max: number): number {
return Math.floor(Math.random() * max);
}
// 生成 [min, max] 的随机整数(包含两端)
function getRandomIntInRange(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(getRandomIntInRange(1, 100)); // 1~100 随机整数
3.4 最大值/最小值与数组展开
let max = Math.max(1, 5, 3, 9, 2); // 9
let min = Math.min(1, 5, 3, 9, 2); // 1
// 对数组求最大/最小值
let arr = [10, 20, 30, 5];
let arrMax = Math.max(...arr); // 30(使用展开运算符)
let arrMin = Math.min(...arr); // 5
3.5 常用数学运算方法
| 方法 | 作用 | 示例 |
|---|---|---|
Math.abs(x) |
绝对值 | Math.abs(-5) → 5 |
Math.pow(x, y) |
x 的 y 次方 | Math.pow(2, 3) → 8 |
Math.sqrt(x) |
平方根 | Math.sqrt(16) → 4 |
Math.cbrt(x) |
立方根 | Math.cbrt(27) → 3 |
Math.hypot(x, y) |
平方和的平方根 | Math.hypot(3, 4) → 5 |
// 实际应用:计算两点距离
function distance(x1: number, y1: number, x2: number, y2: number): number {
return Math.hypot(x2 - x1, y2 - y1);
}
四、常用内置对象:Date 详解
4.1 创建 Date 对象的多种方式
// 方式1:当前时间
let now = new Date();
// 方式2:时间戳(毫秒)
let fromTimestamp = new Date(1700000000000);
// 方式3:日期字符串
let fromString = new Date("2026-05-20T14:30:00");
// 方式4:年、月、日、时、分、秒、毫秒
// 注意:月份从 0 开始(0=一月,11=十二月)
let fromParams = new Date(2026, 4, 20, 14, 30, 0, 0);
// 2026年5月20日 14:30:00
4.2 获取年月日时分秒(含UTC)
let date = new Date();
// 本地时间
let year = date.getFullYear(); // 2026
let month = date.getMonth(); // 4(实际是5月,需要+1)
let day = date.getDate(); // 1-31
let weekday = date.getDay(); // 0-6(0=周日)
let hours = date.getHours(); // 0-23
let minutes = date.getMinutes(); // 0-59
let seconds = date.getSeconds(); // 0-59
let milliseconds = date.getMilliseconds(); // 0-999
// UTC 时间(世界协调时)
let utcHours = date.getUTCHours();
let utcMonth = date.getUTCMonth();
4.3 设置日期时间
let date = new Date();
date.setFullYear(2025);
date.setMonth(11); // 12月(月份0-11)
date.setDate(25); // 25日
date.setHours(10);
date.setMinutes(30);
date.setSeconds(0);
date.setMilliseconds(0);
4.4 时间戳操作与日期差计算
let date = new Date();
// 获取时间戳(毫秒,从1970-01-01开始)
let timestamp = date.getTime(); // 方式1
let timestamp2 = +date; // 方式2(一元加号)
let timestamp3 = Date.now(); // 方式3(静态方法,无需 new)
// 时间戳转 Date
let fromTs = new Date(timestamp);
// 计算两个日期之间的天数差
let start = new Date(2026, 0, 1); // 2026-01-01
let end = new Date(2026, 0, 10); // 2026-01-10
let diffMs = end.getTime() - start.getTime(); // 777600000 毫秒
let diffDays = diffMs / (1000 * 60 * 60 * 24); // 9 天
4.5 日期格式化(含自定义格式化函数)
let date = new Date();
// 内置格式化
console.log(date.toLocaleDateString()); // "2026/5/20"
console.log(date.toLocaleTimeString()); // "14:30:00"
console.log(date.toLocaleString()); // "2026/5/20 14:30:00"
console.log(date.toISOString()); // "2026-05-20T06:30:00.000Z"
// 自定义格式化函数
function formatDate(date: Date, format: string = "YYYY-MM-DD"): string {
let y = date.getFullYear();
let m = String(date.getMonth() + 1).padStart(2, "0");
let d = String(date.getDate()).padStart(2, "0");
let h = String(date.getHours()).padStart(2, "0");
let min = String(date.getMinutes()).padStart(2, "0");
let s = String(date.getSeconds()).padStart(2, "0");
return format
.replace("YYYY", String(y))
.replace("MM", m)
.replace("DD", d)
.replace("HH", h)
.replace("mm", min)
.replace("ss", s);
}
console.log(formatDate(new Date(), "YYYY年MM月DD日 HH:mm:ss"));
4.6 日期计算与比较(加N天、相差天数)
// 日期比较(直接用 > < == 比较时间戳)
let d1 = new Date(2026, 0, 1);
let d2 = new Date(2026, 0, 15);
console.log(d1 < d2); // true
// 计算 N 天后的日期
function addDays(date: Date, days: number): Date {
let result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
// 计算两个日期之间的天数差(绝对值)
function daysBetween(date1: Date, date2: Date): number {
const msPerDay = 1000 * 60 * 60 * 24;
const diffMs = Math.abs(date2.getTime() - date1.getTime());
return Math.floor(diffMs / msPerDay);
}
五、函数参数:可选参数与剩余参数详解
5.1 可选参数(?)与参数默认值对比
在参数名后加 ? 表示可选,未传值时值为 undefined。可选参数必须放在必选参数之后。
// 可选参数版本
function buildName(firstName: string, lastName?: string): string {
if (lastName) {
return `${firstName} ${lastName}`;
} else {
return firstName;
}
}
console.log(buildName("张三")); // "张三"
console.log(buildName("李", "四")); // "李 四"
// 参数默认值版本(推荐)
function greet(name: string = "Anonymous"): string {
return `Hello ${name}`;
}
console.log(greet()); // "Hello Anonymous"
console.log(greet("张三")); // "Hello 张三"
5.2 剩余参数(…rest)
用于接收不定数量的参数,以数组形式保存。剩余参数必须放在最后。
// 求和:接收任意多个数字
function sum(...numbers: number[]): number {
let total = 0;
for (let num of numbers) {
total += num;
}
return total;
}
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum()); // 0
// 混合使用
function multiply(base: number, ...factors: number[]): number[] {
return factors.map(factor => base * factor);
}
console.log(multiply(2, 3, 5, 7)); // [6, 10, 14]
5.3 可选参数与剩余参数混合使用
function logMessage(prefix: string, suffix?: string, ...messages: string[]): void {
for (let msg of messages) {
if (suffix) {
console.log(`${prefix}: ${msg}${suffix}`);
} else {
console.log(`${prefix}: ${msg}`);
}
}
}
logMessage("INFO", "!", "登录成功", "数据加载完成");
// INFO: 登录成功!
// INFO: 数据加载完成!
六、异常处理:try/catch/finally 与自定义错误详解
6.1 try-catch-finally 基本语法
try {
// 可能抛出异常的代码
let result = riskyOperation();
console.log("执行成功:", result);
} catch (error) {
// 捕获并处理错误
console.error("出错了:", error.message);
} finally {
// 无论是否出错都会执行(可选)
console.log("清理工作");
}
6.2 错误对象 Error 详解
catch 捕获到的 error 是 Error 类型,包含以下属性:
try {
throw new Error("自定义错误信息");
} catch (err) {
console.log(err.name); // "Error"
console.log(err.message); // "自定义错误信息"
console.log(err.stack); // 调用堆栈信息(调试用)
}
6.3 抛出自定义错误(throw)
使用 throw 主动抛出异常,可以抛出任何值,但建议抛出 Error 对象。
function divide(a: number, b: number): number {
if (b === 0) {
throw new Error("除数不能为0");
}
return a / b;
}
try {
let result = divide(10, 0);
} catch (err) {
console.log(err.message); // "除数不能为0"
}
6.4 自定义错误类(继承Error)
// 基础业务错误类
class BusinessError extends Error {
public code: number;
constructor(code: number, message: string) {
super(message);
this.name = "BusinessError";
this.code = code;
}
}
// 具体错误类型
class ValidationError extends BusinessError {
constructor(message: string) {
super(1001, message);
this.name = "ValidationError";
}
}
class NetworkError extends BusinessError {
constructor(message: string) {
super(2001, message);
this.name = "NetworkError";
}
}
// 使用示例
function validateAge(age: number): void {
if (age < 0) {
throw new ValidationError("年龄不能为负数");
}
if (age > 150) {
throw new ValidationError("年龄超出合理范围");
}
}
try {
validateAge(-5);
} catch (err) {
if (err instanceof ValidationError) {
console.log(`校验错误 [${err.code}]: ${err.message}`);
} else if (err instanceof NetworkError) {
console.log(`网络错误 [${err.code}]: ${err.message}`);
} else {
console.log("未知错误", err);
}
}
6.5 finally 的典型使用场景
// 场景1:资源释放
let isLoading = true;
try {
await fetchData();
} finally {
isLoading = false; // 无论成功还是失败,都结束加载状态
}
// 场景2:文件操作(伪代码)
function readFile(path: string): string {
let file = openFile(path);
try {
return file.read();
} finally {
file.close(); // 确保文件被关闭
}
}
6.6 异常处理最佳实践
// 1. 只捕获和处理你能处理的异常
try {
let data = JSON.parse(jsonString);
} catch (err) {
console.error("JSON解析失败:", err);
throw new Error("数据格式错误"); // 重新抛出
}
// 2. 不要吞掉异常
try {
riskyOp();
} catch (err) {
// ❌ 不要这样:什么都不做
}
// 3. 异步函数异常处理
async function fetchData(): Promise<void> {
try {
let response = await fetch("/api/data");
let data = await response.json();
} catch (err) {
console.error("请求失败:", err);
}
}
七、字符串常用操作详解
7.1 去除空格(trim/trimStart/trimEnd)
| 方法 | 作用 | 示例 |
|---|---|---|
trim() |
去除首尾空格 | " hello ".trim() → "hello" |
trimStart() |
去除开头空格 | " hello ".trimStart() → "hello " |
trimEnd() |
去除结尾空格 | " hello ".trimEnd() → " hello" |
7.2 大小写转换与忽略大小写比较
let str = "Hello World";
console.log(str.toUpperCase()); // "HELLO WORLD"
console.log(str.toLowerCase()); // "hello world"
function equalsIgnoreCase(a: string, b: string): boolean {
return a.toLowerCase() === b.toLowerCase();
}
console.log(equalsIgnoreCase("Hello", "HELLO")); // true
7.3 判断开头/结尾/包含(startsWith/endsWith/includes)
let filename = "document.pdf";
console.log(filename.endsWith(".pdf")); // true
console.log(filename.startsWith("doc")); // true
console.log(filename.includes("ume")); // true
// 第二个参数:起始搜索位置
console.log("hello world".includes("o", 5)); // true
7.4 截取子串(slice/substring 区别)
| 方法 | 特点 | 示例 |
|---|---|---|
slice(start, end) |
支持负数索引,end 不包含 | "hello".slice(1,4) → "ell" |
substring(start, end) |
负数当作 0,自动交换大小 | "hello".substring(4,1) → "ell" |
let str = "Hello World";
console.log(str.slice(0, 5)); // "Hello"
console.log(str.slice(-5)); // "World"
console.log(str.substring(0, 5)); // "Hello"
7.5 分割与拼接(split/join)
// split - 字符串 → 数组
let str = "apple,banana,orange";
let arr = str.split(","); // ["apple", "banana", "orange"]
let limited = str.split(",", 2); // ["apple", "banana"]
// join - 数组 → 字符串
let fruits = ["苹果", "香蕉", "橙子"];
console.log(fruits.join("-")); // "苹果-香蕉-橙子"
7.6 替换操作(replace/replaceAll/正则)
let text = "hello world hello";
console.log(text.replace("hello", "hi")); // "hi world hello"
console.log(text.replaceAll("hello", "hi")); // "hi world hi"
// 正则全局替换(大小写不敏感)
console.log(text.replace(/hello/gi, "hi"));
7.7 字符串遍历(for/for…of/Array.from)
let str = "Hello";
// 方式1:for 循环
for (let i = 0; i < str.length; i++) {
console.log(str[i]);
}
// 方式2:for...of(推荐,正确处理 Unicode)
for (let char of str) {
console.log(char);
}
// 方式3:转为数组
let chars = Array.from(str); // ["H", "e", "l", "l", "o"]
八、小数处理与类型互转详解(超详细)
8.1 小数取几位:toFixed() 的返回值陷阱与精度问题
toFixed(n) 将数字四舍五入为指定小数位数的字符串。
let num = 3.1415926;
console.log(num.toFixed(2)); // "3.14" (注意:是字符串!)
console.log(typeof num.toFixed(2)); // "string"
// 边界情况:浮点数精度导致四舍五入错误
let num2 = 1.005;
console.log(num2.toFixed(2)); // "1.00" ❌ 预期应该是 "1.01"
// 解决方案:使用更精确的算法
function preciseRound(num: number, decimals: number): number {
const factor = Math.pow(10, decimals);
return Math.round(num * factor) / factor;
}
console.log(preciseRound(1.005, 2)); // 1.01 ✅
// 如果需要得到数字类型
let fixedNum = Number(num.toFixed(2)); // 3.14(数字)
8.2 判断是否为有效数字:isNaN() / Number.isNaN() / isFinite() 详解
isNaN() vs Number.isNaN() 的区别:
| 方法 | 是否会先转换类型 | isNaN("abc") |
isNaN(NaN) |
|---|---|---|---|
isNaN() |
✅ 是(先转数字) | true |
true |
Number.isNaN() |
❌ 否(不转换) | false |
true |
// isNaN() - 会先尝试将参数转换为数字
console.log(isNaN(NaN)); // true
console.log(isNaN("abc")); // true("abc" 转数字是 NaN)
console.log(isNaN("123")); // false("123" 转数字是 123)
console.log(isNaN(undefined)); // true
// Number.isNaN() - 不转换,只有真正的 NaN 才返回 true
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("abc")); // false("abc" 不是 NaN 类型)
console.log(Number.isNaN(123)); // false
// isFinite() - 判断是否为有限数字
console.log(isFinite(123)); // true
console.log(isFinite(Infinity)); // false
console.log(isFinite(NaN)); // false
console.log(isFinite("123")); // true(会先转换)
// Number.isFinite() - 不转换版本
console.log(Number.isFinite("123")); // false(类型不是 number)
最佳实践:判断字符串是否可以转为有效数字
function isValidNumber(str: string): boolean {
// 去除空格后,空字符串返回 false
let trimmed = str.trim();
if (trimmed === "") return false;
// 使用 Number() 转换并检查是否为 NaN
let num = Number(trimmed);
return !isNaN(num) && isFinite(num);
}
console.log(isValidNumber("123")); // true
console.log(isValidNumber("12.34")); // true
console.log(isValidNumber("-5.6")); // true
console.log(isValidNumber("123px")); // false
console.log(isValidNumber("")); // false
console.log(isValidNumber(" ")); // false
console.log(isValidNumber("abc")); // false
8.3 字符串转数字:Number / parseInt / parseFloat / 一元加号 全面对比
| 方法 | "123" |
"12.34" |
"123px" |
"abc" |
"" |
" " |
|---|---|---|---|---|---|---|
Number() |
123 | 12.34 | NaN | NaN | 0 | 0 |
parseInt(str,10) |
123 | 12 | 123 | NaN | NaN | NaN |
parseFloat() |
123 | 12.34 | 123 | NaN | NaN | NaN |
+str |
123 | 12.34 | NaN | NaN | 0 | 0 |
// 详细示例
console.log(Number("123")); // 123
console.log(Number("12.34")); // 12.34
console.log(Number("123px")); // NaN(❌ 注意:带单位会失败)
console.log(Number("")); // 0(⚠️ 空字符串转为 0)
console.log(Number(" ")); // 0
console.log(parseInt("123px", 10)); // 123(✅ 忽略非数字后缀)
console.log(parseInt("12.34", 10)); // 12(只取整数部分)
console.log(parseInt("abc123", 10)); // NaN
console.log(parseInt("08", 10)); // 8(必须指定进制)
console.log(parseFloat("12.34.56")); // 12.34(遇到第二个点停止)
console.log(parseFloat("12.34abc")); // 12.34
// 一元加号
console.log(+"123"); // 123
console.log(+"12.34"); // 12.34
console.log(+"123px"); // NaN
8.4 数字转字符串:toString / String / 拼接
| 方法 | 示例 | 特点 |
|---|---|---|
.toString() |
(123).toString() → "123" |
可指定进制 |
String() |
String(123) → "123" |
处理 null/undefined 友好 |
| 拼接空字符串 | 123 + "" → "123" |
隐式转换 |
let num = 123.456;
console.log(num.toString()); // "123.456"
console.log(num.toFixed(2)); // "123.46"(同时做四舍五入)
console.log(String(num)); // "123.456"
console.log(num + ""); // "123.456"
// 进制转换
let n = 255;
console.log(n.toString(16)); // "ff"(十六进制)
console.log(n.toString(2)); // "11111111"(二进制)
8.5 浮点数精度问题与多种解决方案
8.5.1 问题演示(0.1 + 0.2 ≠ 0.3)
JavaScript/ArkTS 使用 IEEE 754 双精度浮点数,某些小数无法精确表示。
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
console.log(0.1 + 0.7); // 0.7999999999999999
console.log(0.2 + 0.4); // 0.6000000000000001
console.log(0.3 - 0.2); // 0.09999999999999998
8.5.2 解决方案一:先乘整数再除(推荐)
原理:将小数乘以 10 的 n 次方转为整数运算,结果再除以同一个倍数。
// 通用函数:精确加法
function add(a: number, b: number, decimals: number = 10): number {
const factor = Math.pow(10, decimals);
return (Math.round(a * factor) + Math.round(b * factor)) / factor;
}
console.log(add(0.1, 0.2)); // 0.3 ✅
console.log(add(0.1, 0.7)); // 0.8 ✅
// 精确减法
function subtract(a: number, b: number, decimals: number = 10): number {
const factor = Math.pow(10, decimals);
return (Math.round(a * factor) - Math.round(b * factor)) / factor;
}
// 精确乘法
function multiply(a: number, b: number, decimals: number = 10): number {
const factor = Math.pow(10, decimals);
return (Math.round(a * factor) * Math.round(b * factor)) / (factor * factor);
}
// 精确除法
function divide(a: number, b: number, decimals: number = 10): number {
if (b === 0) throw new Error("除数不能为0");
const factor = Math.pow(10, decimals);
return (Math.round(a * factor) / Math.round(b * factor));
}
// 使用示例
console.log(0.1 * 0.2); // 0.020000000000000004
console.log(multiply(0.1, 0.2)); // 0.02 ✅
8.5.3 解决方案二:精度容差法(比较两个浮点数)
不追求完全相等,而是判断差值是否小于一个极小值(epsilon)。
// 比较两个浮点数是否相等
function isFloatEqual(a: number, b: number, epsilon: number = 1e-10): boolean {
return Math.abs(a - b) < epsilon;
}
console.log(isFloatEqual(0.1 + 0.2, 0.3)); // true ✅
// 比较两个浮点数的大小关系
function isFloatGreater(a: number, b: number, epsilon: number = 1e-10): boolean {
return a - b > epsilon;
}
function isFloatLess(a: number, b: number, epsilon: number = 1e-10): boolean {
return b - a > epsilon;
}
console.log(isFloatGreater(0.1 + 0.2, 0.3)); // false(视为相等)
console.log(isFloatGreater(0.3, 0.1 + 0.2)); // false
8.5.4 解决方案三:使用 toFixed + Number(简单场景)
function safeAdd(a: number, b: number, decimals: number = 10): number {
return Number((a + b).toFixed(decimals));
}
console.log(safeAdd(0.1, 0.2)); // 0.3 ✅
console.log(safeAdd(0.1, 0.7)); // 0.8 ✅
// 注意事项:toFixed 本身也有精度问题
console.log(1.005.toFixed(2)); // "1.00" ❌ 还是有问题
// 结合前面写过的 preciseRound 函数
function betterAdd(a: number, b: number, decimals: number = 10): number {
return preciseRound(a + b, decimals);
}
8.5.5 解决方案四:使用第三方库(高精度场景)
如果需要极高精度(如金融计算),可考虑使用 decimal.js 或 big.js(需要额外安装)。
// 伪代码示例(需安装 decimal.js)
// import Decimal from 'decimal.js';
// let a = new Decimal(0.1);
// let b = new Decimal(0.2);
// let result = a.plus(b).toNumber(); // 0.3
8.6 实战综合示例:表单价格输入处理
// 模拟用户输入的价格字符串,需要转为数字(保留2位小数)
function parseAndRoundPrice(input: string): number | null {
// 1. 去除首尾空格
let trimmed = input.trim();
if (trimmed === "") return null;
// 2. 转为数字
let num = Number(trimmed);
// 3. 判断是否为有效数字
if (isNaN(num) || !isFinite(num)) {
return null;
}
// 4. 使用精确四舍五入保留2位小数(先乘后除方式,避免 toFixed 精度问题)
let rounded = Math.round(num * 100) / 100;
return rounded;
}
// 测试
console.log(parseAndRoundPrice("12.345")); // 12.35
console.log(parseAndRoundPrice(" -5.678 ")); // -5.68
console.log(parseAndRoundPrice("100px")); // null
console.log(parseAndRoundPrice("")); // null
console.log(parseAndRoundPrice(" ")); // null
// 金额累加(防止浮点数精度问题)
function sumPrices(prices: number[]): number {
// 先乘100转为整数(分),累加后再除以100
let totalCents = prices.reduce((sum, price) => {
return sum + Math.round(price * 100);
}, 0);
return totalCents / 100;
}
let prices = [0.1, 0.2, 0.3];
console.log(sumPrices(prices)); // 0.6 ✅(而不是 0.6000000000000001)
九、总结速查表
分支判断速查
| 场景 | 正确写法 | 错误写法 |
|---|---|---|
| 单选(等级、权限) | if-else if-else |
多个独立 if |
| 多选(同时校验多个条件) | 多个独立 if |
if-else if |
比较运算符速查
| 符号 | 名称 | 推荐度 |
|---|---|---|
= |
赋值 | 仅用于赋值 |
== |
松散相等 | ❌ 不推荐 |
=== |
严格相等 | ✅ 推荐 |
假值列表(Falsy)
false、0、-0、0n、""、null、undefined、NaN
Math 常用方法速查
| 方法 | 作用 | 示例结果 |
|---|---|---|
Math.floor(3.9) |
向下取整 | 3 |
Math.ceil(3.1) |
向上取整 | 4 |
Math.round(3.5) |
四舍五入 | 4 |
Math.random() |
随机数 [0,1) | 0~0.999 |
Math.max(1,5,3) |
最大值 | 5 |
Math.abs(-5) |
绝对值 | 5 |
Math.pow(2,3) |
幂运算 | 8 |
Date 常用方法速查
| 方法 | 返回值范围 |
|---|---|
getFullYear() |
2026 |
getMonth() |
0~11(需+1) |
getDate() |
1~31 |
getDay() |
0~6(0=周日) |
getTime() |
时间戳(毫秒) |
判断数字有效性速查
| 场景 | 推荐方法 |
|---|---|
| 判断一个值是否为真正的 NaN | Number.isNaN(value) |
| 判断字符串是否可转为有效数字 | !isNaN(Number(str)) && str.trim() !== "" |
| 判断一个数字是否为有限数 | isFinite(num) |
字符串转数字速查
| 输入 | Number() |
parseInt() |
parseFloat() |
|---|---|---|---|
"123" |
123 | 123 | 123 |
"12.34" |
12.34 | 12 | 12.34 |
"123px" |
NaN | 123 | 123 |
"abc" |
NaN | NaN | NaN |
"" |
0 | NaN | NaN |
浮点数精度问题解决方案
| 方案 | 适用场景 | 示例 |
|---|---|---|
| 先乘整数再除 | 加减乘除通用 | Math.round(a*100 + b*100)/100 |
| 精度容差法 | 比较两个浮点数 | Math.abs(a-b) < 1e-10 |
| toFixed + Number | 简单场景(注意精度) | Number((a+b).toFixed(10)) |
希望这份超详细的指南能帮你彻底理解鸿蒙ArkTS中的这些基础知识点。如果还有任何疑问,欢迎在评论区留言交流!
更多推荐

所有评论(0)