普通视图

发现新文章,点击刷新页面。
昨天 — 2026年1月15日首页

JS-类型转换:从显式“强制”到隐式“魔法”

2026年1月15日 17:12

前言

在 JavaScript 中,类型转换(Type Coercion)既是它的魅力所在,也是许多 Bug 的温床。为什么 [] == ![] 会等于 true?理解了显示与隐式转换的规则,你就能像编译器一样思考。

一、 显式类型转换 (Explicit Conversion)

显式转换是指开发者通过代码明确地将一种类型转换为另一种类型。

1. 转换为字符串 (String)

  • toString() 方法:大多数值都有此方法。

    注意nullundefined 没有这个方法,直接调用会报错。

  • 字符串拼接:与空字符串相加 val + ""

  • String() 构造函数:万能转换,包括 nullundefined

2. 转换为布尔值 (Boolean)

  • Boolean() 包装:手动转换。

  • 双感叹号 !! :利用逻辑非特性快速转换。

    JavaScript

    console.log(!!'hello'); // true
    console.log(!!0);       // false
    

3. 转换为数字 (Number)

  • Number()

    • null \rightarrow 0
    • undefined \rightarrow NaN
    • true \rightarrow 1, false \rightarrow 0
  • parseInt() / parseFloat()

    • 相比 Number() 更加严格,如果参数是 nullundefinedboolean,统统返回 NaN
    • 常用于从字符串中提取数字:parseInt("12.5px") \rightarrow 12

二、 隐式类型转换 (Implicit Conversion)

当运算符两边的数据类型不统一时,JavaScript 会在后台自动完成转换。

1. 逻辑触发:布尔值转换

在以下逻辑语句中,非布尔值会被隐式转换为布尔值:

  • if (...) / while (...) / for (...)
  • 逻辑非 ! :隐式转为布尔并取反。
  • 逻辑与 && 和 逻辑或 || :先将操作数转为布尔值再判断,但要注意它们返回的是原始操作数,而非布尔值。

2. 算术触发:数字转换

除了加法 + 之外的算术运算符,都会强制将两端转为 Number

  • 运算符-, *, /, %, ++, --
  • 一元正负号+a, -a 会尝试将 a 转为数字。

3. “加法 + ”的特殊规则

+ 运算符具有双重身份(数值加法或字符串拼接):

  • 字符串优先:只要其中一个是字符串,另一个就会转成字符串,然后拼接。
  • 数字优先:如果两个操作数都不是字符串,则都转为数字(或 NaN)进行运算。

三、 对象转基本类型的底层逻辑

当对象参与运算或转换时,JS 引擎会遵循以下流程:

  1. Symbol.toPrimitive:如果对象定义了这个方法,优先调用。
  2. valueOf() :如果没有 toPrimitive,通常先尝试获取原始值。
  3. toString() :如果 valueOf 没能返回基本类型,则调用 toString

JavaScript

// 自定义转换行为
const obj = {
  valueOf: () => 10,
  toString: () => "obj"
};
console.log(obj + 1); // 11 (优先调用 valueOf)

四、 避坑小结:布尔判断中的对象

  • 所有对象(包括空数组 [] 和空对象 {})在转换为布尔值时,结果均为 true
  • 在验证 nullundefined 时,始终建议使用全等 ===,以避免隐式转换带来的干扰。

五、 进阶:经典面试题深度推导

为了验证你是否掌握了前面的知识,我们来看这几个面试高频题:

1. 为什么 [] == ![] 结果是 true

这道题几乎涵盖了所有的隐式转换规则,推导过程如下:

  1. 右侧优先处理![]。由于 [] 是对象,转为布尔值为 true,取反后得到 false

    • 表达式变为:[] == false
  2. 类型不统一:一边是对象,一边是布尔值。根据规则,布尔值先转为数字false 转为 0

    • 表达式变为:[] == 0
  3. 对象转基本类型[] 会尝试调用 valueOf(返回自身)和 toString[].toString() 得到空字符串 ""

    • 表达式变为:"" == 0
  4. 字符串转数字:空字符串 "" 转为数字 0

    • 表达式变为:0 == 0
  5. 结果true

2. 1 + "2" vs 1 - "2"

  • 1 + "2" :遇到 + 且有字符串,触发字符串拼接,结果为 "12"
  • 1 - "2"- 运算符只能用于数值计算,强制将 "2" 转为数字 2,结果为 -1

3. NaN 的奇特逻辑

JavaScript

console.log(NaN == NaN); // false
  • 解析NaN(Not a Number)是 JavaScript 中唯一一个不等于自身的值。
  • 避坑:判断一个值是否是 NaN,请使用 Number.isNaN(),不要直接用 ==

JS-ES6新特性

