ES6+ 新特性完整教程(解构赋值、展开运算符、模块化)
上一篇我们学会了 JavaScript 基础语法。但你在网上看到的现代 JS 代码,很多写法你可能没见过——那些大多是 ES6+(ES2015 及以后版本) 带来的新特性。这一篇就专门讲这些现代写法,全部配有示例,照着敲就能懂。
一、什么是 ES6?
ES 是 ECMAScript 的缩写,它是一份”JavaScript 语言标准说明书”。每隔一两年,标准就会更新,加入新语法。ES6(也叫 ES2015)是 2015 年发布的一次大更新,从那以后的版本统称 ES6+。
为什么要学? 现在所有主流浏览器都支持 ES6+,而且几乎所有现代框架(Vue、React)都在用它。不学 ES6+,你会看不懂真实项目的代码。
二、解构赋值:从数组/对象里”拆”出值
以前我们想从数组里取值,要一个个按下标拿:
let arr = ["小明", 18, "高三"];let name = arr[0];let age = arr[1];console.log(name, age); // 小明 18解构赋值可以一行搞定:把数组”拆开”,按顺序赋给变量。
let arr = ["小明", 18, "高三"];let [name, age, grade] = arr;
console.log(name); // 小明console.log(age); // 18console.log(grade); // 高三let [a, b, c] = 数组 的意思是:把数组的第 0 个元素给 a,第 1 个给 b,第 2 个给 c。
跳过某些元素
let arr = ["a", "b", "c", "d"];let [first, , third] = arr; // 中间空着就是跳过console.log(first); // aconsole.log(third); // c给默认值(防止 undefined)
let arr = ["苹果"];let [fruit, price = "未知"] = arr;console.log(fruit); // 苹果console.log(price); // 未知(因为数组里没有第二个元素)对象解构(最常用!)
对象解构不用管顺序,按属性名取值:
let student = { name: "小红", age: 20, city: "北京" };
let { name, age } = student;console.log(name); // 小红console.log(age); // 20还可以改名和给默认值:
let { name: studentName, city = "未知", score = 0 } = student;console.log(studentName); // 小红console.log(city); // 北京(原对象里有)console.log(score); // 0(原对象没有,用默认值)函数参数解构(实战高频写法)
写函数时,直接解构参数对象,调用的人一眼就能看清传了什么:
function greet({ name, age }) { console.log(`你好,我叫${name},今年${age}岁`);}
greet({ name: "小刚", age: 25 }); // 你好,我叫小刚,今年25岁交换两个变量的值(一行搞定)
以前要三行加临时变量,现在:
let a = 1;let b = 2;[a, b] = [b, a];console.log(a, b); // 2 1三、展开运算符:把数组/对象”摊开”
展开运算符是三个点 ...,作用是把数组或对象”展开”成一个个元素。
展开数组
let arr1 = [1, 2, 3];let arr2 = [4, 5];
let all = [...arr1, ...arr2];console.log(all); // [1, 2, 3, 4, 5]这比 concat 拼接更直观,还能在中间插值:
let all = [0, ...arr1, 9];console.log(all); // [0, 1, 2, 3, 9]复制数组(避免”引用”坑)
重要知识点: 直接 let b = a 并不会复制数组,a 和 b 指向同一个数组,改 b 会连累 a:
let a = [1, 2, 3];let b = a;b.push(4);console.log(a); // [1, 2, 3, 4] !a 也被改了用展开运算符才能真正复制:
let a = [1, 2, 3];let b = [...a];b.push(4);console.log(a); // [1, 2, 3](a 没变)console.log(b); // [1, 2, 3, 4]展开对象
let user = { name: "小明", age: 18 };let info = { ...user, city: "北京" };
console.log(info);// { name: "小明", age: 18, city: "北京" }注意顺序: 后面的会覆盖前面的同名属性:
let user = { name: "小明", age: 18 };let admin = { ...user, age: 30 };console.log(admin.age); // 30(后面的 age 覆盖了前面的)把类数组转成真数组
比如 arguments、DOM 集合(NodeList)不是真数组,不能直接用 map。用展开可以转:
function collect() { let arr = [...arguments]; console.log(Array.isArray(arr)); // true}collect(1, 2, 3);四、剩余参数:把”剩下的”收集成数组
剩余参数也是 ...,但用在函数参数里,表示”把没被命名的参数收进一个数组”。注意和展开运算符的区别:展开是”摊开”,剩余是”收集”。
function sum(...nums) { let total = 0; for (let n of nums) { total += n; } return total;}
console.log(sum(1, 2, 3)); // 6console.log(sum(1, 2, 3, 4, 5)); // 15不管传几个参数,都会被收进 nums 数组里。
固定参数 + 剩余参数结合:
function introduce(name, ...hobbies) { console.log(`${name} 的爱好:${hobbies.join("、")}`);}
introduce("小明", "篮球", "编程", "画画");// 小明 的爱好:篮球、编程、画画五、箭头函数:更简洁的函数写法
上一篇已经简单介绍过,这里再深入一点。
// 传统写法function add(a, b) { return a + b;}
// 箭头函数const add = (a, b) => a + b;几个简化规则:
// 1. 只有一个参数,可以省略括号const double = n => n * 2;console.log(double(5)); // 10
// 2. 没有参数,必须写空括号const sayHi = () => console.log("你好");sayHi();
// 3. 函数体多行,要加大括号和 returnconst calc = (a, b) => { let sum = a + b; return sum * 2;};console.log(calc(3, 4)); // 14箭头函数和 this: 这是新手最容易懵的点。传统函数里的 this 会随调用方式变化,箭头函数里的 this 固定指向”定义它的地方”。现在你只需记住:在回调函数(比如 addEventListener 里)用箭头函数,this 的行为更符合直觉。
六、模块化:import / export
为什么需要模块?
一个项目代码多了,全写在一个文件里会:变量互相冲突、找不到哪段代码在哪、不好复用。模块就是”把一个文件当成一个独立的小盒子,只把需要的东西导出给别人用”。
导出:export
假设有两个文件:math.js 和 main.js。
math.js(导出的一方):
// 命名导出:可以导多个export const PI = 3.14159;
export function add(a, b) { return a + b;}
export function multiply(a, b) { return a * b;}导入:import
main.js(使用的一方):
// 命名导入:名字必须和导出的一致,用花括号import { add, multiply, PI } from "./math.js";
console.log(add(2, 3)); // 5console.log(multiply(2, 3)); // 6console.log(PI); // 3.14159默认导出:export default
每个模块只能有一个默认导出,导入时不用花括号、可以随便起名:
export default function subtract(a, b) { return a - b;}import subtract from "./math.js";console.log(subtract(10, 4)); // 6导入时改名:as
import { add as plus } from "./math.js";console.log(plus(1, 2)); // 3混合导入
import subtract, { add, PI } from "./math.js";一次性导入全部:*
import * as math from "./math.js";console.log(math.add(1, 2));console.log(math.PI);怎么在浏览器里用?
在 HTML 里给 script 标签加 type="module":
<script type="module" src="main.js"></script>浏览器看到 import 语句,会自动加载对应的模块文件。注意:用模块方式打开页面,必须通过本地服务器(如 python -m http.server)访问,直接双击 HTML 文件会因为浏览器安全策略报错。
七、其他实用新特性速览
这些特性看一眼就能用,都非常实用:
1. 可选链 ?.(避免”读不到属性”报错)
let user = { name: "小明", address: { city: "北京" } };
// 以前:要一层层判断if (user && user.address && user.address.city) { console.log(user.address.city);}
// 现在:?. 会自动检查,没有就返回 undefined,不报错console.log(user?.address?.city); // 北京console.log(user?.phone?.number); // undefined(不会报错)2. 空值合并 ??(给默认值的另一种方式)
let a = null;let b = "有值";
console.log(a ?? "默认值"); // 默认值(null 或 undefined 时用默认)console.log(b ?? "默认值"); // 有值
// 和 || 的区别:|| 对 0、空字符串 也会取右边console.log(0 || "默认"); // 默认console.log(0 ?? "默认"); // 0 !(?? 只在 null/undefined 时取默认)3. 字符串 startsWith / includes
let str = "Hello World";console.log(str.startsWith("Hello")); // trueconsole.log(str.endsWith("World")); // trueconsole.log(str.includes("o W")); // trueconsole.log("abc".repeat(3)); // abcabcabc4. 数组 find / findIndex
let students = [ { name: "小明", score: 95 }, { name: "小红", score: 88 }];
let found = students.find(s => s.name === "小红");console.log(found.score); // 88
let index = students.findIndex(s => s.score < 90);console.log(index); // 1(小红的位置)5. class 类(面向对象语法糖)
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} 叫了一声`); }}
let dog = new Animal("旺财");dog.speak(); // 旺财 叫了一声八、总结
这一篇我们学会了 ES6+ 最核心的现代写法:
- 解构赋值:从数组
[a, b]和对象{a, b}里快速取值 - 展开运算符
...:摊开数组/对象,用于合并、复制 - 剩余参数:把剩余参数收进数组
- 箭头函数:更简洁的函数写法
- 模块化:export 导出、import 导入,组织大型代码
- 实用新特性:可选链
?.、空值合并??、find、class 等
这些语法单独看都很简单,但组合起来就是现代 JS 项目的地基。下一篇我们学习异步编程——这是处理”等待”(比如网络请求)的关键。