文章目录
- 箭头函数(Arrow Functions)
- 为什么需要箭头函数?
- 箭头函数的完整语法
- 箭头函数中的 `this`
- 实用场景
- 解构赋值(Destructuring Assignment)
- 为什么需要解构赋值?
- 数组解构赋值的完整用法
- 对象解构赋值的完整用法
- 实用场景
- 模块化(Modules)
- 为什么需要模块化?
- 模块的基本概念
- 模板字面量(Template Literals)
- 为什么需要模板字面量?
- 模板字面量的基本语法
- 高级用法
- 使用场景
- Promise与异步编程
- 为什么需要 Promise 和异步编程?
- Promise 的语法与用法
- async 和 await
- 实用场景
箭头函数(Arrow Functions)
为什么需要箭头函数?
- 代码简洁:
对比代码:
// ES5 写法
const numbers = [1, 2, 3];
const squared = numbers.map(function (n) {return n * n;
});// ES6 箭头函数写法
const squared = numbers.map(n => n * n);
- ES5 中的函数定义通常会比较冗长,特别是在回调函数或嵌套逻辑中,代码可读性较差。
- 箭头函数通过简化语法,减少 `function` 和 `return` 等关键字的使用,使代码更加直观。
- 避免
this
问题:- 在 ES5 中,函数的
this
是由调用时动态决定的,可能会指向全局对象或undefined
,造成意外错误。 - 箭头函数继承了定义时的
this
(即词法作用域),从而避免了this
绑定问题。
- 在 ES5 中,函数的
箭头函数的完整语法
- 基本形式:
const func = (param1, param2) => {// 函数体return param1 + param2;
};
- 省略规则:
- 如果函数体只有一行表达式,并且需要返回值,可以省略大括号
{}
和return
:
- 如果函数体只有一行表达式,并且需要返回值,可以省略大括号
const add = (a, b) => a + b;
- 如果没有参数,必须加括号:
const greet = () => console.log("Hello!");
- 返回对象字面量:
如果需要返回一个对象字面量,必须用括号包裹,否则会与箭头函数的语法冲突:
const createPerson = (name, age) => ({ name, age });
console.log(createPerson("Kevin", 25)); // 输出 { name: "Kevin", age: 25 }
箭头函数中的 this
this
** 的词法作用域**:
示例:
// 普通函数中 this 的问题
function Timer() {this.seconds = 0;setInterval(function () {this.seconds++; // this 指向全局或 undefinedconsole.log(this.seconds);}, 1000);
}// 用箭头函数解决
function Timer() {this.seconds = 0;setInterval(() => {this.seconds++; // this 绑定到 Timer 对象console.log(this.seconds);}, 1000);
}
- 箭头函数不会创建自己的 `this`,它会从外层作用域中继承 `this`。
- 这在回调函数中尤为重要,避免了手动绑定 `this` 的麻烦。
- 无法用作构造函数:
箭头函数没有[[Construct]]
方法,因此不能使用new
来调用。
const Person = (name) => {this.name = name;
};// new Person("Kevin"); // 报错
实用场景
- 回调函数:
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2); // 简化 map 回调
- 事件监听:
const button = document.querySelector("button");
button.addEventListener("click", () => {console.log("Button clicked!");
});
解构赋值(Destructuring Assignment)
为什么需要解构赋值?
- 简化代码:
在传统写法中,获取数组或对象的特定值需要多次声明变量。解构赋值直接在一行代码中完成这一任务,使代码更紧凑。对比代码:
// ES5 写法
const person = { name: "Kevin", age: 25 };
const name = person.name;
const age = person.age;// ES6 解构赋值
const { name, age } = person;
- 避免多次访问对象属性:
解构赋值减少了对同一对象或数组的多次访问,提升性能。
数组解构赋值的完整用法
- 按顺序提取值:
const arr = [10, 20, 30];
const [first, second] = arr;
console.log(first, second); // 输出 10 20
- 跳过某些值:
const arr = [1, 2, 3];
const [, second] = arr;
console.log(second); // 输出 2
- 使用默认值:
const arr = [10];
const [first, second = 20] = arr;
console.log(second); // 输出 20
对象解构赋值的完整用法
- 基础用法:
const obj = { x: 10, y: 20 };
const { x, y } = obj;
console.log(x, y); // 输出 10 20
- 别名(重命名):
const obj = { x: 10, y: 20 };
const { x: newX, y: newY } = obj;
console.log(newX, newY); // 输出 10 20
- 嵌套解构:
const obj = { person: { name: "Kevin", age: 25 } };
const { person: { name, age } } = obj;
console.log(name, age); // 输出 Kevin 25
实用场景
- 函数参数:
function greet({ name, age }) {console.log(`Hello, ${name}. You are ${age} years old.`);
}
greet({ name: "Kevin", age: 25 });
- 交换变量值:
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 输出 2 1
模块化(Modules)
为什么需要模块化?
在 ES5 之前,JavaScript 没有原生模块化系统,开发者通常使用全局变量或外部库(如 require.js
)来实现模块化。但这会导致代码难以维护、命名冲突等问题。
模块的基本概念
- 导出模块:
- 命名导出:可以导出多个变量或函数。
export const name = "Kevin";
export function greet() {console.log("Hello!");
}
- **默认导出**:每个模块只能有一个默认导出。
export default function greet() {console.log("Hello!");
}
- 引入模块:
- 引入命名导出:
import { name, greet } from "./module.js";
- **引入默认导出**:
import greet from "./module.js";
好的,以下将针对剩余的 模板字面量(Template Literals) 和 Promise与异步编程 进行更加详细的讲解,包括其语法背景、具体用法、以及背后的原理和实用场景。
模板字面量(Template Literals)
为什么需要模板字面量?
在 ES5 中,字符串的操作非常繁琐,尤其是在拼接变量时,需要使用 +
串联,且难以处理多行字符串。此外,插入表达式和动态生成内容也较为麻烦。
模板字面量 的出现解决了这些问题,提供了一种更加优雅的方式来处理字符串。
模板字面量的基本语法
- 模板字面量的定义:用反引号 ``(反引号 ` 而不是单引号 ')定义的字符串。**
const message = `这是一个模板字面量`;
- 插入变量(占位符):
使用${}
将变量或表达式嵌入到字符串中:
const name = "Kevin";
const age = 25;
const message = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(message); // 输出:Hello, my name is Kevin and I am 25 years old.
- 多行字符串:
在 ES5 中,我们需要通过\n
或字符串拼接实现多行字符串,这不仅麻烦而且不直观。- ES5 写法:
const text = "这是第一行\n" +"这是第二行";
console.log(text);
- **模板字面量写法**:
const text = `这是第一行
这是第二行`;
console.log(text); // 输出多行字符串
- 嵌入表达式:
可以直接在${}
中编写表达式,而不仅仅是变量:
const a = 10, b = 20;
const result = `结果是:${a + b}`;
console.log(result); // 输出:结果是:30
高级用法
- 函数调用:
模板字面量支持在${}
中直接调用函数:
function greet(name) {return `Hello, ${name}!`;
}
const message = `${greet("Kevin")}`;
console.log(message); // 输出:Hello, Kevin!
- 标签模板(Tagged Template Literals):
标签模板允许我们通过函数对模板字面量进行自定义解析。
function highlight(strings, ...values) {return strings.reduce((result, str, i) => {return `${result}${str}<strong>${values[i] || ""}</strong>`;}, "");
}const name = "Kevin";
const age = 25;
const message = highlight`Name: ${name}, Age: ${age}`;
console.log(message);
// 输出:Name: <strong>Kevin</strong>, Age: <strong>25</strong>
- 嵌套模板字面量:
支持嵌套使用模板字面量:
const user = { name: "Kevin", age: 25 };
const info = `User Info: ${`Name: ${user.name}, Age: ${user.age}`}`;
console.log(info);
// 输出:User Info: Name: Kevin, Age: 25
使用场景
- 动态生成 HTML:
模板字面量非常适合用于构建动态 HTML 字符串:
const name = "Kevin";
const html = `<div><h1>${name}</h1><p>欢迎访问我的网站!</p></div>`;
console.log(html);
- 构建 SQL 查询:
const table = "users";
const column = "name";
const sql = `SELECT ${column} FROM ${table} WHERE active = 1`;
console.log(sql);
// 输出:SELECT name FROM users WHERE active = 1
Promise与异步编程
为什么需要 Promise 和异步编程?
在 JavaScript 中,异步操作(如网络请求、文件读取、定时器等)是常见场景。
传统的异步处理方式(如回调函数)可能会导致 回调地狱(Callback Hell),使代码难以阅读和维护。
Promise 和 async/await
的引入解决了这些问题,使异步代码更加可读和可维护。
Promise 的语法与用法
- 创建 Promise:
Promise 是一个对象,表示一个异步操作的最终完成(或失败)及其结果。
它有三种状态:
示例:
const promise = new Promise((resolve, reject) => {const success = true;if (success) {resolve("操作成功");} else {reject("操作失败");}
});promise.then(result => console.log(result)) // 操作成功.catch(error => console.log(error)); // 捕获错误
- `pending`:初始状态,操作尚未完成。
- `fulfilled`:操作成功完成。
- `rejected`:操作失败。
- 链式调用:
Promise 支持链式调用,使代码逻辑更清晰。
const fetchData = () => {return new Promise(resolve => {setTimeout(() => resolve("数据加载完成"), 1000);});
};fetchData().then(data => {console.log(data);return "处理后的数据";}).then(processed => console.log(processed));
- Promise.all:
并行执行多个 Promise,并等待所有完成:
const p1 = new Promise(resolve => setTimeout(() => resolve(1), 1000));
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 2000));Promise.all([p1, p2]).then(results => console.log(results));
// 输出:[1, 2]
async 和 await
- 为什么需要 async/await?
async/await
是 Promise 的语法糖,使异步代码看起来像同步代码,极大地提高了代码的可读性。
- 基本用法:
示例:
const fetchData = () => {return new Promise(resolve => setTimeout(() => resolve("数据加载完成"), 1000));
};async function loadData() {const data = await fetchData();console.log(data); // 输出:数据加载完成
}loadData();
- 使用 `async` 声明一个函数,该函数返回一个 Promise。
- 使用 `await` 暂停异步操作,直到 Promise 完成。
- 错误处理:
可以使用try...catch
捕获错误:
async function fetchWithErrorHandling() {try {const response = await fetch("https://api.example.com/data");const data = await response.json();console.log(data);} catch (error) {console.error("请求出错:", error);}
}
实用场景
- 数据请求:
async function fetchData() {const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");const post = await response.json();console.log(post);
}fetchData();
- 动画和定时器:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));async function run() {console.log("开始");await delay(1000); // 等待 1 秒console.log("结束");
}run();