Dark Dwarf Blog background

JavaScript 语法技巧

JavaScript 语法技巧

这里记录了很多JavaScript的神奇技巧,会持续更新…

1. 解构赋值

解构赋值语法是一种允许你将数组或对象中的数据“解压”到独立变量中的表达式。

把解构想象成你收到了一个“礼品篮”(对象或数组)。你不需要整个篮子,你只想拿出其中的几样东西(属性或元素),并给它们分别贴上标签(变量名)。解构就是帮你自动完成这个“拿出并贴标签”过程的语法。

a.a. 对象解构 (Object Destructuring)

这是最常用的解构形式,用于从对象中提取属性。

const user = {
  id: 42,
  name: 'Alice',
  age: 25,
  address: {  
    city: 'Wonderland'
  }
};

// 1. 基本用法
const { name, age } = user;
console.log(name); // 'Alice'
console.log(age);  // 25

// 2. 赋给新的变量名
const { name: userName, age: userAge } = user;
console.log(userName); // 'Alice'

// 3. 提供默认值
const { country = 'Unknown' } = user;
console.log(country); // 'Unknown'

// 4. 嵌套解构
const { address: { city } } = user;
console.log(city); // 'Wonderland'

b.b. 数组解构 (Array Destructuring)

数组解构根据元素的位置进行提取。

const fruits = ['Apple', 'Banana', 'Cherry', 'Date'];

// 1. 基本用法
const [first, second] = fruits;
console.log(first);  // 'Apple'
console.log(second); // 'Banana'

// 2. 跳过元素
const [ , , third] = fruits;
console.log(third); // 'Cherry'

// 3. 剩余元素 (Rest Syntax)
const [mainFruit, ...otherFruits] = fruits;
console.log(mainFruit);    // 'Apple'
console.log(otherFruits); // ['Banana', 'Cherry', 'Date']

2. 展开语法与剩余参数 (…)

... 运算符根据其使用场景,既可以作为“展开语法”使用,也可以作为“剩余参数”使用。它的核心思想是“展开”与“收集”。

a.a. 展开语法 (Spread Syntax):展开

展开语法将一个可迭代对象(如数组、字符串)或对象“展开”成多个独立的元素或属性。

想象一下,你把一盒卡牌(数组)从盒子里倒出来,摊在桌面上,变成一张张独立的卡牌(多个元素)。

// 1. 在数组中使用:合并与复制
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2, 5]; // [1, 2, 3, 4, 5]
const copied = [...arr1]; // [1, 2] (浅拷贝)

// 2. 在对象中使用:合并与复制
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const mergedObj = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }
// 如果有同名属性,后面的会覆盖前面的
const updatedObj = { ...obj1, b: 99 }; // { a: 1, b: 99 }

// 3. 在函数调用中使用
const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 等同于 Math.max(1, 2, 3)

b.b. 剩余参数 (Rest Parameters):收集

在函数定义中,... 可以将所有剩余的参数“收集”到一个真正的数组中。

想象一下,在派对上,主持人先介绍了两位主宾(前两个参数),然后指着剩下所有的人说:“以及其余各位(剩余参数)!”

function sum(first, second, ...others) {
  console.log('First:', first);
  console.log('Second:', second);
  console.log('Others:', others); // others 是一个真数组
  return others.reduce((acc, val) => acc + val, first + second);
}

console.log(sum(1, 2, 3, 4, 5));
// First: 1
// Second: 2
// Others: [3, 4, 5]
// Result: 15

这比使用老旧的、类数组的 arguments 对象要现代和方便得多。

3. 函数参数的增强

a.a. 默认参数 (Default Parameters)

你可以直接为函数参数指定默认值,当调用时未提供该参数或传入 undefined 时,将使用默认值。

function greet(name = 'Guest', greeting = 'Hello') {
  console.log(`${greeting}, ${name}!`);
}

greet('Alice'); // "Hello, Alice!"
greet();        // "Hello, Guest!"

b.b. 在函数参数中使用解构

这是一个非常强大且常见的模式,尤其是在处理配置对象时。

// 不使用解构
function printUser(user) {
  console.log(`Name: ${user.name}, Age: ${user.age}`);
}

// 使用解构
function printUserDestructured({ name, age, country = 'USA' }) {
  console.log(`Name: ${name}, Age: ${age}, Country: ${country}`);
}

const user = { name: 'Bob', age: 42 };
printUserDestructured(user); // "Name: Bob, Age: 42, Country: USA"

这种方式使函数签名更具自描述性,调用者无需关心参数对象的顺序。

4. 简化对象和属性访问

a.a. 属性值缩写 (Shorthand Properties)

当你想创建一个对象,且其属性名与你用来赋值的变量名相同时,可以只写一次。

const name = 'Charlie';
const age = 35;

// 老方法
const personOld = { name: name, age: age };

// 缩写
const personNew = { name, age };

console.log(personNew); // { name: 'Charlie', age: 35 }

b.b. 可选链操作符 (?.)

在尝试访问深层嵌套的对象属性时,如果中间某个属性不存在(为 nullundefined),代码会立即抛出 TypeError。可选链 ?. 解决了这个问题。

它就像在一条不确定的路上探索。每到一个岔路口(属性),你都先问一句“这里有路吗?”(?.)。如果没有,你就停下来并报告“此路不通”(undefined),而不是一头栽进沟里(抛出错误)。

const user = {
  name: 'David',
  // address 属性缺失
};

// 老方法,需要冗长的检查
const cityOld = user && user.address && user.address.city;
console.log(cityOld); // undefined

// 使用可选链
const cityNew = user?.address?.city;
console.log(cityNew); // undefined (代码更简洁,且不会抛错)

// 也可用于函数调用
const admin = { getName: () => 'Admin' };
const regularUser = {};
console.log(admin.getName?.());     // 'Admin'
console.log(regularUser.getName?.()); // undefined

c.c. 空值合并操作符 (??)

在提供默认值时,逻辑或操作符 || 会将所有“假值”(如 0, '', false)都视为无效而使用默认值,这有时并非我们所愿。

空值合并操作符 ?? 更为精确:只有当左侧的值为 nullundefined,才会返回右侧的默认值。

let score = 0;

// 使用 || 的问题:0 被视为无效,错误地使用了默认值
const finalScore1 = score || 100;
console.log(finalScore1); // 100 (错误!)

// 使用 ?? 的正确行为:0 是一个有效值
const finalScore2 = score ?? 100;
console.log(finalScore2); // 0 (正确!)

let username = '';
const displayName = username ?? 'Guest';
console.log(displayName); // '' (正确!)