我想判断一个计算结果是不是 NaN,顺手写了 x === NaN,结果它永远返回 false、NaN 一路混了过去到处传播,因为 NaN 是唯一一个连等于它自己都不成立的值的深度复盘

我有段计算,某些异常输入下结果会变成 NaN,我想判断一下是 NaN 就用默认值兜底,写了 if (result === NaN) result = 0。结果完全没生效:result === NaN 永远返回 false,哪怕 result 真的就是 NaN 也拦不住,于是 NaN 一路混过去、参与后续计算又产生新的 NaN、到处传播,最后页面一片 NaN 却定位不到源头。复盘才搞懂:NaN 是一个极其特殊的值,它不等于任何值包括它自己——IEEE 754 规定任何与 NaN 的比较(==/===/)都返回 false(只有 != 返回 true),所以 NaN === NaN 是 false,这是整个语言里唯一一个不等于自己的值,打破了 x === x 这个最基本的规律。因此用 === NaN 永远判不出 NaN,indexOf(NaN) 也找不到(用 ===)。判断一个值是不是 NaN 要用专门的 Number.isNaN(x) 或 Object.is。我用通用的相等判断去判一个连相等判断都不适用的特例,自然失效。这篇复盘从故障现场讲到 NaN 的特殊性、为何 === 判不出、正确判断方法,再到用 Number.isNaN(别用全局 isNaN)、源头校验拦截、includes 查找、Number.isFinite 排除的完整正解,以及其他特例打破常规规律的坑(null/typeof、浮点、-0),和普适规律有不遵守它的特例、通用工具对特例会悄悄失效、识别特例并用专门手段的认知。

我想判断一个计算结果是不是 NaN,顺手写了 x === NaN,结果它永远返回 false、NaN 一路混了过去到处传播,因为 NaN 是唯一一个连"等于它自己"都不成立的值:一次没识别 NaN 这个打破常规的特例、硬用相等判断它的深度复盘

那个"页面上到处显示 NaN、却查不出哪来的"的 bug,让我重新认识了 JavaScript 里的 NaN。我有段计算,某些异常输入下结果会变成 NaN(Not-a-Number),我想"那我判断一下,是 NaN 就用默认值兜底",于是写了 if (result === NaN) result = 0;。结果完全没生效:result === NaN 永远返回 false——哪怕 result 真的就是 NaN,这个判断也拦不住它;于是 NaN 一路混了过去,参与后续计算又产生新的 NaN,到处传播,最后页面上一片 NaN,我却定位不到源头。复盘 NaN 的特性,我才彻底搞懂,后背发凉:问题出在 NaN 是一个极其特殊的值——它不等于任何值,包括它自己IEEE 754 浮点标准规定:任何与 NaN 的比较(=====<>)都返回 false(!= 除外,返回 true);所以 NaN === NaNfalse——这是整个语言里唯一一个"不等于自己"的值,它打破了"任何东西都等于它自己(x === x)"这个我们视为天经地义的最基本规律;因此=== NaN 永远判不出 NaN(因为没有任何值会 === NaN,连 NaN 自己都不会);同理 [1, NaN].indexOf(NaN)找不到(indexOf 内部用 ===);判断一个值是不是 NaN,要用专门的方法 Number.isNaN(x)(或 Object.is(x, NaN)),它们专门处理了 NaN 这个特例;用"通用的相等判断"去判一个"连相等判断都不适用的特例",自然判不出来。根本原因是:NaN 是唯一不等于任何值(包括自己)的值,与 NaN 的所有比较(==/===/</>)都返回 false;所以 x === NaN 永远为 false、判不出 NaN;要用专门的 Number.isNaN(x) 判断;我用通用的相等判断去判一个不适用相等判断的特例,自然失效。问题的根,是用 === 判断 NaN——NaN 不等于自己、=== NaN 永远 false;应该用专门的 Number.isNaN(x);根源是没识别 NaN 这个打破常规的特例、硬套了通用的相等判断。这篇就把这次"NaN 判断"的坑,从头到尾复盘一遍。

