ES6+ 基础学习笔记

你可以把JavaScript语言想象成一部手机的操作系统。ES5(如Android 4.0)功能基础,而**ES6(ECMAScript 2015)**​ 就像一次重大的系统版本更新(如Android 8.0),它带来了全新的语法糖、更强大的API和更现代的编程范式,是现代前端开发的"基石"。它之后,JavaScript以"年号"命名,如ES2016, ES2017,统称ES6+。

想象ES6+的语法是"未来世界"的通用语。旧版浏览器(IE等)是"古代人",听不懂。Babel​ 就像一个"实时翻译官",在代码交付给浏览器前,将ES6+代码"翻译"成ES5代码,确保兼容性。在Node.js或Webpack等工具中,我们配置好这个"翻译官",就能愉快地书写现代代码了。

一、ES6 简介与开发环境

1.1 什么是 ES6? (ECMAScript 2015)

  • ES6ECMAScript 2015 的简称,是 JavaScript 语言的下一代标准,于 2015 年发布。
  • 它为 JavaScript 带来了大量新特性 (如箭头函数、Promise、类等),是现代前端开发的基石
  • 类比:如果 JavaScript 是一门语言,ECMAScript 就是它的 "语法规范",ES6 就是这套规范的 "重大升级版本"。

1.2 环境配置

  • 浏览器支持:现代浏览器(Chrome、Firefox、Safari、Edge)对 ES6 支持度很高,但旧版浏览器(如 IE)不支持。
  • Babel :一个 JavaScript 编译器,核心作用是将 ES6+ 语法转换为 ES5,以兼容旧环境。
  • Node.js & 打包工具
    • Node.js 环境可直接运行大部分 ES6+ 特性。
    • 结合 Webpack/Vite 等打包工具,可在项目中统一处理 ES6+ 转换和模块打包。

二、变量与常量声明

var的"混乱作用域"走向 let/const的"精确作用域",让变量的生命周期更清晰、可预测。

2.1 let 关键字

let 用于声明块级作用域的变量,具有以下特性:

块级作用域 :仅在 {} 内有效。let把变量囚禁在 {}这个"监狱"里,出了监狱它就失效。这解决了 var的变量泄露问题。

javascript 复制代码
if (true) {
    let prison = '囚犯';
    var outsider = '外人';
}
console.log(outsider); // '外人',var逃出了if块
console.log(prison); // ReferenceError! let被关在{}内,无法访问

不存在变量提升:必须先声明后使用。

javascript 复制代码
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;

暂时性死区(TDZ) :在代码块内,使用 let 声明变量之前,该变量不可用。即在 let声明之前,变量仿佛进入了一个"死亡区域",无法访问。这本质是为了在语法上强制"先声明,后使用"的好习惯 ,避免未知的undefined值。

不允许重复声明 :同一作用域内不能用 let 重复声明同一变量。
新手误解点let没有变量提升?不,准确说它有"提升",但未被初始化。引擎知道它的存在,但在声明语句执行前不允许你访问,这个状态就是"暂时性死区"。

2.2 const 关键字

const 用于声明常量 ,特性与 let 类似,但有以下特殊点:

保护的是"内存地址" :这是最核心的点。const声明的变量,好比一个贴在瓶子上的固定标签。标签不能换(不能重新赋值)。

  • 对于基本类型(Number、String、Boolean),值不可变。如果瓶子里装的是水(基本类型:字符串、数字等),水和瓶子一体,所以"水"也不能变。

  • 对于引用类型对象/数组 ),内存地址不可变 (但内部属性可修改)。如果瓶子里装的是一个可变的玩具(对象/数组)const只保证标签贴在这个玩具上,不保证你不能拆解、改装这个玩具。

javascript 复制代码
const bottle = ['乐高A', '乐高B']; // 标签贴在乐高集合上
bottle.push('乐高C'); // ✅ 可以!改装乐高集合本身
bottle = ['新玩具']; // ❌ 报错!试图把标签撕下来贴到另一个玩具上。

声明时必须初始化

javascript 复制代码
const PI; // SyntaxError: Missing initializer in const declaration
const PI = 3.14; // 正确

不允许重复赋值

javascript 复制代码
const obj = { name: 'Alice' };
obj.name = 'Bob'; // 允许,修改的是对象属性
obj = { name: 'Charlie' }; // TypeError: Assignment to constant variable

2.3 对比 varletconst

特性 var let const
作用域 函数级 / 全局 块级 块级
变量提升
重复声明 允许 不允许 不允许
重新赋值 允许 允许 不允许
初始化要求 必须初始化

