🔧 一、工业级加法函数:从「能跑就行」到「绝对可靠」
旧代码问题:
// ❌ 旧版(漏洞百出)
function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('必须是数字');
}
return a + b;
}
add(null, 2); // 静默失败 → 返回 "null2"(隐式转换灾难)
权威解决方案(符合 IEEE 754 和 ES2023):
// ✅ 工业级方案(BigInt/Number 双兼容 + 防御性编程)
function add(a, b) {
// 1. 严格类型检查(排除 null/undefined/非数字)
if (
(typeof a !== 'number' && typeof a !== 'bigint') ||
(typeof b !== 'number' && typeof b !== 'bigint')
) {
throw new TypeError('Arguments must be numbers or BigInts');
}
// 2. 显式处理边界(NaN/Infinity)
if (!Number.isFinite(a) || !Number.isFinite(b)) {
throw new RangeError('Arguments must be finite numbers');
}
// 3. 类型一致校验(禁止 Number + BigInt)
if (typeof a !== typeof b) {
throw new TypeError('Cannot mix Number and BigInt');
}
return a + b;
}
// 测试用例
add(1n, 2n); // ✅ 3n(BigInt 支持)
add(1, 2); // ✅ 3
add(1, '2'); // ❌ TypeError
add(1, NaN); // ❌ RangeError
为什么权威?
- 遵循 ECMAScript 类型转换规则(避免隐式转换)
- 兼容 BigInt 提案(ES2020) 和 Number 类型
- 使用
Number.isFinite(比isNaN更严格,排除Infinity)
🔑 二、Symbol 的 5 个杀手级应用(来自 Lodash 和 React 源码)
1. 防止属性污染(React 私有状态管理)
// ✅ React 源码风格:用 Symbol 保护内部状态
const React = {
// 内部状态键(外部无法访问)
__internalState: Symbol('react.state'),
useState(initialValue) {
const state = this[this.__internalState] || initialValue;
this[this.__internalState] = state;
return [state, (v) => { this[this.__internalState] = v }];
}
};
const [count, setCount] = React.useState(0);
console.log(count); // 0
// 外部无法直接修改 __internalState(防篡改)
2. 高性能枚举(TypeScript 编译后方案)
// ✅ 替代传统字符串枚举(避免命名冲突)
const LogLevel = {
DEBUG: Symbol('debug'),
WARN: Symbol('warn'),
ERROR: Symbol('error')
};
function log(message, level = LogLevel.DEBUG) {
if (level === LogLevel.ERROR) console.error(message);
else if (level === LogLevel.WARN) console.warn(message);
else console.log(message);
}
log('Debug', LogLevel.DEBUG); // 无魔法字符串风险
3. 元编程(实现自定义迭代协议)
// ✅ 符合 ECMAScript 迭代协议(Generator + Symbol.iterator)
const Fibonacci = {
*[Symbol.iterator]() { // 生成器函数
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
};
// 获取前10项
for (const num of Fibonacci) {
if (num > 100) break;
console.log(num); // 0, 1, 1, 2, 3, 5...
}
🔍 三、类型判断的终极方案(比 typeof 强 100 倍)
1. 区分 Array 和 Object(Vue 3 源码方案)
// ✅ 比 Array.isArray 更底层(兼容跨 iframe 场景)
function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
// Vue 3 源码中的实际应用
if (isArray(children)) {
mountArrayChildren(children); // 特殊处理数组
}
2. 检测纯对象(jQuery 经典实现)
// ✅ 排除 null/Array/Date 等(只认纯对象)
function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]' &&
Object.getPrototypeOf(obj) === Object.prototype;
}
isPlainObject({}); // true
isPlainObject([]); // false
isPlainObject(null); // false
3. 安全类型检查(Node.js 内部 utils 风格)
// ✅ 处理所有 8 种 JS 类型(包括 BigInt 和 Symbol)
function getType(value) {
if (value === null) return 'null';
const type = typeof value;
if (type !== 'object') return type; // 原始类型
return Object.prototype.toString.call(value)
.slice(8, -1)
.toLowerCase();
}
getType(42n); // 'bigint'
getType(Symbol()); // 'symbol'
getType(new Date()); // 'date'
🧠 四、灵魂拷问:你的代码能通过 TC39 专家的审查吗?
经典面试题:
// 以下代码的输出是什么?(考察类型系统底层)
console.log(typeof (() => {})); // 'function'(规范特殊处理)
console.log(typeof class {}); // 'function'(类本质是函数)
console.log(typeof new (class {})()); // 'object'(实例是对象)
规范依据:
- ECMAScript 2023 §13.7.5.16:
typeof对可调用对象返回"function" - 历史包袱:
typeof null === 'object'(1995 年设计缺陷)
💥 结尾暴击:
“如果你还在用
typeof x === 'object',你的代码库可能已经埋了 10 个null炸弹。” —— 用规范级的类型判断,让 Bug 在你面前瑟瑟发抖!
🔥 互动挑战:
尝试用
Symbol.hasInstance重写instanceof的行为(评论区交出你的代码!)