JavaScript 学习笔记:深入理解 map() 方法与面向对象特性

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 不等于任何值,包括它自己

    ini 复制代码
    console.log(NaN == NaN);     // false
    console.log(NaN === NaN);    // false
  • 正确判断 NaN 的方法

    javascript 复制代码
    if (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(最后一次出现)

五、总结与最佳实践

  1. 慎用 map(parseInt) :务必显式指定进制或改用 Number
  2. 正确判断 NaN :使用 Number.isNaN() 而非 == NaN
  3. 理解包装类机制:原始类型能调用方法是 JS 的语法糖,背后是临时对象。
  4. 优先使用 map 返回新数组 :若不需要返回值,应使用 forEach
  5. 注意字符串编码问题 :处理 emoji 或生僻字时,length 可能不符合直觉。

通过深入理解 map()、NaN、包装类等核心概念,我们不仅能写出更健壮的代码,还能避免常见的"坑"。JavaScript 虽灵活,但其设计哲学强调开发者友好性与一致性,掌握这些底层机制,方能真正驾驭这门语言。

📚 参考资料:MDN - Array.prototype.map()

相关推荐
玉宇夕落2 小时前
深入剖析 JavaScript 中 map() 与 parseInt 的“经典组合陷阱”
javascript·面试
太平洋月光2 小时前
MJML邮件如何随宽度变化动态切换有几列📮
前端·css
AAA不会前端开发2 小时前
TypeScript核心类型系统完全指南
前端·typescript
徐同保2 小时前
使用GitKraken把feature_xtb_1104分支的多次提交记录合并到一起,只保留一次提交记录,并合并到master分支
前端
小光学长2 小时前
基于Vue的智慧楼宇报修平台设计与实现066z15wb(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
醉方休2 小时前
web前端 DSL转换技术
前端
sen_shan2 小时前
Vue3+Vite+TypeScript+Element Plus开发-27.表格页码自定义
前端·javascript·typescript
刺客_Andy2 小时前
React 第五十二节 Router中 useResolvedPath使用详解和注意事项示例
前端·react.js·架构
豆浆9452 小时前
vue3+qiankun主应用和微应用的路由跳转返回
前端