JavaScript 学习笔记:深入理解 map() 方法与面向对象特性
一、Array.prototype.map() 方法详解
map() 是 ES6 中引入的重要数组方法之一,属于高阶函数(Higher-order Function) 。它的核心作用是对原数组的每个元素执行一个回调函数,并返回一个由回调函数返回值组成的新数组,而不会修改原数组。
1.1 基本语法
c
const newArray = arr.map(callbackFn(element, index, array), thisArg);
-
callbackFn:为每个元素执行的函数,必须返回一个值。element:当前元素index:当前索引array:原数组本身
-
thisArg(可选) :指定回调函数中this的值
1.2 使用示例
dart
const numbers = [1, 4, 9];
const roots = numbers.map(num => Math.sqrt(num)); // [1, 2, 3]
const doubles = numbers.map(num => num * 2); // [2, 8, 18]
注意:map() 不会改变原数组,而是返回一个全新数组。
1.3 常见陷阱:map(parseInt) 的误区
一个经典面试题:
arduino
console.log(["1", "2", "3"].map(parseInt)); // [1, NaN, NaN]
原因分析:
map() 会传递三个参数给回调函数:(element, index, array)
而 parseInt(string, radix) 接收两个参数:字符串和进制基数。
因此实际调用过程如下:
javascript
parseInt("1", 0) // → 1(radix=0 被忽略,默认十进制)
parseInt("2", 1) // → NaN(1进制非法)
parseInt("3", 2) // → NaN(2进制不含数字3)
正确写法:
scss
// 方式1:显式指定基数
["1", "2", "3"].map(str => parseInt(str, 10));
// 方式2:使用 Number 构造函数(更简洁)
["1", "2", "3"].map(Number); // [1, 2, 3]
⚠️ 注意:
Number()会解析浮点数和科学计数法,而parseInt()只取整数部分。
二、NaN(Not a Number)详解
2.1 什么是 NaN?
NaN表示"不是一个数字",但其typeof结果为'number'。- 它出现在无效的数学运算或类型转换中。
2.2 常见产生 NaN 的场景
javascript
console.log(0 / 0); // NaN
console.log("abc" - 1); // NaN
console.log(undefined + 1); // NaN
console.log(parseInt("hello")); // NaN
console.log(Math.sqrt(-1)); // NaN
2.3 NaN 的特殊性质
-
NaN 不等于任何值,包括它自己:
iniconsole.log(NaN == NaN); // false console.log(NaN === NaN); // false -
正确判断 NaN 的方法:
javascriptif (Number.isNaN(value)) { console.log("这是一个 NaN"); }✅ 推荐使用
Number.isNaN(),而非全局isNaN()(后者会进行类型转换,可能导致误判)。
三、JavaScript 的面向对象特性与包装类
3.1 JS 是完全面向对象的语言
尽管 JavaScript 有原始类型(如 string, number, boolean),但它通过包装类(Wrapper Classes) 实现了统一的对象调用风格。
例如:
perl
"hello".length; // 5
114.514.toFixed(2); // "114.51"
这些看似"原始类型调用方法"的操作,在底层实际上是:
javascript
(new String("hello")).length;
(new Number(114.514)).toFixed(2);
JS 引擎会临时创建包装对象,调用方法后立即销毁,实现"傻瓜式"编程体验。
3.2 包装类的生命周期
javascript
let str = "hello"; // 原始字符串
let strObj = new String(str); // 显式创建 String 对象
console.log(typeof str); // "string"
console.log(typeof strObj); // "object"
strObj = null; // 手动释放(通常不需要)
💡 日常开发中无需手动创建包装对象,JS 会自动处理。
四、字符串处理相关知识补充
4.1 字符串长度与编码
JavaScript 使用 UTF-16 编码,大多数字符占 1 个单位,但某些 Unicode 字符(如 emoji)占 2 个或更多:
arduino
console.log("a".length); // 1
console.log("中".length); // 1
console.log("𝄞".length); // 2(音乐符号)
console.log("👋".length); // 2(emoji)
4.2 常用字符串方法对比
| 方法 | 支持负索引 | 参数顺序处理 | 示例 |
|---|---|---|---|
slice(start, end) |
✅ | 保持顺序,start > end 返回空 |
"Hello".slice(-3, -1) → "ll" |
substring(start, end) |
❌(负数转为 0) | 自动交换使 start ≤ end |
"Hello".substring(3, 1) → "el" |
4.3 查找字符位置
rust
const str = "Hello";
console.log(str.indexOf('l')); // 2(首次出现)
console.log(str.lastIndexOf('l')); // 3(最后一次出现)
五、总结与最佳实践
- 慎用
map(parseInt):务必显式指定进制或改用Number。 - 正确判断 NaN :使用
Number.isNaN()而非== NaN。 - 理解包装类机制:原始类型能调用方法是 JS 的语法糖,背后是临时对象。
- 优先使用
map返回新数组 :若不需要返回值,应使用forEach。 - 注意字符串编码问题 :处理 emoji 或生僻字时,
length可能不符合直觉。
通过深入理解 map()、NaN、包装类等核心概念,我们不仅能写出更健壮的代码,还能避免常见的"坑"。JavaScript 虽灵活,但其设计哲学强调开发者友好性与一致性,掌握这些底层机制,方能真正驾驭这门语言。
📚 参考资料:MDN - Array.prototype.map()