个人主页:Guiat
归属专栏:HTML CSS JavaScript
文章目录
正文
1. ES6 (ECMAScript 2015) 核心特性
1.1 let 和 const
新的变量声明方式,提供块级作用域。
// let 声明的变量可以重新赋值
let count = 1;
count = 2;
// const 声明的常量不能重新赋值
const PI = 3.14159;
// PI = 3; // TypeError: Assignment to constant variable
// 块级作用域
if (true) {
let blockScoped = 'only available in this block';
var functionScoped = 'available throughout function';
}
// console.log(blockScoped); // ReferenceError
console.log(functionScoped); // "available throughout function"
// 暂时性死区 (TDZ)
// console.log(notYetDeclared); // ReferenceError
let notYetDeclared = 'this variable is in the TDZ before declaration';
1.2 箭头函数
简洁的函数语法,没有自己的 this
,arguments
,super
或 new.target
。
// 基本语法
const add = (a, b) => a + b;
// 等价于
function add(a, b) {
return a + b;
}
// 单个参数可以省略括号
const square = x => x * x;
// 无参数需要空括号
const getRandomNumber = () => Math.random();
// 多行函数体需要花括号和 return 语句
const calculateArea = (width, height) => {
const area = width * height;
return area;
};
// this 指向
function Traditional() {
this.value = 1;
setTimeout(function() {
this.value++; // this 指向 window 或 undefined
console.log(this.value); // NaN 或 错误
}, 1000);
}
function ArrowFunction() {
this.value = 1;
setTimeout(() => {
this.value++; // this 指向外层作用域的 this
console.log(this.value); // 2
}, 1000);
}
1.3 模板字符串
增强的字符串字面量,支持多行字符串和字符串插值。
// 多行字符串
const multiline = `This is a string
that spans multiple
lines.`;
// 字符串插值
const name = 'World';
const greeting = `Hello, ${name}!`;
console.log(greeting); // "Hello, World!"
// 表达式插值
const a = 5;
const b = 10;
console.log(`Sum: ${a + b}, Product: ${a * b}`);
// "Sum: 15, Product: 50"
// 带标签的模板字符串
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
return result + string + (values[i] ? `<strong>${values[i]}</strong>` : '');
}, '');
}
const language = 'JavaScript';
const experience = 5;
const text = highlight`I have been programming in ${language} for ${experience} years.`;
// "I have been programming in <strong>JavaScript</strong> for <strong>5</strong> years."
1.4 解构赋值
从数组或对象中提取值,对变量进行赋值。
// 数组解构
const [a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
// 跳过某些值
const [x, , z] = [1, 2, 3];
console.log(x, z); // 1 3
// 剩余模式
const [first, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [2, 3, 4]
// 默认值
const [p = 5, q = 7] = [1];
console.log(p, q); // 1 7
// 对象解构
const { name, age } = { name: 'John', age: 30 };
console.log(name); // "John"
console.log(age); // 30
// 属性重命名
const { name: userName, age: userAge } = { name: 'John', age: 30 };
console.log(userName); // "John"
console.log(userAge); // 30
// 嵌套解构
const {
name,
address: { city, country }
} = { name: 'John', address: { city: 'New York', country: 'USA' } };
console.log(city, country); // "New York" "USA"
// 函数参数解构
function printPerson({ name, age = 25 }) {
console.log(`${name} is ${age} years old`);
}
printPerson({ name: 'Alice' }); // "Alice is 25 years old"
1.5 默认参数
函数参数的默认值。
// 基本用法
function greet(name = 'Guest') {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, Guest!"
console.log(greet('John')); // "Hello, John!"
// 表达式作为默认值
function getTimestamp(time = Date.now()) {
return time;
}
// 参数默认值可以引用前面的参数
function createRange(start = 0, end = start + 10) {
return Array.from({ length: end - start }, (_, i) => start + i);
}
console.log(createRange(5)); // [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
// 解构与默认参数结合
function fetchData({ url, method = 'GET', data = {} } = {}) {
console.log(method, url, data);
}
fetchData({ url: 'api/users' }); // "GET" "api/users" {}
fetchData(); // "GET" undefined {}
1.6 扩展运算符
将可迭代对象展开为各个独立元素。
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];
console.log(arr2); // [1, 2, 3, 4, 5]
// 用于函数参数
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6
// 合并数组
const firstHalf = [1, 2, 3];
const secondHalf = [4, 5, 6];
const fullArray = [...firstHalf, ...secondHalf];
console.log(fullArray); // [1, 2, 3, 4, 5, 6]
// 创建数组副本
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4]
// 对象展开 (ES2018)
const obj1 = { x: 1, y: 2 };
const obj2 = { ...obj1, z: 3 };
console.log(obj2); // { x: 1, y: 2, z: 3 }
// 属性覆盖
const defaults = { theme: 'light', fontSize: 14 };
const userSettings = { theme: 'dark' };
const mergedSettings = { ...defaults, ...userSettings };
console.log(mergedSettings); // { theme: 'dark', fontSize: 14 }
1.7 类 (Classes)
JavaScript 面向对象编程的语法糖。
// 基本类声明
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
return `Hello, my name is ${this.name}`;
}
// 获取器
get profile() {
return `${this.name}, ${this.age} years old`;
}
// 设置器
set profile(value) {
[this.name, this.age] = value.split(',');
}
// 静态方法
static createAnonymous() {
return new Person('Anonymous', 0);
}
}
const john = new Person('John', 30);
console.log(john.greet()); // "Hello, my name is John"
console.log(john.profile); // "John, 30 years old"
john.profile = 'Jane,25';
console.log(john.name); // "Jane"
console.log(john.age); // "25"
const anonymous = Person.createAnonymous();
console.log(anonymous.name); // "Anonymous"
// 类继承
class Employee extends Person {
constructor(name, age, company) {
// 调用父类构造函数
super(name, age);
this.company = company;
}
// 覆盖父类方法
greet() {
return `${super.greet()} and I work at ${this.company}`;
}
// 添加新方法
work() {
return `${this.name} is working`;
}
}
const alice = new Employee('Alice', 28, 'Google');
console.log(alice.greet()); // "Hello, my name is Alice and I work at Google"
console.log(alice.work()); // "Alice is working"
1.8 模块 (Modules)
官方的 JavaScript 代码模块化解决方案。
// math.js - 导出模块
// 命名导出
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
export const PI = 3.14159;
// 默认导出
export default class Calculator {
static add(a, b) {
return a + b;
}
}
// app.js - 导入模块
// 导入命名导出
import { add, multiply, PI } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14159
// 导入默认导出
import Calculator from './math.js';
console.log(Calculator.add(2, 3)); // 5
// 导入全部并指定命名空间
import * as math from './math.js';
console.log(math.multiply(2, 3)); // 6
console.log(math.default.add(2, 3)); // 5
// 导入时重命名
import { add as sum, PI as pi } from './math.js';
console.log(sum(2, 3)); // 5
console.log(pi); // 3.14159
// 动态导入 (ES2020)
async function loadMath() {
const math = await import('./math.js');
return math.add(2, 3);
}
1.9 Promise
用于异步操作的对象,代表一个尚未完成但预期将来会完成的操作。
// 创建 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('Operation successful');
} else {
reject(new Error('Operation failed'));
}
}, 1000);
});
// 使用 Promise
promise
.then(result => {
console.log('Success:', result);
return 'Next step';
})
.then(data => {
console.log('Chained then:', data);
})
.catch(error => {
console.error('Error:', error.message);
})
.finally(() => {
console.log('Cleanup code (always runs)');
});
// Promise.all - 等待所有 Promise 完成
Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
])
.then(responses => console.log('All requests succeeded'))
.catch(error => console.error('At least one request failed'));
// Promise.race - 完成或拒绝于第一个完成或拒绝的 Promise
Promise.race([
fetch('/api/fast-endpoint'),
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000))
])
.then(response => console.log('Response received in time'))
.catch(error => console.error('Timed out or failed'));
1.10 生成器 (Generators)
可以暂停和恢复执行的函数。
// 基本生成器函数
function* countUp() {
yield 1;
yield 2;
yield 3;
}
const counter = countUp();
console.log(counter.next()); // { value: 1, done: false }
console.log(counter.next()); // { value: 2, done: false }
console.log(counter.next()); // { value: 3, done: false }
console.log(counter.next()); // { value: undefined, done: true }
// 使用 for...of 遍历
for (const value of countUp()) {
console.log(value); // 1, 2, 3
}
// 接收参数
function* dialogue() {
const response1 = yield 'What is your name?';
const response2 = yield `Hello, ${response1}! How old are you?`;
yield `${response1} is ${response2} years old.`;
}
const conversation = dialogue();
console.log(conversation.next().value); // "What is your name?"
console.log(conversation.next('John').value); // "Hello, John! How old are you?"
console.log(conversation.next('30').value); // "John is 30 years old."
// 无限序列
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
const fib = fibonacci();
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5
2. ES2016 (ES7) 新特性
2.1 指数运算符
// 新的 ** 运算符
console.log(2 ** 3); // 8
// 等价于 Math.pow()
console.log(Math.pow(2, 3)); // 8
// 结合赋值运算符
let num = 3;
num **= 2;
console.log(num); // 9
2.2 Array.prototype.includes()
检查数组是否包含指定元素。
const array = [1, 2, 3, NaN, 4, 5];
// 使用 includes()
console.log(array.includes(3)); // true
console.log(array.includes(6)); // false
// 可以找到 NaN (与 indexOf 不同)
console.log(array.includes(NaN)); // true
console.log(array.indexOf(NaN) !== -1); // false
// 指定起始索引
console.log(array.includes(1, 2)); // false
3. ES2017 (ES8) 新特性
3.1 async/await
异步编程的语法糖,使异步代码更易于阅读和维护。
// 使用 Promise
function fetchUserWithPromise() {
return fetch('/api/users/1')
.then(response => response.json())
.then(user => {
return fetch(`/api/posts?userId=${user.id}`);
})
.then(response => response.json())
.then(posts => {
return { user, posts };
})
.catch(error => {
console.error('Error:', error);
});
}
// 使用 async/await
async function fetchUserWithAsync() {
try {
const userResponse = await fetch('/api/users/1');
const user = await userResponse.json();
const postsResponse = await fetch(`/api/posts?userId=${user.id}`);
const posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error('Error:', error);
}
}
// 并行请求
async function fetchDataParallel() {
try {
const [userResponse, settingsResponse] = await Promise.all([
fetch('/api/users/1'),
fetch('/api/settings')
]);
const user = await userResponse.json();
const settings = await settingsResponse.json();
return { user, settings };
} catch (error) {
console.error('Error:', error);
}
}
// 结合使用 Promise 和 async/await
async function processData() {
const data = await fetchUserWithAsync();
// 做一些处理...
return data;
}
// 异步函数始终返回 Promise
processData().then(result => console.log(result));
3.2 Object 的新方法
用于操作对象的新静态方法。
const person = {
name: 'John',
age: 30,
job: 'developer'
};
// Object.values() - 返回对象的所有值数组
console.log(Object.values(person)); // ["John", 30, "developer"]
// Object.entries() - 返回键值对数组
console.log(Object.entries(person));
// [["name", "John"], ["age", 30], ["job", "developer"]]
// 使用 Object.entries() 进行迭代
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}
// "name: John"
// "age: 30"
// "job: developer"
// Object.entries() 结合 Map
const map = new Map(Object.entries(person));
console.log(map.get('name')); // "John"
// Object.getOwnPropertyDescriptors() - 获取对象的所有属性描述符
const descriptors = Object.getOwnPropertyDescriptors(person);
console.log(descriptors.name);
// { value: "John", writable: true, enumerable: true, configurable: true }
// 用于浅拷贝对象,包括 getter 和 setter
const source = {
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
};
const target = {};
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
3.3 字符串填充方法
// String.prototype.padStart() - 在字符串开头填充
console.log('5'.padStart(2, '0')); // "05"
console.log('JavaScript'.padStart(15, '=')); // "=====JavaScript"
// String.prototype.padEnd() - 在字符串末尾填充
console.log('5'.padEnd(2, '0')); // "50"
console.log('JavaScript'.padEnd(15, '=')); // "JavaScript====="
// 用于对齐输出
const data = [
{ name: 'John', score: 85 },
{ name: 'Jane', score: 92 },
{ name: 'Bob', score: 78 }
];
for (const item of data) {
console.log(`${item.name.padEnd(10, ' ')} ${item.score}`);
}
// "John 85"
// "Jane 92"
// "Bob 78"
3.4 函数参数列表和调用中的尾随逗号
// 函数定义中的尾随逗号
function createPerson(
name,
age,
job, // 这个逗号在 ES2017 中是有效的
) {
return { name, age, job };
}
// 函数调用中的尾随逗号
createPerson(
'John',
30,
'developer', // 这个逗号在 ES2017 中是有效的
);
// 数组和对象字面量中的尾随逗号 (早已支持)
const array = [
1,
2,
3, // 有效的尾随逗号
];
const object = {
a: 1,
b: 2,
c: 3, // 有效的尾随逗号
};
4. ES2018 (ES9) 新特性
4.1 对象的扩展运算符
// 扩展运算符用于对象
const person = { name: 'John', age: 30 };
const personWithJob = { ...person, job: 'developer' };
console.log(personWithJob); // { name: "John", age: 30, job: "developer" }
// 属性覆盖
const updated = { ...person, age: 31 };
console.log(updated); // { name: "John", age: 31 }
// 对象的剩余模式
const { name, ...rest } = person;
console.log(name); // "John"
console.log(rest); // { age: 30 }
// 只针对自身的可枚举属性
const prototype = { inherited: true };
const obj = Object.create(prototype);
obj.own = 'yes';
const clone = { ...obj };
console.log(clone); // { own: "yes" } - 不包含 inherited 属性
4.2 Promise.prototype.finally()
function fetchData() {
showLoadingIndicator();
return fetch('/api/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
updateUI(data);
return data;
})
.catch(error => {
showError(error);
throw error;
})
.finally(() => {
// 无论成功或失败都会执行
hideLoadingIndicator();
});
}
4.3 异步迭代
// 异步迭代器
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
async next() {
if (i < 5) {
// 等待 100ms
await new Promise(resolve => setTimeout(resolve, 100));
return { value: i++, done: false };
}
return { done: true };
}
};
}
};
// for-await-of 循环
async function consumeAsyncIterable() {
for await (const num of asyncIterable) {
console.log(num);
}
}
// 异步生成器
async function* fetchCommits(repo) {
let url = `https://api.github.com/repos/${repo}/commits`;
while (url) {
const response = await fetch(url);
const data = await response.json();
for (const commit of data) {
yield commit;
}
// 获取下一页 URL
url = getNextPageUrl(response.headers);
}
}
// 使用异步生成器
async function showCommits() {
for await (const commit of fetchCommits('javascript/javascript')) {
console.log(commit.sha);
}
}
4.4 正则表达式的新功能
// 命名捕获组 (?<name>...)
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = dateRegex.exec('2022-05-15');
console.log(match.groups); // { year: "2022", month: "05", day: "15" }
console.log(match.groups.year); // "2022"
// 使用解构获取命名组
const { groups: { year, month, day } } = dateRegex.exec('2022-05-15');
console.log(year, month, day); // "2022" "05" "15"
// 在替换字符串中使用命名组
const rewrittenDate = '2022-05-15'.replace(dateRegex, '$<month>/$<day>/$<year>');
console.log(rewrittenDate); // "05/15/2022"
// 后行断言 (?<=...) 和 (?<!...)
// 肯定后行断言
const priceRegex = /(?<=\$)\d+(\.\d+)?/;
console.log(priceRegex.exec('Item costs $10.99')); // ["10.99", ".99"]
// 否定后行断言
const notPriceRegex = /(?<!\$)\d+(?:\.\d+)?/;
console.log(notPriceRegex.exec('Temperature is 23.5 degrees')); // ["23.5", ".5"]
// s 标志 (dotAll 模式)
console.log(/hello.world/.test('hello\nworld')); // false
console.log(/hello.world/s.test('hello\nworld')); // true
// Unicode 属性转义 \p{...} 和 \P{...}
console.log(/\p{Script=Greek}/u.test('π')); // true
console.log(/\p{Letter}/u.test('A')); // true
console.log(/\P{Digit}/u.test('A')); // true
5. ES2019 (ES10) 新特性
5.1 Array.prototype.flat() 和 Array.prototype.flatMap()
// Array.prototype.flat() - 扁平化嵌套数组
const nestedArray = [1, 2, [3, 4, [5, 6]]];
console.log(nestedArray.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nestedArray.flat(2)); // [1, 2, 3, 4, 5, 6]
// 指定深度
console.log(nestedArray.flat(1)); // [1, 2, 3, 4, [5, 6]]
console.log(nestedArray.flat(Infinity)); // [1, 2, 3, 4, 5, 6]
// 移除空洞
const arrayWithHoles = [1, 2, , 4, 5];
console.log(arrayWithHoles.flat()); // [1, 2, 4, 5]
// Array.prototype.flatMap() - 先映射后扁平化
const sentences = ['Hello world', 'JavaScript is fun'];
console.log(sentences.map(s => s.split(' '))); // [["Hello", "world"], ["JavaScript", "is", "fun"]]
console.log(sentences.flatMap(s => s.split(' '))); // ["Hello", "world", "JavaScript", "is", "fun"]
// flatMap 等价于 map().flat(1)
console.log(sentences.map(s => s.split(' ')).flat()); // ["Hello", "world", "JavaScript", "is", "fun"]
5.2 Object.fromEntries()
// 将键值对列表转换为对象
const entries = [
['name', 'John'],
['age', 30],
['job', 'developer']
];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: "John", age: 30, job: "developer" }
// 结合 Object.entries() 使用
const person = { name: 'John', age: 30, job: 'developer' };
const uppercaseKeys = Object.fromEntries(
Object.entries(person).map(([key, value]) => [key.toUpperCase(), value])
);
console.log(uppercaseKeys); // { NAME: "John", AGE: 30, JOB: "developer" }
// 将 Map 转换为对象
const map = new Map([
['name', 'John'],
['age', 30]
]);
const objFromMap = Object.fromEntries(map);
console.log(objFromMap); // { name: "John", age: 30 }
// 将查询字符串转换为对象
const queryString = 'name=John&age=30&job=developer';
const params = Object.fromEntries(
queryString.split('&').map(param => param.split('='))
);
console.log(params); // { name: "John", age: "30", job: "developer" }
5.3 String.prototype.trimStart() 和 String.prototype.trimEnd()
// String.prototype.trimStart() - 移除字符串开头的空白
const greeting = ' Hello world! ';
console.log(greeting.trimStart()); // "Hello world! "
// String.prototype.trimEnd() - 移除字符串末尾的空白
console.log(greeting.trimEnd()); // " Hello world!"
// 等同于 trimLeft 和 trimRight
console.log(greeting.trimLeft()); // "Hello world! "
console.log(greeting.trimRight()); // " Hello world!"
// 移除所有空白
console.log(greeting.trim()); // "Hello world!"
结语
感谢您的阅读!期待您的一键三连!欢迎指正!