2026年1月15日 14:42

前言

ES6 (ECMAScript 2015) 的发布是现代 JavaScript 开发的分水岭。它不仅修复了 var 带来的历史遗留问题,还引入了更高效的数据结构。本文将带你系统复习 let/const、解构赋值、Map/Set 以及独一无二的 Symbol

一、 变量声明的“进化”:let 与 const

在 ES6 之前,我们只有 varm但 var 带来的变量提升和全局污染常常让人头疼,ES6则新增了let、const。

1. let 特点

  • 禁止重复声明:同一作用域内不可重复定义同名变量。
  • 块级作用域:仅在 {} 内部有效(如 iffor 块)。
  • 无变量提升:存在“暂时性死区”(TDZ),必须先定义后使用,否则抛出 ReferenceError

2. const 特点

  • 必须赋初值:声明时必须立即初始化。

  • 值不可变:一旦声明,其指向的内存地址不可修改。

    注意: 修改对象或数组内部的属性是允许的,因为这并没有改变引用地址。

  • 具备块级作用域,同样不存在提升。

3. 三者对比速查表

特性 var let const
作用域 函数作用域 块级作用域 块级作用域
变量提升 是 (显示 undefined) 否 (报错) 否 (报错)
重复声明 允许 不允许 不允许
必须赋初值

二、 解构赋值:代码瘦身的艺术

解构赋值允许我们按照一定模式,从数组和对象中提取值。

1. 数组解构

数组解构是位置对应的。

JavaScript

let [a, [b, c]] = [1, [2, 3]]; // 支持嵌套解构
  • 注意: 如果等号右边不是可遍历结构(Iterator),将会报错。

2. 对象解构

对象解构是属性名对应的,不强调顺序。

JavaScript

let obj = { first: 'hello', last: 'world' };

// 别名用法:{ 原属性名: 新变量名 }
let { first: f, last: l } = obj; 

console.log(f); // 'hello'

3. 函数参数解构

这是开发中最常用的场景,通过设定默认值可以增强代码的健壮性。

JavaScript

function connect({ host = '127.0.0.1', port = 3000 } = {}) {
    console.log(host, port);
}

4. 妙用场景

  • 快速交换变量[x, y] = [y, x]
  • 提取 JSON 数据:从复杂的接口返回对象中精准拿取字段。
  • 接收多个返回值:函数返回数组或对象后直接解构。

三、 键值对的新选择:Map

Map 是一组键值对结构,其查找时间复杂度为 O(1)O(1)

1. Map 的常用 API

  • set(key, value):添加元素。
  • get(key):获取元素。
  • has(key):检查是否存在。
  • delete(key):删除指定元素。
  • size:属性,返回元素个数。
  • clear():清空所有。

2. 核心特性

  • Key 的多样性:对象的 key 只能是字符串或 Symbol,而 Map 的 key 可以是任意类型(包括对象、函数)。
  • 覆盖性:同一个 key 放入多个 value,后面的会覆盖前面的。

JavaScript

const m = new Map();
m.set('Bob', 59);
m.forEach((val, key) => {
    console.log(`${key}: ${val}`);
});

四、 唯一值的容器:Set

Set 类似于数组,但其成员的值都是唯一的。

1. 数组去重的神技

在 ES6 中,一行代码即可搞定数组去重:

JavaScript

let arr = [1, 2, 2, 3];
let uniqueArr = Array.from(new Set(arr)); 
// 或者使用扩展运算符
let uniqueArr2 = [...new Set(arr)];
console.log(uniqueArr,uniqueArr2) //[1, 2, 3],[1, 2, 3]

2. 常用操作

  • add(value):添加新成员。
  • delete(value):删除。
  • has(value):判断是否存在。
  • size:获取长度。

3. 遍历演示

JavaScript

let set = new Set([123, 456, 789]);

for (let item of set) {
   console.log(item); 
}

// 过滤小数值
set.forEach(e => {
    if(e < 500) set.delete(e);
});
console.log(set); // Set { 789 }

五、 独一无二的 Symbol

Symbol 是 ES6 引入的一种原始数据类型,表示独一无二的值。

1. 为什么需要 Symbol?

为了防止对象属性名冲突。如果你给一个他人提供的对象添加属性,使用 Symbol 可以确保不会覆盖原有属性。

2. 基本使用

JavaScript

let s1 = Symbol('desc');
let s2 = Symbol('desc');

console.log(s1 === s2); // false (即使描述相同,值也是唯一的)

// 作为对象属性
let obj = {
    [s1]: 'Hello Symbol'
};

注意Symbol 作为属性名时,通过 for...inObject.keys() 是遍历不到的,需要使用 Object.getOwnPropertySymbols()

❌
❌