新手易错点 :优先使用 const,需要重新赋值时用 let,尽量避免 var


三、函数增强

3.1 箭头函数

箭头函数是 ES6 最常用的特性之一,语法简洁且 this 指向明确。

基本语法 :省去 functionreturn

javascript 复制代码
// 传统函数
const add = function(a, b) { return a + b; };
// 箭头函数
const add = (a, b) => { return a + b; };
// 单行表达式可省略 `{}` 和 `return`
const add = (a, b) => a + b;
// 单个参数可省略 `()`
const square = x => x * x;

核心特性

没有自己的 this这是最核心的特性 。箭头函数的 this就像"透明的",它直接继承定义时所在外层代码块this值,且一旦绑定,永不改变 。这解决了传统函数中 this指向混乱的经典问题。

javascript 复制代码
function Timer() {
    this.seconds = 0;
    // 传统函数,`this` 指向调用它的对象(这里会是window或undefined)
    setInterval(function() {
        this.seconds++; // ❌ 这里的this不是Timer实例!
    }, 1000);
    // 箭头函数,`this` 继承自Timer函数(即Timer实例)
    setInterval(() => {
        this.seconds++; // ✅ 这里的this就是Timer实例!
    }, 1000);
}

不能用作构造函数 :因为它没有自己的 this,自然无法 new

没有 arguments 对象:可用 Rest 参数替代。

3.2 函数参数默认值

允许为函数参数设置默认值,当参数未传递或为 undefined 时使用默认值。给参数一个"保底"值,避免 undefined捣乱。function greet(name = '访客')

javascript 复制代码
function greet(name = 'Guest') {
  console.log(`Hello, ${name}!`);
}
greet(); // Hello, Guest!
greet('Alice'); // Hello, Alice!

3.3 Rest 参数

使用 ... 将函数的剩余参数收集为一个数组,用于处理不定数量的参数。它像一个"吸尘器",把函数多余的实参收集成一个真正的数组。完美替代了原本的类数组对象 arguments。

javascript 复制代码
function sum(...nums) {
  return nums.reduce((acc, curr) => acc + curr, 0);
}
sum(1, 2, 3); // 6

四、字符串与正则表达式

4.1 模板字符串