故障现场:result === NaN 永远是 false

问题在于 NaN 不等于自己、=== 判不出它:

// 我的写法: 用 === 判断 NaN(想兜底)
let result = compute(input);   // 某些异常输入下 result 是 NaN
if (result === NaN) {          // ✗ 永远 false! 拦不住 NaN
  result = 0;
}
// NaN 没被拦住 → 继续参与计算 → 产生更多 NaN → 到处传播 → 页面一片 NaN。

// 各种与 NaN 比较的诡异结果:
console.log(NaN === NaN);   // false  ← NaN不等于它自己!(唯一这样的值)
console.log(NaN == NaN);    // false
console.log(NaN !== NaN);   // true   ← 反而"不等于自己"是true
console.log(NaN < 1, NaN > 1, NaN === 1);  // false false false  ← 和任何值比都是false
console.log([1, NaN, 3].indexOf(NaN));     // -1  ← 找不到!(indexOf用===)
console.log([1, NaN, 3].includes(NaN));    // true ← includes用的是SameValueZero, 能找到NaN!

// 正确判断 NaN:
console.log(Number.isNaN(result));   // ✓ 专门判断是不是NaN
console.log(Object.is(result, NaN)); // ✓ Object.is 也能正确判NaN

/*
为什么 NaN 这么特殊:
  - NaN = Not-a-Number, 表示"无效的/未定义的数值结果"(如 0/0、Math.sqrt(-1)、parseInt("abc"));
  - IEEE 754 标准规定: 任何与NaN的比较(==、===、<、>、<=、>=)都返回 false;
    (只有 != / !== 返回 true)
  - 所以 NaN === NaN 是 false —— 它是语言里唯一"不等于自己"的值;
  - 道理上: NaN表示"无效结果", 两个"无效结果"未必是"同一个东西", 所以不判它们相等(有其逻辑);
  - 后果: 用 === / == / indexOf(内部用===) 都判不出NaN; 必须用专门方法。

正确判断/处理 NaN:
  - Number.isNaN(x): 严格判断x是不是NaN(推荐; 不会像全局isNaN那样把"abc"也当NaN);
  - Object.is(x, NaN): 也能正确判NaN(Object.is 把NaN视为等于NaN);
  - 注意区分 全局 isNaN(x): 它会先把x转数字再判, isNaN("abc")是true(因为"abc"转数字是NaN), 易误判;
    判"是不是NaN"用 Number.isNaN, 别用全局 isNaN。

★ 核心: NaN不等于任何值(包括它自己), 与NaN的所有比较(==/===/<>)都返回false;
  所以 ===NaN 永远判不出NaN; 要用 Number.isNaN(x)(或Object.is) 这种专门处理了NaN特例的方法。

看着 NaN === NaN 居然是 false,我又错愕又恍然:"我一直以为'任何东西都等于它自己'是铁律——x === x 不永远是 true 吗?谁知道 NaN 这个家伙偏偏不等于自己!我用 === NaN 去判它,等于用一把对它根本不起作用的尺子量它,当然量不出来。"这个坑最反直觉的地方在于:它打破了一个我们最不假思索的基本假设——"一个值总等于它自己(自反性)";这个假设对其他所有值都成立,唯独 NaN 例外;正因为它是"唯一的例外",最容易被忽略;而且 === NaN 这个写法语法完全正确、不报错,只是默默地永远不成立,让 NaN 悄悄溜过下面就来拆解,NaN 该怎么判断和处理。

第一件事:搞懂 NaN 的特殊性与正确判断

我顺着这次事故,把 NaN 和"打破常规的特例"彻底理清了。

NaN 为什么不等于自己? 怎么正确判断/处理它?

【核心: NaN不等于任何值(含自己)、与NaN的所有比较都返回false、是唯一不满足x===x的值; ===NaN永远判不出NaN;
   用Number.isNaN(x)/Object.is专门判; 别用全局isNaN(会转换误判); 识别这种打破常规的特例、用专门手段处理】

1. NaN 是什么:
   - NaN = Not-a-Number, 表示"无效/未定义的数值结果"(0/0、Math.sqrt(-1)、Number("abc")、undefined算术等);
   - 它是 number 类型(typeof NaN === 'number'), 但代表"不是一个有效的数"。

2. NaN 的特殊性(打破常规):
   - 与NaN的所有比较(==、===、<、>、<=、>=)都返回false; 只有 != / !== 返回true;
   - 所以 NaN === NaN 是 false —— 它是语言里唯一"不等于自己"的值(违反了 x===x 的自反性);
   - 道理: 两个"无效结果"未必是同一个东西, 故不判相等(IEEE 754规定)。

3. 由此带来的坑:
   - x === NaN / x == NaN 永远false, 判不出NaN;
   - indexOf(NaN) 找不到(内部用===); (但 includes(NaN) 能找到, 用SameValueZero);
   - 排序/比较含NaN的数组结果诡异; NaN参与运算结果还是NaN(会传播)。

4. 正确判断 NaN:
   - Number.isNaN(x): 严格判断x是否就是NaN(推荐);
   - Object.is(x, NaN): 也正确(Object.is视NaN等于NaN, 且区分+0/-0);
   - 别用全局 isNaN(x): 它先把x转数字再判, isNaN("abc")=true(误判非数字为NaN); 判NaN本身用Number.isNaN。

5. 正确处理 NaN(防患于未然):
   - 计算前校验输入(parseInt/Number结果、除数、用户输入), 别让NaN产生;
   - 产生NaN的源头(0/0、对非数字做算术、JSON里的异常)及早拦截;
   - 用 Number.isNaN 在边界判断并兜底, 别让NaN往下传播(它会污染后续所有计算)。

6. 本质: 再普适的规律也可能有"打破它的特例"; 遇到行为反常的, 别硬套通用规则, 用专门手段
   - "x等于自己"看似铁律, NaN就是例外; 用通用的===去判一个"不适用===的特例", 必然失效;
   - 要能识别这类"违反常规假设的特例", 并用为它准备的专门方法(Number.isNaN)处理。

一句话: NaN不等于任何值(含自己)、与它的所有比较都返回false、是唯一不满足x===x的值; ===NaN永远判不出NaN;
   用Number.isNaN(x)/Object.is判、别用全局isNaN; 计算前校验防NaN产生; 识别打破常规的特例用专门手段。

这套认知,是整个坑的根。NaN 是什么:Not-a-Number,表示无效/未定义的数值结果(0/0、sqrt(-1)、Number("abc")),是 number 类型但代表"不是有效的数"特殊性:与 NaN 的所有比较都返回 false、只有 != 返回 true,是唯一不等于自己(违反 x===x 自反性)的值带来的坑:===NaN/==NaN 永远 false 判不出、indexOf(NaN) 找不到(但 includes 能)、NaN 参与运算传播正确判断:Number.isNaN(x)(推荐)、Object.is(x, NaN);别用全局 isNaN(会转换误判)正确处理:计算前校验输入防 NaN 产生、源头拦截、边界用 Number.isNaN 兜底别让它传播本质:再普适的规律也可能有打破它的特例;用通用规则(===)判一个不适用它的特例(NaN)必然失效,要识别特例用专门手段一句话:NaN 不等于任何值(含自己)、与它的所有比较都返回 false、是唯一不满足 x===x 的值;===NaN 永远判不出 NaN;用 Number.isNaN(x)/Object.is 判、别用全局 isNaN;计算前校验防 NaN 产生;识别打破常规的特例用专门手段。

第二件事:正解——用 Number.isNaN 判断,并在源头拦截

知道了 NaN 不等于自己,正解就清楚了:用专门方法判断,并在计算边界拦住 NaN。

// 正解1: 判断 NaN 用 Number.isNaN(本次该用的)
let result = compute(input);
if (Number.isNaN(result)) {     // ✓ 专门处理了NaN特例, 能判出来
  result = 0;                    // 兜底
}

// 正解2: 区分 Number.isNaN 和 全局 isNaN(别用全局isNaN判"是不是NaN")
Number.isNaN("abc");   // false  ← "abc"不是NaN(它是字符串), 严格正确
isNaN("abc");          // true   ← 全局isNaN先把"abc"转数字(=NaN)再判, 误判! 别用它判NaN
Number.isNaN(NaN);     // true   ← 只有真的是NaN才true

// 正解3: 在源头/边界拦截 NaN, 别让它产生和传播
function safeParse(s) {
  const n = Number(s);
  if (Number.isNaN(n)) throw new Error(`无法解析为数字: ${s}`);  // 边界处就拦, 别让NaN流进系统
  return n;
}
// 用户输入、parseInt/Number结果、除法(防0)、JSON里的数值 —— 在进入计算前校验。

// 正解4: 查找/去重含NaN时, 注意 indexOf vs includes
[1, NaN].indexOf(NaN);    // -1   ← indexOf用===, 找不到NaN
[1, NaN].includes(NaN);   // true ← includes用SameValueZero, 能找到NaN
[...new Set([NaN, NaN])]; // [NaN] ← Set用SameValueZero, NaN去重后只留一个

// 正解5: 计算结果做合理性校验
const avg = total / count;             // count为0时 avg是NaN(0/0)或Infinity
if (!Number.isFinite(avg)) avg = 0;    // Number.isFinite 同时排除 NaN 和 Infinity, 更稳

// 反例(别这样):
// if (x === NaN) ...      // ✗ 永远false, 判不出
// if (isNaN(x)) ...        // △ 全局isNaN会把非数字也判true, 易误判; 判NaN用Number.isNaN

// 核心: 判NaN用Number.isNaN(x)(或Object.is), 别用===NaN(永远false)、别用全局isNaN(会误判);
//   在计算源头/边界校验拦截NaN, 别让它传播; 含NaN查找用includes、Number.isFinite排除NaN和Infinity。

这套正解的关键,是用"专门为 NaN 准备的方法"判断它,并在它产生的源头就拦住判断用 Number.isNaN:它专门处理了 NaN 特例,能判出来——这正是本次该用的(=== NaN 永远 false)。区分全局 isNaN:全局 isNaN("abc") 会先转数字再判、误判非数字为 NaN,判 NaN 本身用 Number.isNaN源头/边界拦截:用户输入、parseInt/Number 结果、除法在进入计算前校验,别让 NaN 产生和传播。查找含 NaN 用 includes(indexOf 用 === 找不到)、计算结果用 Number.isFinite同时排除 NaN 和 Infinity。

第三件事:其他几个"特例打破常规规律"的坑

顺着这次 NaN,我把"特例不遵守通用规律、却被硬套通用规则"的几类坑也一并理了:

几类"特例打破常规规律"的坑:

坑1: NaN !== NaN(本篇)——打破"x等于自己"; 用Number.isNaN, 别用===。

坑2: typeof null === 'object'——null是个特例(历史bug), 判null用 x === null, 别靠typeof;
   typeof判类型时要单独处理null。

坑3: 0.1 + 0.2 !== 0.3(浮点精度, 同554)——打破"小数运算精确"; 用误差范围比, 别用===。

坑4: -0 === 0 为true但 1/-0 是-Infinity——-0是特例; 需区分用 Object.is(x, -0)。

坑5: [] == false 为true、[] == ![] 为true(==隐式转换怪规则, 同567)——空数组的特例;
   用===避免, 别依赖==的怪转换。

坑6: typeof NaN === 'number'——NaN是"不是数字"却是number类型; 别以为是number就是有效数字。

坑7: 时区/闰秒/夏令时的日期特例(同572/579)——某些日期不遵守"每天24小时""每分钟60秒";
   日期计算别想当然, 用成熟库。

共同的根: 任何"普适的规律/假设"(x等于自己、数运算精确、类型判断可靠、加减天数得对应日期),
   在某些"特殊值/边界情况"上都可能【不成立】; 用"基于通用规律的通用做法"去处理这些特例, 必然出错;
   要能【识别这些打破常规的特例】, 并用【为它们专门准备的处理方式】, 而非盲目套用通用规则。

这些坑看似不同,根却是同一个:任何"普适的规律/假设"(x 等于自己、数运算精确、类型判断可靠),在某些"特殊值/边界情况"上都可能不成立;用"基于通用规律的通用做法"去处理这些特例,必然出错;要能识别这些打破常规的特例,并用为它们专门准备的处理方式认清这个根("普适规律有特例,遇反常者别硬套通用规则、用专门手段"),才不会被各种"例外"绊倒。

第四件事:NaN 比较行为 / 判断方法——两张对照表

我把 NaN 的各种比较行为、以及判断方法,整理成对照表,贴在了团队的 JS 规范里:

表达式 结果 说明
NaN === NaN false 不等于自己(唯一)
NaN == NaN false 同上
NaN !== NaN true 反而"不等于自己"为真
NaN < 1 / NaN > 1 false 与任何值比都 false
[NaN].indexOf(NaN) -1 indexOf 用 ===,找不到
[NaN].includes(NaN) true includes 用 SameValueZero
typeof NaN 'number' 是 number 类型但非有效数
判断方法 对 NaN 对 "abc" 推荐
x === NaN false(错) false ✗ 永远判不出
isNaN(x) 全局 true true(误判) △ 会转换,易误判
Number.isNaN(x) true false ✓ 严格,推荐
Object.is(x, NaN) true false ✓ 也正确

这两张表的核心,第一张是NaN 与任何值的相等/大小比较都是 false(它不等于自己),所以 === NaNindexOf 都判不出它;第二张是判 NaN 要用 Number.isNaN(x)(严格)或 Object.is,别用 === NaN(永远 false)或全局 isNaN(会把非数字误判)。记住一条:判断 NaN,记住一个方法就够——Number.isNaN(x)

第五件事:关于 NaN 的几组容易想当然的认知

这次事故也让我厘清了几组关于 NaN 的、容易想当然的概念:

直觉以为 实际上
任何值都等于它自己(x === x) NaN 不等于自己,是唯一例外
x === NaN 能判出 NaN 永远 false,判不出
typeof NaN 不是 number 是 'number'(但代表无效数)
indexOf 能找到数组里的 NaN 找不到(用 ===);includes 能
全局 isNaN 就是判 NaN 它会先转数字,非数字也判 true
NaN 不影响后续计算 NaN 参与运算还是 NaN,会传播污染
判 NaN 没什么讲究 必须用 Number.isNaN,普通比较都失效

这张表里,我栽的是第一行和第二行:把"任何值都等于它自己"当成了铁律,用 === NaN 去判 NaN,完全没想到 NaN 偏偏不等于自己、这个判断永远不成立厘清这些,核心是一个意识:NaN 是 JavaScript 里一个"打破最基本规律(不等于自己)"的特例;对这种特例,通用的相等判断完全失效,必须用专门为它准备的 Number.isNaN——遇到行为反常的东西,别硬用通用规则去套,要识别它的特殊性、用对的工具。

第六件事:处理数值 / 判断特殊值时,我现在的自检习惯

现在每当我要判断一个值是不是某种"特殊值"(NaN/null/-0 等),我都会先按这张图问自己:

这张图的精髓,是"特殊值不一定遵守通用相等规律、用为它准备的专门方法判"先问这个特殊值遵不遵守通用规律(NaN 不遵守就用 Number.isNaN、null 用 ===null)、再源头防它产生这套习惯,让我从"一律用 === 判"变成了"识别特例、用专门方法"——核心始终是:NaN 不等于任何值(含自己)、与它的所有比较都返回 false、是唯一不满足 x===x 的值;===NaN 永远判不出 NaN;用 Number.isNaN/Object.is 判;识别打破常规的特例用专门手段。

我立下的几条规矩

这场"=== NaN 永远 false、NaN 到处传播"的事故,换来了我处理数值时,刻进骨子里的几条铁律:

  1. NaN 不等于任何值(包括它自己);与 NaN 的所有比较(==/===/</>)都返回 false,只有 != 返回 true。
  2. NaN 是语言里唯一"不等于自己"的值;x === NaN 永远 false,判不出 NaN。
  3. 判断 NaN 用 Number.isNaN(x)(推荐)或 Object.is(x, NaN)。
  4. 别用全局 isNaN(x) 判 NaN:它会先转数字,把非数字(如 "abc")也判为 true。
  5. indexOf(NaN) 找不到(用 ===);用 includes(NaN) 或 Set 能正确处理。
  6. typeof NaN 是 'number',但它代表"无效数";别以为是 number 就是有效数字。
  7. 在源头/边界校验拦截 NaN(输入、parseInt/Number、除 0),用 Number.isFinite 排除 NaN 和 Infinity,别让它传播。

附:几个一跑就明白的 NaN 小实验

为了让团队直观感受 NaN 的"不等于自己",我写了几个一跑就明白的小实验,贴在内部 wiki 上。

// 实验1: NaN 不等于它自己(整个语言唯一)
console.log(NaN === NaN);        // false ← 唯一不等于自己的值!
console.log(NaN !== NaN);        // true  ← 可以用它来"检测"NaN(老技巧): x !== x 为true则x是NaN

// 实验2: 各种判断方法的对比
const x = 0 / 0;                  // NaN
console.log(x === NaN);          // false (判不出)
console.log(isNaN(x));           // true
console.log(isNaN("abc"));       // true  (误判! "abc"不是NaN, 但全局isNaN转换后判true)
console.log(Number.isNaN(x));    // true  ✓
console.log(Number.isNaN("abc"));// false ✓ (严格: "abc"不是NaN)

// 实验3: NaN 在数组里"找不到"
const arr = [1, NaN, 3];
console.log(arr.indexOf(NaN));   // -1   (indexOf用===, 找不到)
console.log(arr.includes(NaN));  // true (includes用SameValueZero, 找得到)
console.log(arr.findIndex(Number.isNaN));  // 1 (用Number.isNaN找)

// 实验4: NaN 会传播污染
console.log(NaN + 1, NaN * 2, Math.max(1, NaN));  // NaN NaN NaN ← 一旦有NaN, 结果全成NaN

// 实验5: 哪些操作产生NaN(在源头警惕)
console.log(0/0, Math.sqrt(-1), Number("abc"), parseInt("x"), undefined + 1);
// NaN NaN NaN NaN NaN ← 这些都产生NaN, 计算前要校验

// 经典技巧: x !== x 为true 等价于 x是NaN(因为只有NaN不等于自己)——但可读性差, 推荐Number.isNaN。

这几个实验的价值,在于把"NaN 不等于自己"这个反直觉的特性,变成了亲眼可见的输出(NaN === NaN 居然是 falsex !== x 居然是 true)。那种"它怎么连自己都不认"的意外,会牢牢记住"判 NaN 不能用普通比较";而实验 4、5 则提醒:NaN 会污染传播、且有不少操作会悄悄产生它,所以更要在源头校验、在边界用 Number.isNaN 拦住它。

再补一句我后来的体会:NaN 这个坑之所以经典,是因为它把「x 等于它自己」这条我们从小就当作公理、从不怀疑的规律给打破了。我们的大脑里有太多这样「从不被检验的默认假设」,它们绝大多数时候都对,以至于我们彻底忘了它们「只是假设、可能有例外」。而编程里最阴险的 bug,往往就藏在这些「我们坚信不疑、压根想不到要去验证」的地方——NaN 不等于自己、null 的 typeof 是 object、0.1+0.2 不等于 0.3。所以我现在养成了一个习惯:当一个「最基本、最不该出错」的判断诡异地失灵时,不要先怀疑自己写错了语法,而要想想——会不会是我撞上了某条「默认规律的特例」?

写在最后

回头看,这场由"用 === 判断不等于自己的 NaN"引发的、NaN 到处传播的事故,真正教给我的,远不止"用 Number.isNaN 判断"这一个技巧。它让我对"再'普适、天经地义'的规律, 也可能存在'不遵守它的特例'; 而用'基于那条普适规律的通用做法'去处理这个特例, 必然失效——因为这个特例, 恰恰是那条规律够不到的地方",有了一次刻骨的体会。我栽跟头,是因为我用一条我以为'绝无例外'的规律("任何东西都等于它自己"), 去处理一个'恰恰是这条规律的唯一例外'的东西(NaN)——我的工具(===)是建立在"x 等于 x"这个前提上;可 NaN 偏偏不满足这个前提; 于是我这把"默认万物都等于自己"的尺子, 量到这个"不等于自己"的特例时, 彻底失灵了——它不是量错了, 而是根本不适用;我没意识到"我手里的通用工具, 是有它的隐含前提的; 而这个特例, 正好踩在了它的前提之外"这让我领悟到一个关于"规律、特例与工具适用性"的深刻认知:我们用来认识和处理世界的"通用规则/工具/方法", 大多是建立在某些'默认成立的普适假设'之上的(相等有自反性、运算精确、类型可靠); 而总有一些'特例', 恰恰不满足这些假设——对这些特例, 通用工具会悄无声息地失效(不报错, 只是不起作用);所以"识别特例"是一种重要的能力: 知道"哪些东西不遵守常规"、知道"遇到它们时, 通用工具会失灵、要换专门的工具";那些"看似最不可能出错的、最基本的判断"(判个相等而已), 一旦撞上特例, 反而最易出错、最难察觉——因为我们对它毫无防备这给了我一种处理"特殊值/边界"时的清醒:每当我用一个"通用规则/工具"去处理数据时,要保留一份警觉——"这里有没有'不遵守常规假设'的特例(NaN、null、空、0、极值)?我这个通用做法, 对它们还成立吗?"——对那些"已知会打破常规的特例", 主动用为它们准备的专门方法处理, 而非默认通用规则放之四海皆准;"识别打破常规假设的特例、不对它们盲目套用通用工具、改用专门手段",是写出正确处理边界与特例之代码的关键认清普适规律有不遵守它的特例、通用工具对特例会悄悄失效、识别特例并用专门手段——这,是我用一次 NaN 判断的事故,换来的、关于 JavaScript、也关于如何对待规律之特例的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次想判断一个数是不是 NaN 时,绕开那个永远 false 的 === NaN、直接用 Number.isNaN,那我对着满屏 NaN 找不到源头的这段时间,就值了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

我想用多线程加速一段纯计算的代码,开了 8 个线程满心以为能快 8 倍,结果不但没快、反而比单线程还慢,因为 Python 有个 GIL、同一时刻只让一个线程真正在算的深度复盘

2026-6-3 2:53:17

技术教程

我的 HTTP 请求明明已经超时返回了,可它在后台启动的 goroutine 还在埋头跑、下游调用也没停,goroutine 越积越多内存一路涨,因为我没把 context 的取消信号传下去也没人监听它的深度复盘

2026-6-3 3:05:38

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索