深入理解ES6及ES6新特性-----变量声明,箭头函数,模板字符串,解构赋值,扩展运算符,类和模块,promise对象
ES6(ECMAScript 2015)是JavaScript的重要更新,引入了诸多新特性:1.变量声明方式优化,使用let和const替代var;2.箭头函数简化语法,解决this指向问题;3.模板字符串支持多行文本和表达式嵌入;4.解构赋值方便提取数组和对象值;5.扩展运算符简化数组和对象操作;6.类语法支持面向对象编程;7.模块化实现代码组织和复用;8.Promise和async/await
一、ES6简介
1. 什么是ES6
ES6,全称ECMAScript 6,是JavaScript语言的第六个标准版本,于2015年6月正式发布,也被称为ECMAScript 2015。ES6引入了许多新特性,使JavaScript更加强大和灵活,极大地提升了开发效率和代码质量。
2. 与之前版本的主要区别
ES6相比ES5引入了大量新特性,主要包括:
- 新的变量声明方式(let、const)
- 箭头函数
- 模板字符串
- 解构赋值
- 扩展运算符
- 类和模块
- Promise对象
- 等等
二、ES6新特性
1. 变量声明
let和const
ES6引入了let和const两个新的变量声明关键字,用于替代var。
let声明
let声明的变量只在其声明的块级作用域内有效- 不存在变量提升
- 暂时性死区:在声明前使用会报错
- 不允许重复声明
// 块级作用域示例
{
let a = 10;
var b = 1;
}
console.log(b); // 1
console.log(a); // ReferenceError: a is not defined
const声明
const声明一个只读的常量,一旦声明,常量的值就不能改变- 与
let一样具有块级作用域 - 必须在声明时初始化
const PI = 3.1415926;
PI = 3.14; // TypeError: Assignment to constant variable
注意:const声明的对象或数组,其内部属性是可以修改的
const obj = { name: 'Alice' };
obj.name = 'Bob'; // 可以修改
obj = {}; // TypeError: Assignment to constant variable
var、let、const比较
|
特性 |
var |
let |
const |
|
作用域 |
函数作用域 |
块级作用域 |
块级作用域 |
|
变量提升 |
是 |
否 |
否 |
|
重复声明 |
允许 |
不允许 |
不允许 |
|
初始化要求 |
非必须 |
非必须 |
必须 |
|
可修改性 |
可修改 |
可修改 |
不可修改(对象内部属性可修改) |
2. 箭头函数
(1)箭头函数比普通函数更加简洁
- 如果没有参数,就直接写一个空括号即可
- 如果只有一个参数,可以省去参数的括号
- 如果有多个参数,用逗号分割
- 如果函数体的返回值只有一句,可以省略大括号
- 如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。最常见的就是调用一个函数:
let fn = () => void doesNotReturn();
(2)箭头函数没有自己的this
箭头函数不会创建自己的this, 所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中this的指向在它在定义时已经确定了,之后不会改变。
而且由于它没有prototype,也没有自己的this指向,更不可以使用arguments参数,所以不能New一个箭头函数。
(4)call()、apply()、bind()等方法不能改变箭头函数中this的指向
var id = 'Global';
let fun1 = () => {
console.log(this.id)
};
fun1(); // 'Global'
fun1.call({id: 'Obj'}); // 'Global'
fun1.apply({id: 'Obj'}); // 'Global'
fun1.bind({id: 'Obj'})(); // 'Global'
(5)箭头函数不能作为构造函数使用
不能使用new,没有prototype属性
构造函数在new的步骤在上面已经说过了,实际上第二步就是将函数中的this指向该对象。 但是由于箭头函数时没有自己的this的,且this指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。
(6)箭头函数没有自己的arguments
箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。
可以使用剩余参数代替:
const sum = (...args) => args.reduce((total, num) => total + num, 0);
console.log(sum(1, 2, 3, 4)); // 10
3. 模板字符串
(1)基本语法
模板字符串使用反引号(`)包裹,可以包含多行文本和嵌入表达式。
// 基本用法
const name = 'Alice';
const greeting = `Hello, ${name}!`; //1.字符串插值
console.log(greeting); // Hello, Alice!
// 2. 支持直接换行,无需使用\n
const multiLine = `这是第一行
这是第二行
这是第三行`;
console.log(multiLine);
/*
这是第一行
这是第二行
这是第三行
*/
(2)特点
- 字符串插值:使用
${expression}在字符串中嵌入表达式 - 多行字符串:支持直接换行,无需使用\n
- 标签模板:可以与函数结合使用,进行更复杂的字符串处理
// 字符串插值中可以包含任何有效的JavaScript表达式
const a = 5;
const b = 10;
console.log(`a + b = ${a + b}`); // a + b = 15
//3. 可以与函数结合使用
console.log(`今天是${new Date().toLocaleDateString()}`); // 今天是2023/5/20
// 标签模板示例
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<strong>${values[i]}</strong>` : '');
}, '');
}
const name = 'Alice';
const age = 25;
const result = highlight`我叫${name},今年${age}岁。`;
console.log(result); // 我叫<strong>Alice</strong>,今年<strong>25</strong>岁。
(3)与传统字符串拼接的比较
// 传统字符串拼接
const name = 'Alice';
const age = 25;
const oldWay = '我叫' + name + ',今年' + age + '岁。';
// 模板字符串
const newWay = `我叫${name},今年${age}岁。`;
模板字符串的优势:
- 更加直观和可读
- 减少了字符串拼接的错误
- 支持多行文本
- 可以嵌入复杂表达式
4. 解构赋值
(1)数组解构
数组解构允许我们从数组中提取值,按照位置对变量赋值。
// 基本用法
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// 忽略某些值
const [a, , c] = [1, 2, 3];
console.log(a, c); // 1 3
// 剩余元素
const [a, ...rest] = [1, 2, 3, 4, 5];
console.log(a, rest); // 1 [2, 3, 4, 5]
// 默认值
const [a = 1, b = 2, c = 3] = [4, 5];
console.log(a, b, c); // 4 5 3
// 交换变量
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1
(2)对象解构
对象解构允许我们从对象中提取值,按照属性名对变量赋值。
// 基本用法
const { name, age } = { name: 'Alice', age: 25 };
console.log(name, age); // Alice 25
// 给变量取别名
const { name: userName, age: userAge } = { name: 'Alice', age: 25 };
console.log(userName, userAge); // Alice 25
// 默认值
const { name = 'Anonymous', age = 0 } = { name: 'Alice' };
console.log(name, age); // Alice 0
// 嵌套解构
const { address: { city } } = { address: { city: 'Beijing' } };
console.log(city); // Beijing
// 结合剩余运算符
const { name, ...rest } = { name: 'Alice', age: 25, gender: 'female' };
console.log(name, rest); // Alice { age: 25, gender: 'female' }
(3)函数参数解构
解构赋值也可以用于函数参数,使函数调用更加灵活。
// 对象参数解构
function printUser({ name, age = 18 }) {
console.log(`${name} is ${age} years old`);
}
printUser({ name: 'Alice', age: 25 }); // Alice is 25 years old
printUser({ name: 'Bob' }); // Bob is 18 years old
// 数组参数解构
function sum([a, b]) {
return a + b;
}
console.log(sum([1, 2])); // 3
(4)解构赋值的应用场景
- 交换变量值:无需使用临时变量
- 函数返回多个值:可以直接解构接收
- 提取JSON数据:快速获取需要的属性
- 函数参数默认值:结合解构提供更灵活的默认值
- 模块导入:选择性导入模块中的特定功能
// 函数返回多个值
function getUser() {
return { name: 'Alice', age: 25, gender: 'female' };
}
const { name, age } = getUser();
// 导入模块特定功能
import { useState, useEffect } from 'react';
5. 扩展运算符
(1)数组的扩展运算符
扩展运算符(...)可以将一个数组转为用逗号分隔的参数序列。
// 复制数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1];
console.log(arr2); // [1, 2, 3]
// 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [...arr1, ...arr2];
console.log(arr3); // [1, 2, 3, 4]
// 将字符串转为数组
const str = 'hello';
const chars = [...str];
console.log(chars); // ['h', 'e', 'l', 'l', 'o']
// 结合解构赋值
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first, rest); // 1 [2, 3, 4, 5]
(2)对象的扩展运算符
ES2018引入了对象的扩展运算符,用于取出对象的所有可遍历属性,拷贝到当前对象中。
// 复制对象
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1 };
console.log(obj2); // { a: 1, b: 2 }
// 合并对象
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const obj3 = { ...obj1, ...obj2, c: 3 };
console.log(obj3); // { a: 1, b: 2, c: 3 }
// 属性覆盖
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, b: 3 };
console.log(obj2); // { a: 1, b: 3 }
(3)函数调用中的扩展运算符
扩展运算符可以替代Function.prototype.apply的使用。
// 传统方式
function sum(x, y, z) {
return x + y + z;
}
const args = [1, 2, 3];
console.log(sum.apply(null, args)); // 6
// 使用扩展运算符
console.log(sum(...args)); // 6
// 构造数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [0, ...arr1, ...arr2, 5];
console.log(arr3); // [0, 1, 2, 3, 4, 5]
(4)剩余参数
剩余参数(Rest parameters)也使用...语法,但作用与扩展运算符相反,用于将多个参数收集为一个数组。
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
function printInfo(name, ...details) {
console.log(name);
console.log(details);
}
printInfo('Alice', 25, 'female', 'Beijing');
// Alice
// [25, 'female', 'Beijing']
6. 对象与类
(1)对象字面量增强
ES6对对象字面量进行了增强,使其更加简洁和灵活。
// 属性简写
const name = 'Alice';
const age = 25;
const user = { name, age }; // 等同于 { name: name, age: age }
console.log(user); // { name: 'Alice', age: 25 }
// 方法简写
const obj = {
sayHi() { // 等同于 sayHi: function() {
console.log('Hi!');
}
};
obj.sayHi(); // Hi!
// 计算属性名
const key = 'name';
const user = {
[key]: 'Alice',
[`user_${key}`]: 'Bob'
};
console.log(user); // { name: 'Alice', user_name: 'Bob' }
(2)类的定义与使用
ES6引入了类(Class)的概念,作为对象的模板。通过class关键字,可以定义类。
// 类的基本语法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
get info() {
return `${this.name}, ${this.age} years old`;
}
set info(value) {
[this.name, this.age] = value.split(',');
this.age = parseInt(this.age);
}
static isAdult(age) {
return age >= 18;
}
}
const alice = new Person('Alice', 25);
alice.sayHello(); // Hello, my name is Alice
console.log(alice.info); // Alice, 25 years old
alice.info = 'Bob,30';
console.log(alice.name); // Bob
console.log(alice.age); // 30
console.log(Person.isAdult(20)); // true
(3)类的继承
ES6的类支持通过extends关键字实现继承。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类的constructor
this.breed = breed;
}
speak() {
console.log(`${this.name} barks.`);
}
getBreed() {
console.log(`${this.name} is a ${this.breed}.`);
}
}
const dog = new Dog('Rex', 'German Shepherd');
dog.speak(); // Rex barks.
dog.getBreed(); // Rex is a German Shepherd.
(4)类与传统构造函数的比较
类实际上是构造函数的语法糖,但提供了更清晰、更面向对象的语法。
// 传统构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
};
Person.isAdult = function(age) {
return age >= 18;
};
// ES6类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
static isAdult(age) {
return age >= 18;
}
}
7. 模块化
(1)模块的导出与导入
ES6模块化允许JavaScript程序分割成可按需导入的单独模块。
导出(export)
// 命名导出
export const name = 'Alice';
export function sayHello() {
console.log('Hello!');
}
// 或者集中导出
const name = 'Alice';
function sayHello() {
console.log('Hello!');
}
export { name, sayHello };
// 导出时重命名
export { name as userName, sayHello as greet };
// 默认导出(每个模块只能有一个默认导出)
export default function() {
console.log('Default export');
}
导入(import)
// 导入命名导出
import { name, sayHello } from './module.js';
// 导入时重命名
import { name as userName, sayHello as greet } from './module.js';
// 导入所有导出并绑定到一个对象
import * as module from './module.js';
console.log(module.name);
module.sayHello();
// 导入默认导出
import defaultFunction from './module.js';
// 同时导入默认导出和命名导出
import defaultFunction, { name, sayHello } from './module.js';
(2)动态导入
ES2020引入了动态导入功能,使用import()函数按需加载模块。
button.addEventListener('click', async () => {
const module = await import('./module.js');
module.default(); // 使用默认导出
module.sayHello(); // 使用命名导出
});
(3)模块化的优势
- 命名空间隔离:避免全局变量污染
- 代码重用:模块可以在不同项目中重用
- 依赖管理:明确模块之间的依赖关系
- 按需加载:可以动态导入模块,提高性能
8. Promise与异步编程
(1)Promise基础
Promise是异步编程的一种解决方案,比传统的回调函数更加优雅。
// 创建Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('操作成功');
} else {
reject('操作失败');
}
}, 1000);
});
// 使用Promise
promise
.then(result => {
console.log(result); // 操作成功
})
.catch(error => {
console.error(error); // 操作失败
})
.finally(() => {
console.log('无论成功还是失败,都会执行');
});
(2)Promise链式调用
Promise可以链式调用,避免回调地狱。
// 回调地狱
getUser(userId, function(user) {
getOrders(user.id, function(orders) {
getOrderDetails(orders[0].id, function(details) {
// 处理订单详情
});
});
});
// Promise链式调用
getUser(userId)
.then(user => getOrders(user.id))
.then(orders => getOrderDetails(orders[0].id))
.then(details => {
// 处理订单详情
})
.catch(error => {
// 统一处理错误
});
(3)Promise.all和Promise.race
Promise提供了一些静态方法来处理多个Promise。
// Promise.all:所有Promise都成功才成功,有一个失败就失败
const promises = [
fetch('/api/users'),
fetch('/api/orders'),
fetch('/api/products')
];
Promise.all(promises)
.then(([users, orders, products]) => {
// 所有请求都成功
})
.catch(error => {
// 至少有一个请求失败
});
// Promise.race:返回最先完成的Promise的结果
const promise1 = new Promise(resolve => setTimeout(() => resolve('一秒后完成'), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('两秒后完成'), 2000));
Promise.race([promise1, promise2])
.then(result => console.log(result)) // 一秒后完成
.catch(error => console.error(error));
(4)async/await
ES2017引入了async/await语法,使异步代码看起来像同步代码,更加直观。
// 使用async/await
async function fetchUserData(userId) {
try {
const user = await getUser(userId);
const orders = await getOrders(user.id);
const details = await getOrderDetails(orders[0].id);
return details;
} catch (error) {
console.error('获取用户数据失败:', error);
}
}
// 调用异步函数
fetchUserData(123).then(details => {
console.log(details);
});
// 并行执行多个异步操作
async function fetchAllData() {
try {
const [users, orders, products] = await Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/orders').then(res => res.json()),
fetch('/api/products').then(res => res.json())
]);
return { users, orders, products };
} catch (error) {
console.error('获取数据失败:', error);
}
}
更多推荐



所有评论(0)