使用反引号 ````` 定义,,是字符串的超级模式解决了传统字符串拼接的痛点。

多行字符串 :直接换行即可,无需 \n拼接。

javascript 复制代码
const str = `这是第一行
这是第二行`;

变量 / 表达式插值 :用 ${} 嵌入变量或表达式。${expression},可以放入任何有效的JS表达式,它会自动转换为字符串拼接进去。这本质是将数据和展示更清晰、更安全地分离。

javascript 复制代码
const name = 'Alice';
const age = 25;
const info = `Name: ${name}, Age: ${age + 1}`; // Name: Alice, Age: 26

4.2 字符串扩展方法

  • str.includes(s):判断字符串是否包含指定子串,返回布尔值。
  • str.startsWith(s):判断字符串是否以指定子串开头。
  • str.endsWith(s):判断字符串是否以指定子串结尾。
  • str.repeat(n):将字符串重复 n 次,返回新字符串。
javascript 复制代码
'Hello'.includes('e'); // true
'Hello'.startsWith('H'); // true
'Hello'.endsWith('o'); // true
'Hi'.repeat(3); // 'HiHiHi'

4.3 正则表达式扩展

  • u 修饰符(Unicode 模式):正确处理 4 字节的 Unicode 字符(如 emoji)。
  • y 修饰符(粘连匹配) :要求匹配必须从剩余字符串的第一个位置开始。

五、解构赋值

本质是从一个复杂的"数据包裹"(对象或数组)中,精准、快速地提取出你需要的数据项,并赋值给对应的变量。

5.1 数组的解构赋值

位置提取数组中的值赋给变量。

  • 基本用法

    javascript 复制代码
    let [a, b] = [1, 2];
    console.log(a); // 1
    console.log(b); // 2
  • 嵌套解构

    javascript 复制代码
    let [a, [b, c]] = [1, [2, 3]];
  • 默认值

    javascript 复制代码
    let [a, b = 10] = [5];
    console.log(b); // 10
  • Rest 模式

    javascript 复制代码
    let [a, ...rest] = [1, 2, 3];
    console.log(rest); // [2, 3]

5.2 对象的解构赋值

属性名提取对象中的值赋给变量。

  • 基本用法

    javascript 复制代码
    let { name, age } = { name: 'Alice', age: 25 };
    console.log(name); // Alice
  • 重命名变量

    javascript 复制代码
    let { name: username } = { name: 'Alice' };
    console.log(username); // Alice
  • 默认值

    javascript 复制代码
    let { name, gender = 'female' } = { name: 'Alice' };
  • 函数参数解构

    javascript 复制代码
    function greet({ name = 'Guest' }) {
      console.log(`Hello, ${name}!`);
    }
    greet({ name: 'Alice' });

六、数组的扩展

6.1 扩展运算符 ...

将数组展开为逗号分隔的序列,是 ES6 最实用的操作符之一。它和Rest参数是"一体两面":Rest是"收集",扩展是"展开"。它像一把"胡椒罐",可以把数组(一罐胡椒粉)里的内容均匀地"撒"到需要的地方。

  • 函数调用

    javascript 复制代码
    function sum(a, b, c) { return a + b + c; }
    const nums = [1, 2, 3];
    sum(...nums); // 6
  • 数组字面量 :合并数组(替代 concat)。

    javascript 复制代码
    const arr1 = [1, 2];
    const arr2 = [3, 4];
    const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
  • 复制数组

    javascript 复制代码
    const oldArr = [1, 2, 3];
    const newArr = [...oldArr]; // 浅拷贝

6.2 新增的数组方法

  • Array.from():将类数组对象可迭代对象 转为数组。可以理解为将"像数组的东西"(如arguments, NodeList)变成"真正的数组",以便使用数组方法。

    javascript 复制代码
    Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
  • Array.of():将一组值转为数组(解决 Array() 参数个数不同导致行为差异的问题)。

    javascript 复制代码
    Array.of(1, 2, 3); // [1, 2, 3]
  • arr.find(fn) / arr.findIndex(fn):查找符合条件的第一个元素 / 索引, 返回第一个 满足条件的元素findIndex返回其索引

    javascript 复制代码
    [1, 2, 3].find(x => x > 2); // 3
    [1, 2, 3].findIndex(x => x > 2); // 2
  • arr.includes(item):判断数组是否包含某元素(比 indexOf 更直观,可识别 NaN)。

  • arr.flat(depth):数组扁平化,depth 为扁平化深度(默认 1)。

    javascript 复制代码
    [1, [2, [3]]].flat(2); // [1, 2, 3]

七、对象的扩展

7.1 属性的简洁表示法

当属性名与变量名相同时,可简写;方法也可简写。

javascript 复制代码
const name = 'Alice';
const obj = {
  name, // 等价于 name: name
  sayHi() { // 等价于 sayHi: function() {}
    console.log('Hi!');
  }
};

7.2 属性名表达式

在对象字面量中,用 [expression] 作为属性名。

javascript 复制代码
const propName = 'age';
const obj = {
  ['prop' + 'Name']: 'Alice',
  [propName]: 25
};
// obj: { propName: 'Alice', age: 25 }

7.3 对象的新增方法

  • Object.is(val1, val2):更严格的相等比较(解决 ===NaN !== NaN+0 === -0 的问题)。

    javascript 复制代码
    Object.is(NaN, NaN); // true
    Object.is(+0, -0); // false
  • Object.assign(target, ...sources):将源对象的属性浅拷贝 到目标对象。

    javascript 复制代码
    const target = { a: 1 };
    const source = { b: 2 };
    Object.assign(target, source); // target: { a: 1, b: 2 }
  • Object.keys() / Object.values() / Object.entries():分别返回对象的键数组值数组键值对数组

    javascript 复制代码
    const obj = { a: 1, b: 2 };
    Object.keys(obj); // ['a', 'b']
    Object.values(obj); // [1, 2]
    Object.entries(obj); // [['a', 1], ['b', 2]]

八、Symbol

8.1 概述

Symbol 是 ES6 新增的原始数据类型 ,用于表示独一无二的值 。创建一个全局唯一 的值,主要用于解决对象属性名冲突问题。它像工厂里为每一件产品打上的永不重复的序列号

8.2 基本用法

  • 创建 Symbol

    javascript 复制代码
    let s1 = Symbol('desc'); // 'desc' 是描述符,仅用于调试
    let s2 = Symbol('desc');
    console.log(s1 === s2); // false,即使描述符相同,值也不同
  • 作为对象属性名 :防止属性名冲突。

    javascript 复制代码
    const mySymbol = Symbol('key');
    const obj = {
      [mySymbol]: 'value'
    };
    console.log(obj[mySymbol]); // 'value'

九、集合类型

9.1 Set

Set 是一种成员值唯一、无序 的数据结构。是一个值永不重复 的集合。它像一本数学意义上的"集合",自动帮你去重

  • 常用 API

    javascript 复制代码
    add(value):添加值。
    delete(value):删除值。
    has(value):判断是否包含值。
    size:获取成员数量。
    clear():清空所有成员。
  • 数组去重

    javascript 复制代码
    const arr = [1, 2, 2, 3];
    const uniqueArr = [...new Set(arr)]; // [1, 2, 3]

9.2 Map

Map 是一种键值对集合 ,键可以是任意类型(对象的键只能是字符串或 Symbol)。它弥补了传统对象只能用字符串或Symbol作键的限制。

Object是键为字符串的电话本。Map是一个万能仓库,你可以用一把真正的钥匙(对象)、一张照片(函数)​ 作为"标签"来存放物品。

  • 常用 API

    javascript 复制代码
    set(key, value):设置键值对。
    get(key):获取键对应的值。
    has(key):判断是否包含键。
    delete(key):删除键值对。
    size:获取键值对数量。
javascript 复制代码
const map = new Map();
const objKey = { id: 1 };
map.set(objKey, 'value');
console.log(map.get(objKey)); // 'value'

十、迭代器与生成器

10.1 迭代器

  • Iterable 接口 :拥有 Symbol.iterator 方法的对象(如数组、Set、Map、字符串)。如果一个对象拥有 [Symbol.iterator]方法,它就是"可迭代的"。这个方法返回一个迭代器对象

  • for...of 循环 :用于遍历实现了 Iterable 接口的对象。是专门为消费"可迭代对象"(数组、Map、Set、字符串等)设计的语法糖。它内部会自动调用迭代器,依次获取值。

    javascript 复制代码
    for (let item of [1, 2, 3]) {
      console.log(item); // 1, 2, 3
    }

核心区别for...in遍历键名 (适合对象),for...of遍历键值(适合数组等集合)。

10.2 生成器

生成器是一种可以暂停和恢复执行 的函数,使用 function* 定义,yield 暂停执行。生成器像一个"可多次暂停和继续的函数"。它返回一个迭代器 。每次调用迭代器的 next(),函数就从上次 yield处恢复执行,直到下一个 yieldreturn。用于实现惰性求值、异步编程(async/await的底层基础)。

  • 基本语法

    javascript 复制代码
    function* gen() {
      yield 1;
      yield 2;
      return 3;
    }
    const g = gen();
    g.next(); // { value: 1, done: false }
    g.next(); // { value: 2, done: false }
    g.next(); // { value: 3, done: true }
  • 核心特性 :惰性求值,每次调用 next() 才执行到下一个 yield


十一、Promise

11.1 异步编程的背景

传统异步编程依赖回调函数,容易导致回调地狱 (嵌套过深,代码可读性差)。它是一个"异步任务收据"。它代表一个在未来才会完成(或失败)的操作及其最终结果。它解决了"回调地狱",将异步操作以同步代码的链式调用形式组织起来。

11.2 Promise 基本概念

Promise 是异步编程的一种解决方案,代表一个异步操作的最终完成或失败。

  • 三种状态

    • pending:进行中。收据已开,任务未完成。
    • fulfilled:已成功。任务成功完成,收据有了结果值。
    • rejected:已失败。任务失败,收据知道了原因。
    • 状态一旦改变,就凝固了(从Pending变为Fulfilled或Rejected),不会再变。
  • 创建 Promise

    javascript 复制代码
    const promise = new Promise((resolve, reject) => {
      // 异步操作
      if (/* 成功 */) {
        resolve('success');
      } else {
        reject('error');
      }
    });

11.3 Promise 实例方法

  • .then(onFulfilled, onRejected):处理成功或失败。
  • .catch(onRejected):专门处理失败(推荐使用,更清晰)。
  • .finally(onFinally):无论成功或失败都会执行。
javascript 复制代码
promise
  .then(res => console.log(res))
  .catch(err => console.log(err))
  .finally(() => console.log('done'));

11.4 Promise 静态方法

  • Promise.resolve(value):将现有值转为 Promise 对象(状态为 fulfilled)。
  • Promise.reject(reason):返回一个状态为 rejected 的 Promise。
  • Promise.all([p1, p2, ...]):等待所有 Promise 成功,有一个失败则整体失败。
  • Promise.race([p1, p2, ...]):竞速,以第一个完成的 Promise 为准。

示例:

javascript 复制代码
const promise = new Promise((resolve, reject) => {
    // 执行异步任务,比如请求数据
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve('数据获取成功!'); // 状态变为Fulfilled
        } else {
            reject('获取失败!'); // 状态变为Rejected
        }
    }, 1000);
});

// 消费Promise
promise
    .then(data => { console.log(data); }) // 成功回调
    .catch(error => { console.error(error); }) // 失败回调
    .finally(() => { console.log('请求结束'); }); // 无论如何都执行

11.5 Promise.all 与 Promise.race

  • Promise.all([p1, p2, p3]) :像"集体照",必须**所有人(所有Promise)都到齐(成功)**​ 才算成功,一个人没来(失败)就失败。

  • Promise.race([p1, p2, p3]) :像"赛跑",以**第一个冲过终点(完成)**​ 的选手结果为准。

新手误解点.then.catch本身返回一个新的Promise ,这使链式调用成为可能。.catch不仅能捕获前面Promise的reject,还能捕获前面 .then回调中抛出的错误。


十二、类

12.1 class 关键字

ES6 引入 class 语法,class是ES5基于原型的继承的语法糖,它提供了更清晰、更接近传统面向对象语言的写法,但底层依然是原型链。

javascript 复制代码
class Person {
  // 类的内容
}

12.2 类的构成

  • 构造函数 constructor():创建实例时自动调用。
  • 实例方法:通过实例调用。
  • 静态方法 / 属性 :用 static 关键字定义,通过类本身调用。
javascript 复制代码
class Person {
  constructor(name, age) {
    this.name = name; // 实例属性
    this.age = age;
  }
  sayHi() { // 实例方法
    console.log(`Hi, I'm ${this.name}`);
  }
  static isAdult(age) { // 静态方法
    return age >= 18;
  }
}
const alice = new Person('Alice', 25);
alice.sayHi(); // Hi, I'm Alice
Person.isAdult(25); // true

12.3 继承

  • extends 关键字:实现子类继承父类。
  • super 关键字:调用父类的构造函数或方法。
javascript 复制代码
class Student extends Person {
  constructor(name, age, grade) {
    super(name, age); // 调用父类构造函数
    this.grade = grade;
  }
  study() {
    console.log(`${this.name} is studying`);
  }
}

十三、模块化

13.1 模块化概念

ES6 引入了原生模块化系统,一个文件就是一个模块 ,解决了命名冲突和依赖管理问题。"高内聚,低耦合"。每个文件是一个独立的模块,拥有自己的作用域,只暴露需要公开的部分,按需引入其他模块的功能。就像乐高积木,每块独立制造,通过标准接口拼接成复杂系统。

13.2 export 命令

用于导出模块中的变量、函数或类,分为命名导出默认导出

  • 命名导出 (可多个):

    javascript 复制代码
    // module.js
    export const name = 'Alice';
    export function sayHi() { console.log('Hi!'); }
    // 或统一导出
    const name = 'Alice';
    function sayHi() { console.log('Hi!'); }
    export { name, sayHi };
  • 默认导出 (只能一个):

    javascript 复制代码
    // module.js
    export default class Person { /* ... */ }
    
    // MyClass.js
    export default class MyClass { ... }
    
    // main.js
    import MyClass from './MyClass.js'; // 导入时可以任意命名

13.3 import 命令

用于导入其他模块的内容。

  • 导入命名导出

    javascript 复制代码
    import { name, sayHi } from './module.js';
    // 重命名导入
    import { name as username } from './module.js';
  • 导入默认导出

    javascript 复制代码
    import Person from './module.js';
相关推荐
zx_zx_1232 小时前
红黑树的学习
学习
小陈phd2 小时前
多模态大模型学习笔记(二十六)—— 核心技术篇③ | 虚拟人的声音情感:从语音合成到声音克隆
笔记·学习
不会聊天真君6472 小时前
基础语法·下(golang笔记第三期)
开发语言·笔记·golang
FakeOccupational2 小时前
【电路笔记 通信】IEEE 1588精密时间协议(PTP):数学假设+时间同步链路建模+消除主从偏差算法
笔记·算法
云边散步2 小时前
godot2D游戏教程系列二(23)
笔记·学习·游戏·音视频·游戏开发
hzb666662 小时前
xd_day32-day40
java·javascript·学习·安全·web安全·tomcat·php
前端小趴菜~时倾3 小时前
自我提升-python爬虫学习:day05-函数与面向对象编程
爬虫·python·学习
像素猎人3 小时前
差分数组【自用笔记】【c++】
c++·笔记·算法
星幻元宇VR3 小时前
VR安全带防坠落体验平台|强化高空作业安全教育的新途径
科技·学习·安全·生活·vr