在ES6中,模块化成为了JavaScript的标准特性。ES6模块化提供了一种更加优雅和可维护的方式来组织和管理JavaScript代码,可以有效地避免全局变量的污染和命名冲突的问题。以下是ES6模块化的一些主要特性:
export
关键字将一个变量、函数或类导出为一个模块,以便在其他模块中使用。例如,以下代码将一个函数square导出为一个模块:
// module.js
export function square(x) {
return x * x;
}
import
关键字从其他模块中导入一个变量、函数或类,并在当前模块中使用。例如,以下代码从module.js模块中导入square函数并使用它:
// app.js
import { square } from './module.js';
console.log(square(5)); // 25
export default
语法将一个变量、函数或类默认导出为一个模块。例如,以下代码将一个函数add默认导出为一个模块:
// module.js
export default function add(x, y) {
return x + y;
}
import
语法从其他模块中默认导入一个变量、函数或类,并在当前模块中使用。例如,以下代码从module.js模块中默认导入add函数并使用它:
// app.js
import add from './module.js';
console.log(add(2, 3)); // 5
总之,ES6模块化提供了一种更加灵活和可维护的方式来组织和管理JavaScript代码,可以有效地避免全局变量的污染和命名冲突的问题。
ES6引入了class
关键字,提供了一种更加简洁和面向对象的方式来定义和使用对象。class
可以看做是JavaScript中的一种特殊函数,它可以定义一个类并创建类的实例。以下是ES6 class
的一些主要特性:
class
关键字定义一个类。例如,以下代码定义了一个名为Person的类:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
}
}
例如,以下代码定义了一个Person类的构造函数:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
例如,以下代码定义了一个Person类的sayHello方法:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
}
}
例如,以下代码定义了一个Student类,它继承自Person类:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
}
}
class Student extends Person {
constructor(name, age, grade) {
super(name, age);
this.grade = grade;
}
study() {
console.log(`${this.name} is studying in grade ${this.grade}.`);
}
}
例如,以下代码使用super关键字调用Person类的构造函数:
class Student extends Person {
constructor(name, age, grade) {
super(name, age);
this.grade = grade;
}
}
总之,ES6 class
提供了一种更加简洁和面向对象的方式来定义和使用对象,可以大大简化JavaScript代码的编写和维护。同时,ES6 class
还支持继承、多态等面向对象编程的重要特性,使得JavaScript代码更加灵活和可扩展。
ES6 引入了一种新的基本数据类型 Symbol
,它是一种不可变的原始值,可以用作对象属性的唯一标识符。每个 Symbol
值都是唯一的,它们不会重复。Symbol
是一种类似于字符串的数据类型,但是它的值是唯一的,不可变的,且不会被自动转换为其他类型。
可以使用 Symbol()
函数创建一个新的 Symbol
值,它可以接受一个可选的描述字符串作为参数,用于描述这个 Symbol
的用途,但是这个描述字符串不会对 Symbol
的唯一性产生影响。
下面是 Symbol
的一些特性:
Symbol
值可以作为对象的属性名,因为每个 Symbol
值都是唯一的,所以不会产生命名冲突的问题。在对象字面量中,可以使用方括号语法来定义 Symbol
属性。const mySymbol = Symbol();
const obj = {
[mySymbol]: 'value'
};
console.log(obj[mySymbol]); // 输出 'value'
Symbol
值不会被自动转换成字符串,因此不能用作对象属性名的一部分,否则会抛出 TypeError
错误。const mySymbol = Symbol();
const obj = {};
// 正确做法:将 Symbol 作为属性名
obj[mySymbol] = 'value';
// 错误做法:将 Symbol 转换成字符串作为属性名
obj[mySymbol.toString()] = 'value'; // 抛出 TypeError 错误
Symbol
值具有特殊的含义,例如 Symbol.iterator
、Symbol.hasInstance
等,用于指定对象的默认迭代器、自定义 instanceof
行为等。const arr = [1, 2, 3];
const iter = arr[Symbol.iterator]();
console.log(iter.next()); // 输出 { value: 1, done: false }
console.log(iter.next()); // 输出 { value: 2, done: false }
console.log(iter.next()); // 输出 { value: 3, done: false }
console.log(iter.next()); // 输出 { value: undefined, done: true }
Symbol
值可以被用作常量,以避免命名冲突的问题。比如,可以使用一个 Symbol
值作为事件名称,以确保不会和其他事件名称产生冲突。const EVENT_A = Symbol('event_a');
const EVENT_B = Symbol('event_b');
// 发送事件 A
eventEmitter.emit(EVENT_A);
// 监听事件 B
eventEmitter.on(EVENT_B, () => {
// do something
});
综上所述,Symbol
是一种可以用作对象属性名的唯一标识符,可以用于定义常量、指定默认迭代器等。它的出现使得 JavaScript 中对象属性名的命名更加灵活和丰
ES6引入了箭头函数(Arrow Functions)作为一种新的函数定义语法。箭头函数相比于传统的函数定义方式具有一些特殊的语法和行为。
特点如下:
简洁的语法:箭头函数使用箭头(=>
)来定义,省略了function
关键字和函数体中的大括号。如果函数体只有一条语句,则可以省略return关键字并隐式返回该表达式的结果。
// 传统函数定义
function add(a, b) {
return a + b;
}
// 箭头函数定义
const add = (a, b) => a + b;
箭头函数没有自己的this
、arguments
、super
或new.target
。它们继承外层作用域的对应值。这意味着在箭头函数内部,this
指向的是定义函数时的上下文对象,而不是调用时的对象。
const person = {
name: 'John',
sayHello: function () {
setTimeout(() => {
console.log(`Hello, ${this.name}!`);
}, 1000);
}
};
person.sayHello(); // 输出 "Hello, John!"
箭头函数不能作为构造函数使用,不能使用new
关键字实例化对象。箭头函数没有prototype
属性,因此无法使用new
关键字创建对象实例。
const Person = (name) => {
this.name = name; // 错误:箭头函数不能用作构造函数
};
const john = new Person('John'); // 错误:无法使用new关键字实例化对象
如果箭头函数只有一个参数,可以省略参数的括号;如果没有参数或有多个参数,则需要使用括号。
// 一个参数
const greet = name => console.log(`Hello, ${name}!`);
// 没有参数
const sayHello = () => console.log('Hello!');
// 多个参数
const add = (a, b) => a + b;
箭头函数在许多场景中简化了函数的定义,尤其在处理回调函数、处理上下文绑定和简化函数嵌套等方面非常有用。然而,需要注意箭头函数和传统函数之间的差异,特别是在处理this
上下文时的行为。
ES6的解构赋值是一种方便地从数组或对象中提取值并赋给变量的语法。它能够让我们以简洁的方式编写代码,并从复杂的数据结构中快速提取需要的值。
解构赋值可以应用于数组和对象,下面分别详解这两种情况:
数组解构赋值:
数组解构赋值允许我们按照特定的模式从数组中提取值,并将它们赋给变量。模式匹配是基于数组的位置。
const [a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 1
console.log(b); // 2
console.log(rest); // [3, 4, 5]
在上面的例子中,我们使用数组解构赋值将数组 [1, 2, 3, 4, 5]
中的值分别赋给变量 a
、b
和 rest
。a
和 b
分别接收数组的第一个和第二个元素,而 rest
接收剩余的元素组成的新数组。
对象解构赋值:
对象解构赋值允许我们从对象中提取属性值并赋给变量,通过属性名进行匹配。
const person = {
name: 'Alice',
age: 25,
address: {
city: 'New York',
country: 'USA'
}
};
const { name, age, address: { city, country } } = person;
console.log(name); // 'Alice'
console.log(age); // 25
console.log(city); // 'New York'
console.log(country); // 'USA'
在上面的例子中,我们使用对象解构赋值从 person
对象中提取属性 name
、age
、address.city
和 address.country
,并将它们赋给对应的变量。
注意,通过冒号 :
可以为解构赋值的属性指定别名。在上面的例子中,我们将 person.address
的值赋给变量 address
,并进一步从 address
中解构出 city
和 country
。
解构赋值还支持默认值、嵌套结构、忽略某些元素等更多功能,可以根据具体的需求进行灵活运用。它在处理复杂的数据结构时可以提高代码的可读性和编写效率。
ES6引入了rest参数(剩余参数)的概念,它允许我们将不定数量的参数表示为一个数组。在函数声明中,rest参数用三个点(…)后跟一个参数名来表示。这样做可以方便地处理传递给函数的多余参数。
下面是rest参数的一些特点和用法的详细解释:
语法:
function functionName(...rest) {
// 函数体
}
在函数声明中,使用...rest
的形式来定义rest参数。rest
是一个数组,包含了函数调用时传入的所有额外参数。
处理多余参数:
当函数调用时传递了多个参数,而函数声明中只有少数参数定义时,多余的参数将会被捕获到rest参数中。
function sum(...rest) {
let total = 0;
for (let num of rest) {
total += num;
}
return total;
}
console.log(sum(1, 2, 3, 4, 5)); // 15
在上面的例子中,sum
函数的参数列表中只有一个rest参数rest
,但是我们可以传递任意数量的参数给sum
函数,并通过在函数体中遍历rest
数组来计算总和。
rest参数与其他参数结合使用:
在函数声明中,rest参数可以与其他参数共存,但是rest参数必须是最后一个参数。
function foo(a, b, ...rest) {
console.log(a); // 1
console.log(b); // 2
console.log(rest); // [3, 4, 5]
}
foo(1, 2, 3, 4, 5);
在上面的例子中,a
和b
是普通的参数,而rest
是rest参数,它接收了传递给函数的额外参数。
rest参数的应用:
arguments
对象是一个类数组对象,而rest参数是一个真正的数组,可以使用数组的各种方法。总结:ES6的rest参数为我们提供了一种处理不定数量参数的方式,将其收集为一个数组,并在函数体中进行操作。它使函数声明更加灵活,使得处理多余参数变得更加方便和可读。
ES6引入了Set
和Map
这两个新的数据结构,它们提供了更方便和高效的方式来处理数据集合和键值对。
Set:
const set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(2); // 添加重复元素,但不会生效
console.log(set.size); // 3
console.log(set.has(2)); // true
set.delete(3);
console.log(set.size); // 2
set.clear();
console.log(set.size); // 0
Map:
const map = new Map();
const key1 = 'key1';
const key2 = { name: 'John' };
map.set(key1, 'value1');
map.set(key2, 'value2');
console.log(map.size); // 2
console.log(map.get(key1)); // 'value1'
map.delete(key2);
console.log(map.size); // 1
console.log(map.has(key2)); // false
map.clear();
console.log(map.size); // 0
Map还提供了迭代器(Iterator)方法,如keys()
、values()
和entries()
,可以用于遍历Map的键、值和键值对。
const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
for (let key of map.keys()) {
console.log(key); // 'a', 'b', 'c'
}
for (let value of map.values()) {
console.log(value); // 1, 2, 3
}
for (let [key, value] of map.entries()) {
console.log(key, value); // 'a' 1, 'b' 2, 'c' 3
}
Map还可以接受一个数组作为参数来初始化。
const array = [['a', 1], ['b', 2], ['c', 3]];
const map = new Map(array);
console.log(map.get('a')); // 1
Set和Map提供了高效的数据处理方法,适用于需要管理集合和键值对的场景。它们的设计和使用方式使得操作更直观和高效,并且具有更好的性能和扩展性。## let/const
ES6引入了模板字符串(Template Strings),它提供了一种更便捷和灵活的方式来处理字符串。
模板字符串是用反引号(`)包围的字符串,它支持以下特性:
字符串插值(String Interpolation):
使用${}
语法在模板字符串中插入变量或表达式。
const name = 'John';
const age = 30;
const message = `My name is ${name} and I'm ${age} years old.`;
console.log(message); // "My name is John and I'm 30 years old."
在${}
内部可以是任意的JavaScript表达式,可以包含变量、函数调用、运算符等。
多行字符串:
模板字符串可以跨越多行,不需要使用转义字符或拼接操作符。
const multiline = `
This is
a multiline
string.
`;
console.log(multiline);
/*
"This is
a multiline
string."
*/
多行字符串保留了模板字符串中的缩进和换行符,使代码更具可读性。
嵌套模板字符串:
可以在模板字符串中嵌套其他模板字符串,以构建更复杂的字符串。
const name = 'John';
const message = `Welcome to our website, ${name}!
Today's special offer:
Buy 1 get 1 free!
Offer valid until ${new Date().toLocaleDateString()}.`;
console.log(message);
/*
"Welcome to our website, John!
Today's special offer:
Buy 1 get 1 free!
Offer valid until 5/11/2023."
*/
在上述示例中,${name}
嵌套在外部模板字符串中,${new Date().toLocaleDateString()}
嵌套在内部模板字符串中。
原始字符串(Raw Strings):
在模板字符串前添加前缀String.raw
可以创建一个原始字符串,该字符串不会对反斜杠进行转义处理。
const path = String.raw`C:\Users\John\Documents\file.txt`;
console.log(path); // "C:\Users\John\Documents\file.txt"
在上述示例中,反斜杠在原始字符串中保持不变,而不会被转义为特殊字符。
模板字符串提供了更直观和便捷的方式来处理字符串,尤其在需要插入变量或构建复杂文本的情况下。它的使用可以提高代码的可读性和可维护性,并且减少了字符串拼接和转义的繁琐操作。
ES6中的扩展运算符(Spread Operator)是一个三个连续的点(…)用于展开可迭代对象(如数组、字符串或类数组对象)。
扩展运算符有以下主要用途和特性:
数组展开:
扩展运算符可以将一个数组展开为多个独立的元素。
const arr = [1, 2, 3];
console.log(...arr); // 1 2 3
这样可以方便地将一个数组的元素传递给函数或合并多个数组。
字符串展开:
扩展运算符可以将一个字符串展开为单个字符。
const str = 'hello';
console.log(...str); // "h" "e" "l" "l" "o"
这对于需要对字符串进行逐字符处理的操作非常有用。
对象展开:
扩展运算符可以将一个对象展开为多个键值对。
const obj = { x: 1, y: 2 };
console.log({ ...obj }); // { x: 1, y: 2 }
这可以用于复制对象、合并对象或创建新的对象。
函数调用时的参数传递:
扩展运算符可以将一个数组或类数组对象的元素作为函数的参数传递。
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6
这样可以避免使用apply()
或手动解构参数的麻烦。
数组和对象的浅拷贝:
扩展运算符可以用于创建数组和对象的浅拷贝。
const arr = [1, 2, 3];
const arrCopy = [...arr];
const obj = { x: 1, y: 2 };
const objCopy = { ...obj };
这样可以创建原始数组和对象的副本,而不是引用相同的数据。
扩展运算符提供了一种简洁和灵活的方式来处理数组、字符串和对象,使代码更具可读性和可维护性。它在函数调用、数组合并、对象复制等场景下非常有用,并且可以大大简化相关操作的代码。
ES6引入了迭代器(Iterator)和生成器(Generator)两个概念,用于处理可迭代对象和生成可迭代对象的函数。
迭代器(Iterator):
迭代器是一种对象,它提供了一种顺序访问数据集合的方式。它具有一个next()
方法,每次调用都会返回一个包含value
和done
属性的对象。
value
:表示迭代器当前返回的值。done
:表示迭代器是否已经遍历完所有元素,如果为true
则表示迭代器已经结束,否则为false
。迭代器可以手动实现,也可以使用ES6提供的可迭代对象(如数组、字符串、Set、Map等)的内置迭代器。
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
可迭代对象(Iterable):
可迭代对象是具有迭代器的对象,它可以被迭代(遍历)的数据集合。可迭代对象必须具有一个名为Symbol.iterator
的方法,返回一个迭代器对象。
内置的可迭代对象包括数组、字符串、Set、Map等。
const arr = [1, 2, 3];
const str = 'hello';
const set = new Set([1, 2, 3]);
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
console.log(arr[Symbol.iterator]); // [Function: values]
console.log(str[Symbol.iterator]); // [Function: [Symbol.iterator]]
console.log(set[Symbol.iterator]); // [Function: values]
console.log(map[Symbol.iterator]); // [Function: entries]
生成器(Generator):
生成器是一种特殊的函数,它使用function*
关键字定义,并且在函数体内使用yield
语句来暂停函数的执行,并返回一个迭代器对象。
生成器函数可以通过迭代器的方式来逐步产生值,每次调用生成器函数的next()
方法,都会执行到下一个yield
语句,并返回一个包含value
和done
属性的对象。
function* generator() {
yield 1;
yield 2;
yield 3;
}
const iterator = generator();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }