ES6 零基础入门指南:核心特性与实战应用全解析
文章目录
前言
ES6 作为 JavaScript 的里程碑版本,彻底革新了前端开发的语法与效率。它不仅带来了 let、Promise 等实用特性,更让代码编写更简洁、逻辑更清晰。本文从基础概念出发,用通俗语言和简单实例拆解 ES6 核心知识点,帮你快速掌握这门前端必备技能,为框架学习和项目开发筑牢根基。
一、ES6是什么?为什么要学ES6?
1、什么是ES6?
ES6其实是ECMAScript 6的简称。ECMAScript 是JavaScript的语法标准,由 Ecma 国际组织制定,用来规范 JavaScript 的语言规则。
从 1997 年第一版发布到现在,ECMAScript 已经更新了多个版本。其中 ES6 在 2015 年发布,是改动最大、最具里程碑意义的版本。从 ES6 开始,每年都会发布一个新版本,版本号比年份最后一位大 1(比如 2016 年发布的 ES7,2019 年发布的 ES10)。
简单说,ES6 就是 JavaScript 的 “升级版本”,在原有基础上增加了很多实用的新语法和新功能。
2、为什么要学习ES6?
- ES6 的变动内容最多,加入了大量新特性,学会它能让编程更简单、更高效;
- 现在前端开发中,ES6 已经成为行业标准,不管是框架开发还是日常工作,都离不开它;
- 掌握 ES6 是前端就业的必备技能,大部分企业招聘都会要求熟练使用 ES6 特性。
二、ES6的核心新特性
1、let关键字
let 专门用来声明变量,和以前的 var 相比,它的规则更清晰,用起来更安全,有三个核心特点:
- 不允许重复声明:同一个变量不能用 let 重复定义;
- 有块级作用域:变量只在所在的大括号{}内有效,出了大括号就不能用了;
- 不存在变量提升:不能在声明变量之前使用它。
// 1. 不允许重复声明
let name = "张三";
// let name = "李四"; // 报错:变量已声明
// 2. 块级作用域
if (true) {
let age = 18;
console.log(age); // 输出:18(块内可以访问)
}
// console.log(age); // 报错:age未定义(块外不能访问)
// 3. 不存在变量提升
// console.log(gender); // 报错:不能在声明前使用
let gender = "男";
let 与 var 的区别
- 作用域不同
var:函数作用域。变量只在声明它的函数内部有效,若在函数外部声明,则为全局作用域。
let:块级作用域。变量在声明它的代码块(如 {} 、 if 、 for 等)内部有效,超出该块则不可访问。 - 变量提升
var:会被提升到函数顶部(声明被提升,赋值不提升)。
let:不会被提升,在声明前访问变量会报错。 - 重复声明
var:允许在同一作用域内重复声明同一变量(后声明的会覆盖前声明的)。
let:不允许在同一作用域内重复声明同一变量(会直接报错)。 - 全局对象属性
var:在全局作用域声明的变量会成为全局对象(如浏览器中的 window )的属性。
let:在全局作用域声明的变量不会成为全局对象的属性。 - 循环中的表现
var:在循环中声明的变量会共享同一个变量实例(循环结束后仍可访问,值为最后一次迭代的值)。
let:在循环中声明的变量会为每次迭代创建新的变量绑定(循环结束后不可访问,且每次迭代的值独立)。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>区别var与let</title>
</head>
<body>
<script>
console.log(a); // undefined
var a = 1;
//console.log(b);
let b = 2; // 报错,不可以变量提升
{
var c = 1;
console.log(c); // 1
}
console.log(c); // 1
{
let d = 1;
console.log(d); // 1
}
//console.log(d); // 报错,块级作用域
let e = 1;
// let e = 2; // 报错,不可重复声明变量
console.log(e);
</script>
</body>
</html>
2、const关键字
const 用来声明 “常量”,也就是值不能随意修改的量,有五个核心特点:
- 声明时必须赋初始值:不能只写const a;,必须写成const a = 10;;
- 标识符一般大写:这是约定俗成的规范,方便区分常量和变量;
- 不允许重复声明:和 let 一样,不能重复定义同一个常量;
- 值不允许修改:常量赋值后,不能再改它的值(如果是对象,对象里的属性可以改);
- 有块级作用域:和 let 一样,只在所在的大括号内有效。
// 1. 声明必须赋初始值
const PI = 3.14;
// const NAME; // 报错:缺少初始值
// 2. 标识符大写(规范)
const SCHOOL = "第一中学";
// 3. 不允许重复声明
// const PI = 3.1415; // 报错:常量已声明
// 4. 值不允许修改
// PI = 3.1415; // 报错:不能修改常量的值
// 5. 块级作用域
if (true) {
const CITY = "北京";
console.log(CITY); // 输出:北京(块内可以访问)
}
// console.log(CITY); // 报错:CITY未定义(块外不能访问)
// 特殊情况:对象类型的常量,属性可以修改
const PERSON = { name: "张三" };
PERSON.name = "李四"; // 允许修改对象属性
console.log(PERSON.name); // 输出:李四
应用场景:声明不需要修改的值用 const(比如固定的配置、对象),需要修改的值用 let。
3、变量的解构赋值
解构赋值是什么呢?
解构赋值就是按照一定模式,从数组或对象中提取值,直接赋给变量。这样写不用一次次通过索引或属性名获取数据,更简洁。
数组结构:
// 以前提取数组元素
const fruits = ["苹果", "香蕉", "橙子"];
let f1 = fruits[0];
let f2 = fruits[1];
console.log(f1, f2); // 输出:苹果 香蕉
// 用解构赋值提取
const [a1, a2, a3] = fruits;
console.log(a1, a2, a3); // 输出:苹果 香蕉 橙子
对象解构:
// 以前提取对象属性
const person = {
name: "林志颖",
age: 40,
tags: ["车手", "歌手", "演员"]
};
let pName = person.name;
let pTags = person.tags;
console.log(pName, pTags); // 输出:林志颖 ["车手","歌手","演员"]
// 用解构赋值提取
const { name, tags } = person;
console.log(name); // 输出:林志颖
console.log(tags); // 输出:["车手","歌手","演员"]
应用场景:频繁使用数组元素或对象属性时,用解构赋值能省很多代码。
4、模板字符串
模板字符串用反引号`标识,比普通字符串更强大,解决了字符串拼接的麻烦,有两个核心特点:
- 可以直接换行:字符串里不用加\n,直接换行写就行;
- 可以用${变量名}形式插入变量:不用再用+拼接变量和字符串。
// 1. 可以直接换行
const html = `
<ul>
<li>张三</li>
<li>李四</li>
<li>王五</li>
</ul>
`;
console.log(html); // 输出带换行的HTML结构
// 2. 插入变量
const star = "田七";
const result = `${star} Hello`;
console.log(result); // 输出:田七 Hello
// 3. 插入表达式
const a = 10;
const b = 20;
const sumStr = `${a} + ${b} = ${a + b}`;
console.log(sumStr); // 输出:10 + 20 = 30
应用场景:需要拼接字符串和变量时,优先用模板字符串。
5、箭头函数
箭头函数用=>来定义函数,语法更简洁,还解决了 this 指向的问题,有五个核心特点:
- 形参只有一个时,小括号可以省略;
- 函数体只有一条语句时,花括号可以省略,并且会自动返回这条语句的结果;
- this 指向声明时所在作用域的 this(不是调用时的对象);
- 不能作为构造函数:不能用 new 关键字创建实例;
- 不能使用 arguments 对象:要获取参数,用剩余参数…代替。
// 1. 通用写法(多个参数,多条语句)
const add = (a, b) => {
return a + b;
};
console.log(add(10, 20)); // 输出:30
// 2. 省略小括号(只有一个参数)
const multiply = num => {
return num * 10;
};
console.log(multiply(3)); // 输出:30
// 3. 省略花括号(只有一条语句,自动返回)
const subtract = (a, b) => a - b;
console.log(subtract(50, 15)); // 输出:35
// 4. this指向(声明时的作用域)
const person = {
name: "张三",
sayHi: function() {
// 普通函数的this指向调用者(person)
const fn = () => {
console.log(this.name); // 箭头函数的this继承自sayHi的this,输出:张三
};
fn();
}
};
person.sayHi();
应用场景:简单函数、回调函数(比如数组方法里的函数)用箭头函数,写法更简洁。
6、回调地狱
在处理多个异步操作时,如果每个操作都嵌套在前面操作的回调函数里,就会形成 “回调地狱”。代码会一层套一层,看起来像金字塔,可读性和维护性都很差。
// 需求:等待1秒打印Python,再等2秒打印Web前端,再等3秒打印Dify
setTimeout(() => {
console.log("Python");
setTimeout(() => {
console.log("Web前端");
setTimeout(() => {
console.log("Dify");
}, 3000); // 第三个异步操作
}, 2000); // 第二个异步操作
}, 1000); // 第一个异步操作
现在存在的一个问题就是:如果有更多异步操作,代码会一直嵌套下去,很难看懂和修改。
那么解决这个问题的方案是什么呢?
答案就是下面要讲的 Promise。
7、同步请求与异步请求
在编程中,请求操作(比如从服务器拿数据、读取文件)分为同步和异步两种,两者的执行逻辑完全不同:
(1)同步请求
特点:按顺序执行,前一个任务完成后,才能执行下一个任务;会阻塞后续代码,前面的任务没做完,后面的代码就不能跑。
// 同步执行:先执行完alert,才会执行console.log
alert("第一个任务");
console.log("第二个任务"); // 只有关掉alert,才会打印
(2)异步请求
特点:不用等前一个任务完成,就能执行下一个任务;不会阻塞代码,前一个任务在执行时,后面的代码可以正常跑;结果通过回调函数、Promise、async/await 处理。
// 异步执行:setTimeout是异步操作,不会阻塞后续代码
setTimeout(() => {
console.log("异步任务完成");
}, 1000);
console.log("同步任务先执行"); // 先打印这句话,1秒后再打印上面的内容
8、Promise
Promise 是 ES6 引入的异步编程新方案,专门用来封装异步操作,还能清晰地获取成功或失败的结果,从根本上解决回调地狱的问题。
Promise 有三个状态:pending(等待中)、fulfilled(成功)、rejected(失败),核心用法是通过then方法处理成功结果,catch方法处理失败结果。
// 用Promise封装异步操作(等待指定时间后打印内容)
function waitAndPrint(content, time) {
// 返回Promise对象
return new Promise((resolve) => {
setTimeout(() => {
console.log(content);
resolve(); // 告诉Promise任务完成,可以执行下一个操作
}, time);
});
}
// 链式调用,解决回调地狱
waitAndPrint("Python", 1000)
.then(() => waitAndPrint("Web前端", 2000)) // 第一个任务完成后,执行第二个
.then(() => waitAndPrint("Dify", 3000)); // 第二个任务完成后,执行第三个
处理成功和失败
new Promise((resolve,reject)=>{
resolve("成功")
// reject("失败")
}).then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
9、模块化
模块化就是把一个大的程序文件,拆分成多个小文件,每个小文件负责一个功能,然后通过特定语法把这些小文件组合起来使用。
- 模块化的好处
- 防止命名冲突:不同文件里的变量、函数名可以重复,不会互相影响;
- 代码复用:一个模块可以在多个地方引入使用,不用重复写代码;
- 高维护性:代码拆分后,结构更清晰,修改起来更方便。
- ES6模块化语法
核心有两个命令:export(导出模块的功能)和import(导入其他模块的功能)。
导入模块:创建一个math.js文件,导出两个函数
// math.js
// 导出加法函数
export function add(a, b) {
return a + b;
}
// 导出乘法函数
export function multiply(a, b) {
return a * b;
}
导出模块:在另一个文件中导入并使用
// main.js
// 导入math.js中的功能
import { add, multiply } from "./math.js";
// 使用导入的函数
console.log(add(10, 20)); // 输出:30
console.log(multiply(3, 4)); // 输出:12
注意:使用模块化时,需要在 HTML 文件中引入脚本时加上type=“module”,比如:
<script type="module" src="main.js"></script>
三、async和await
async 和 await 是 ES8(2017 年)引入的特性,它们是 Promise 的语法糖,能让异步代码的写法更简洁,看起来和同步代码几乎一样。
- async 函数的特点
- async 关键字用在函数前面,标记这个函数是异步函数;
- async 函数的返回值是 Promise 对象;
- Promise 的结果由 async 函数的返回值决定:返回普通值,Promise 就是成功状态;抛出错误,Promise 就是失败状态。
- await 表达式的特点
- await 必须写在 async 函数里面,不能单独使用;
- await 后面一般跟 Promise 对象;
- await 会等待 Promise 完成,然后返回 Promise 成功的值;
- 如果 await 的 Promise 失败了,会抛出异常,需要用try…catch捕获处理。
// 1. 封装异步操作(和前面Promise实例一样)
function waitAndPrint(content, time) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(content);
resolve();
}, time);
});
}
// 2. 使用async/await编写异步代码
async function run() {
await waitAndPrint("Python", 1000); // 等待1秒,打印Python
await waitAndPrint("Web前端", 2000); // 再等2秒,打印Web前端
await waitAndPrint("Dify", 3000); // 再等3秒,打印Dify
}
// 调用函数
run();
失败的处理:
// 封装可能失败的异步操作
function fetchData() {
return new Promise((resolve, reject) => {
const success = false; // 模拟请求失败
if (success) {
resolve("请求到的数据");
} else {
reject("网络错误");
}
});
}
// 使用try...catch捕获失败
async function getData() {
try {
const data = await fetchData();
console.log("成功:", data);
} catch (error) {
console.log("失败:", error); // 输出:失败:网络错误
}
}
getData();
应用场景:处理异步操作时,用 async/await 比 Promise 的链式调用更简洁,代码逻辑更清晰。
结语
ES6 的新特性看似繁多,但核心都是为了让编程更高效、代码更易维护。掌握 let/const 的作用域、Promise 的异步逻辑、async/await 的简洁写法,能让你在前端开发中事半功倍。希望本文能帮你打通 ES6 的学习脉络,在实际项目中灵活运用这些特性,不断提升代码质量与开发效率。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)