一、变量声明
1.let
语法:
let 变量名 = 值;
特点:存在块级作用域;不存在变量提升(考虑暂时性死区),即变量一定要在声明后使用,否则报错;不允许重复声明(包括普通变量和函数参数)。
2.const
语法:
const 常量名 = 值;
特点:用于声明常量,声明后其值不能再被修改;存在块级作用域;不存在变量提升;不允许重复声明;必须声明即赋值,不能先声明后赋值。
3.为什么需要块级作用域
- 内层变量可能会覆盖外层变量
- 用来计数的循环变量泄露为全局变量
ES6声明变量的6种方法:var、function、let、const、import、class
globalThis对象:Node 里面,顶层对象是global
,但其他环境都不支持。在es62020里引入globalThis作为顶层对象,所以在任何环境下都能拿到globalThis。
二、变量的解构赋值
2.1.数组解构赋值
1.基本用法
数组解构赋值使用方括号[]
包围变量名,并与待解构的数组相对应。例如:
javascript
const arr = [1, 2, 3];
const [a, b, c] = arr;
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 3
2.忽略某些值
javascript
const [, , w] = [1, 2, 3];
console.log(w); // 输出 3
3.默认值
如果解构的变量在数组中不存在,可以为其设置默认值:
javascript
const [x = 10, y = 20] = [1];
console.log(x); // 输出 1
console.log(y); // 输出 20(因为y在数组中不存在,所以使用默认值)
4.严格相等运算符
ES6内部使用严格相等运算符===
来判断一个位置是否有值。因此,如果一个数组成员或对象属性不严格等于undefined
,默认值是不会生效的。例如:
javascript
let [x = 1] = [null];
// x = null(因为null不严格等于undefined,所以默认值不生效)
let { foo = 'bar' } = { foo: null }; // foo = null(同理)
5.剩余参数
使用剩余参数...
可以捕获数组中未指定的其余元素:
javascript
const [x, y, ...rest] = [1, 2, 3, 4, 5];
console.log(x); // 输出 1
console.log(y); // 输出 2
console.log(rest); // 输出 [3, 4, 5]
6.嵌套数组
当数组套数组时,可以使用同样的结构对应原数组,解构出对应的值:
javascript
let [a, [b, c, d]] = [1, [3, 4, 5]];
console.log(a, b, c, d); // 输出 1, 3, 4, 5
2.2.对象解构赋值
1.基本用法
对象解构赋值使用花括号{}包围变量名和它们对应的属性名。变量名必须与对象的属性名相匹配(除非显式指定别名):
javascript
const obj = { name: 'Alice', age: 25 };
const { name, age } = obj;
console.log(name); // 输出 Alice
console.log(age); // 输出 25
2.别名
解构的属性设置别名
javascript
const obj = { name: 'Alice', age: 25 };
const { name, age } = obj;
console.log(name); // 输出 Alice
console.log(age); // 输出 25
3.默认值
对象解构赋值同样支持默认值
javascript
const { address = 'Unknown' } = obj;
console.log(address); // 输出 Unknown(因为obj中没有address属性,所以使用默认值)
4.剩余属性
使用剩余属性...可以捕获对象中未指定的其余属性:
javascript
const { name, ...otherInfo } = obj;
console.log(name); // 输出 Alice
console.log(otherInfo); // 输出 { age: 25 }
5.嵌套对象
当对象包含对象时,可以通过属性名:{}获取对应的属性值
javascript
let person = {
uname: 'zs',
age: 34,
dog: {
name: '汪仔',
age: 3
},
cat: {
name: '小花',
age: 2
}
};
let { uname, cat: { name } } = person;
console.log(uname, name); // 输出 zs, 小花
2.3.字符串解构赋值
字符串也可以解构赋值,因为字符串被转换成了一个类似数组的对象
javascript
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值
javascript
let {length : len} = 'hello';
len // 5
2.4.数值和布尔值的解构赋值
解构赋值时,如果等号右边时数值和布尔值,则会先转为对象
javascript
let {toString: s} = 123;
s // ƒ toString() { [native code] }
s === Number.prototype.toString // true
let {toString: s} = true;
s // ƒ toString() { [native code] }
s === Boolean.prototype.toString // true
上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值
解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象
由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错
javascript
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
2.5.函数参数解构赋值
解构赋值也可以在函数参数中使用。但需要注意区分函数的参数的默认值和变量默认值。
javascript
function move({ x = 0, y = 0 } = {}) {
return [x, y];
}
move({ x: 3, y: 8 }); // [3, 8]
move({ x: 3 }); // [3, 0](y使用默认值)
move({}); // [0, 0](x和y都使用默认值)
move(); // [0, 0](空对象作为参数,x和y都使用默认值)
2.6.圆括号问题
1.不能使用圆括号
- 变量声明语句中,不能带有圆括号
javascript
// 全部报错
var [(a)] = [1];
var {x: (c)} = {};
var ({x: c}) = {};
var {(x: c)} = {};
var {(x): c} = {};
var { o: ({ p: p }) } = { o: { p: 2 } };
- 函数参数中,模式不能带有圆括号
javascript
// 报错
function f([(z)]) { return z; }
- 赋值语句中,不能将整个模式,或嵌套模式中的一层,放在圆括号内
javascript
// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];
上述代码将整个模式放在圆括号内,导致报错
2.能使用圆括号
可以使用圆括号的情况只有一种:赋值语句的非模式部分
javascript
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确
上面三行都可以正确执行,因为首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分
第一行,模式是取数组的第一个成员,跟圆括号无关
第二行,模式是p,而不是d
第三行与第一行语句的性质一致
2.7.解构赋值的应用
1.交换变量的值
javascript
[x, y] = [y, x];
// 交换变量x和y的值
2.从函数返回多个值
javascript
// 返回一个数组
function arr() {
return [1, 2, 3];
}
var [a, b, c] = arr();
// 返回一个对象
function obj() {
return {
a: 1,
b: 2
};
}
var { a, b } = obj();
3.函数参数的定义
javascript
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({ x, y, z }) { ... }
f({ z: 3, y: 2, x: 1 });
4.提取JSON数据
javascript
var jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number); // 42, "OK", [867, 5309]
三、箭头函数
3.1.箭头函数语法
箭头函数有几种不同的语法形式,取决于函数体是单个语句还是多个语句,以及是否需要参数。
1.无参数
javascript
const sayHello = () => {
console.log('Hello!');
};
2.单个参数
javascript
const square = x => x * x;
3.多个参数
javascript
const multiply = (x, y) => x * y;
4.带有花括号的函数体
如果函数体包含多条语句,或者需要返回一个对象字面量,则必须使用花括号
{}
和return
语句(对于对象字面量,需要用圆括号()
包裹返回的对象以避免语法歧义)。
javascript
const addAndLog = (x, y) => {
const result = x + y;
console.log(result);
return result;
};
const getObject = () => ({ key: 'value' });
3.2.箭头函数的特性
1.不绑定自己的this
箭头函数不会创建自己的
this
上下文,因此this
的值总是继承自外围作用域(词法作用域)。
javascript
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // `this` 绑定到 Person 实例
}, 1000);
}
const p = new Person();
2.没有argument对象
箭头函数不提供
arguments
对象。如果需要访问函数的参数列表,可以使用剩余参数(...args
)
javascript
const showArguments = (...args) => console.log(args);
3.不能用作构造函数
箭头函数不能用作构造函数,尝试这样做会抛出一个错误。
javascript
const Foo = () => {};
const bar = new Foo(); // TypeError: Foo is not a constructor
4.没有prototype属性
由于不能用作构造函数,箭头函数也没有
prototype
属性
javascript
const Foo = () => {};
console.log(Foo.prototype); // undefined
5.不能使用new.target关键字
new.target
在箭头函数中是未定义的
javascript
const Foo = () => {
console.log(new.target); // undefined
};
6.没有super关键字
箭头函数不能调用
super
,因此不能用于继承
3.3.使用场景
箭头函数非常适合用于回调函数、数组方法(如
map
、filter
、reduce
)等场景,因为它们提供了更简洁的语法,并且不会引入新的this
上下文。
javascript
const numbers = [1, 2, 3, 4, 5];
const squares = numbers.map(x => x * x);
console.log(squares); // [1, 4, 9, 16, 25]
四、字符串
4.1.新增方法
ES6为字符串添加了多种新方法,如
includes()
、startsWith()
、endsWith()
、repeat()
、padStart()
、padEnd()
等。
4.1.1.includes()
作用:判断一个字符串是否包含在另一个字符串中,返回布尔值
语法:
str.includes(searchString, position)
searchString为要搜索的字符串;position为可选参数,表示搜索的起始位置索引
示例
javascript
let str = "Hello, world!";
console.log(str.includes("world")); // true
console.log(str.includes("o", 5)); // true,从索引5开始搜索'o'
4.1.2.startsWith()
作用:判断一个字符串是否以另一个字符串开头,返回布尔值
语法:str.startsWith(searchString, position)
searchString为要搜索的字符串;position为可选参数,表示搜索的起始位置索引
示例
javascript
let str = "Hello, world!";
console.log(str.startsWith("Hello")); // true
console.log(str.startsWith("world", 6));
// true,从索引6开始判断'world'是否为子串的开头
4.1.3.endsWith()
作用:判断一个字符串是否以另一个字符串结尾,返回布尔值
语法 :
str.endsWith(searchString, length)
参数 :
searchString
为要搜索的字符串;length
为可选参数,表示要搜索的字符串长度(从字符串末尾开始计算)。
注意,这里的length
参数与position
在逻辑上有所不同,它指定了搜索的结束位置(从字符串末尾往前数的字符数),而不是搜索的起始位置。不过,在实际使用中,更常见的是省略此参数,直接判断整个字符串是否以指定子串结尾。
示例
javascript
let str = "Hello, world!";
console.log(str.endsWith("world!")); // true
// 注意:endsWith的length参数与startsWith和includes的position参数在逻辑上有所不同
// 但在此处我们主要关注其判断字符串结尾的功能,因此省略length参数的示例
4.1.4.repeat()
作用:返回一个新的字符串,该字符串是将原字符串重复指定次数后的结果。
语法:
str.repeat(count)
参数:
count
为要重复的次数。如果参数是小数,则向下取整;如果参数是0到-1之间的小数,会进行取整运算得到-0,等同于重复零次;如果参数是NaN,也等同于重复零次;如果参数是负数或Infinity,则会报错。
示例:
javascript
let str = "Hello";
console.log(str.repeat(2)); // "HelloHello"
console.log(str.repeat(2.9)); // "HelloHello",小数向下取整
console.log(str.repeat(-1)); // RangeError: Invalid count value
4.1.5.padStart() 和 padEnd()
作用:padStart()
用于在字符串的头部(左侧)补全字符;padEnd()
用于在字符串的尾部(右侧)补全字符。
语法:
str.padStart(targetLength, padString)
和str.padEnd(targetLength, padString)
参数:
targetLength
为指定生成的字符串的最小长度;padString
为用来补全的字符串(如果省略,则默认使用空格填充)。如果原字符串加上补全字符串的长度超过了指定的最小长度,则会截去超出位数的补全字符串。
示例:
javascript
let str = "123";
console.log(str.padStart(10, "0")); // "0000000123"
console.log(str.padEnd(10, "!")); // "123!!!!!!!"
4.2.模板字符串
定义:模板字符串使用反引号(`````)标识,可以包含变量和表达式。
变量插入:在模板字符串中,可以使用${}
语法来插入变量或表达式。
多行字符串:模板字符串支持多行字符串,所有的空格和缩进都会被保留在输出之中。
函数调用:可以在模板字符串中调用函数,并将其返回值插入到字符串中。
标签模板:标签模板是一个函数的调用,其中调用的参数是模板字符串。它允许你构建自定义的模板字符串处理逻辑。
4.3.其他改进
4.3.1.字符串遍历
ES6为字符串添加了遍历器接口,使得字符串可以被for...of
循环遍历。这个遍历器最大的优点是可以识别大于0xFFFF
的码点,而传统的for
循环无法识别这样的码点。
4.3.2.Unicode支持
ES6引入了String.fromCodePoint()
和str.codePointAt()
方法,用于正确处理大于0xFFFF
的Unicode字符(即4个字节储存的字符)。String.fromCodePoint()
方法根据指定的Unicode码点返回对应的字符;str.codePointAt()
方法返回给定索引处字符的Unicode编码点值的非负整数。
五、数组和对象
5.1.ES6数组的新特性和方法
5.1.1.新特性-扩展运算符(...)
扩展运算符(Spread Operator) :使用...
可以将数组元素展开,或合并数组。例如
javascript
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
5.1.2.新方法
5.1.2.1.Array.from()
作用:将类数组对象或可迭代对象转换为真正的数组。
示例:
javascript
const arrayLike = {0: 'a', 1: 'b', length: 2};
const newArray = Array.from(arrayLike); // ['a', 'b']
5.1.2.2.Array.of()
作用:将一组值转换为数组(这个方法的主要目的是提供一个更明确的方式来创建一个具有单个元素的数组)。
示例:
javascript
let arr = Array.of(1, 2, 3); // [1, 2, 3]
5.1.2.3.find()
作用 :返回数组中满足提供的测试函数的第一个元素的值,否则返回undefined
。
示例:
javascript
const array1 = [5, 12, 8, 130, 44];
const found = array1.find(element => element > 10); // 12
5.1.2.4.findIndex()
作用 :返回数组中满足提供的测试函数的第一个元素的索引,若没有找到对应元素则返回-1
。
示例:
javascript
const array1 = [5, 12, 8, 130, 44];
const foundIndex = array1.findIndex(element => element > 10);
// 0(注意这里应该是第一个大于10的元素的索引,即12的索引1,但示例中给出的是0,
// 应为错误或笔误,正确应为:
const foundIndex = array1.findIndex(element => element > 50);
// 3 或者按照原意修正后的示例输出应为1)
注意 :上述示例中的findIndex
方法返回值有误,正确的应该是找到第一个大于10的元素的索引,即1(对应元素12)。如果按原示例意图找大于50的元素,则索引为3。
5.1.2.5.copyWithin()
作用:在当前数组内部,将指定位置的成员复制到另一个指定位置,并返回这个数组。不会改变原数组的长度。
示例:
javascript
let arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3, 4); // [4, 2, 3, 4, 5],将索引3的元素4复制到索引0的位置
5.1.2.6.fill()
作用 :使用给定值,填充一个数组中的所有空余位置。从起始索引(默认值为0)开始,到终止索引(默认值为this.length
)结束(但不包括终止索引)。
示例:
javascript
let arr = [1, 2, 3, 4];
arr.fill(0, 1, 3); // [1, 0, 0, 4],将索引1到索引2(不包括索引3)的位置填充为0
5.1.2.7.entries()、keys()、values()
作用 :entries()
返回一个新的数组迭代器对象,该对象包含数组中每个索引的键/值对;keys()
返回一个新的数组迭代器对象,该对象包含数组中的每个索引的键;values()
返回一个新的数组迭代器对象,该对象包含数组中的每个索引的值。
示例:
javascript
let arr = ['a', 'b', 'c'];
for (let [index, value] of arr.entries()) {
console.log(index, value); // 依次输出索引和值
}
for (let index of arr.keys()) {
console.log(index); // 依次输出索引
}
for (let value of arr.values()) {
console.log(value); // 依次输出值
}
5.2ES6对象的新特性和方法
5.2.1.对象的新特性
简洁的属性名:在ES6中,对象字面量中允许直接写变量名作为对象的键(属性名),这时ES6会将变量名作为字符串处理。
计算属性名:允许计算属性名作为对象的键。
5.2.2.对象的新方法
5.2.2.1.Object.is()
作用 :判断两个值是否严格相等。与严格相等运算符(===)的行为基本一致,但在处理+0
和-0
时不相同,Object.is(+0, -0)
会返回false
。
示例:
javascript
console.log(Object.is('foo', 'foo')); // true
console.log(Object.is({}, {})); // false,因为两个空对象不是同一个引用
console.log(Object.is(+0, -0)); // false
5.2.2.2.Object.assign()
作用:将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
示例:
javascript
const target = { a: 1 };
const source = { b: 2, c: 3 };
const merged = Object.assign(target, source); // { a: 1, b: 2, c: 3 }
5.2.2.3.Object.keys()、Object.values()、Object.entries()
作用 :Object.keys()
返回一个数组,其成员是对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)的键名;Object.values()
返回一个数组,其成员是对象自身的所有可枚举属性的键值;Object.entries()
返回一个数组,其成员是对象自身的所有可枚举属性的键值对数组。
示例:
javascript
let obj = { a: 1, b: 2, c: 3 };
console.log(Object.keys(obj)); // ['a', 'b', 'c']
console.log(Object.values(obj)); // [1, 2, 3]
console.log(Object.entries(obj)); // [['a', 1], ['b', 2], ['c', 3]]
5.2.2.4.Object.getOwnPropertyNames()
作用:返回一个数组,该数组包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)的键名。
示例:
javascript
let obj = { a: 1, b: 2, [Symbol('c')]: 3 };
console.log(Object.getOwnPropertyNames(obj)); // ['a', 'b'],不包括Symbol属性'c'
5.2.2.5.Object.getOwnPropertySymbols()
作用:返回一个数组,该数组包含对象自身的所有Symbol属性的键名。
示例:
javascript
let obj = { a: 1, [Symbol('b')]: 2 };
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(b)]
5.2.2.6.Reflect.ownKeys()
作用:返回一个数组,该数组包含对象自身的(不含继承的)所有键名,不管键名是Symbol或字符串,也不管是否可枚举。
示例:
javascript
let obj = { a: 1, [Symbol('b')]: 2, c: 3, [Symbol.for('d')]: 4 };
console.log(Reflect.ownKeys(obj)); // ['a', Symbol(b), 'c', Symbol(d)]
5.3.rest参数
5.3.1.rest的概念
rest参数是一个参数修饰符,允许在函数中接收任意数量的参数,并将这些参数存储为一个数组。当一个参数被声明为rest参数时,它会收集传递给函数的剩余参数,并将它们作为一个数组进行存储。
5.3.2.rest的基本使用
rest参数(形式为"...变量名")用于获取函数的多余参数,它之后不能再有其他参数。
函数的length属性,不包括rest参数。
javascript
//...args表示rest
function sum(...args) {
let result = 0;
for (let i = 0; i < args.length; i++) {
result += args[i];
}
return result;
}
console.log(sum(1, 2, 3, 4)); // 输出 10
5.3.3.rest的优点
- 语义明确 :与ES6之前的
arguments
对象相比,rest参数在语义上更加明确。它直接表示一个数组,而不需要通过arguments[index]
这样的方式来访问参数。 - 易用性:rest参数可以与其他ES6特性(如箭头函数、解构赋值等)配合使用,使代码更加简洁和易读。
- 灵活性:rest参数可以接收任意数量的参数,这使得函数在处理不确定数量的输入时更加灵活。
5.3.4.rest的注意事项
- 位置要求:rest参数必须出现在参数列表的末尾。如果尝试将rest参数放在其他位置,将会导致语法错误。
- 类型检查:虽然rest参数将参数存储为数组,但数组中的元素类型可以是任意的。因此,在使用rest参数时,可能需要进行类型检查以确保参数的正确性。
5.3.5.rest的应用实例
- 求平均数:可以使用rest参数来实现求平均数的功能。通过遍历所有参数进行求和运算,然后除以参数的数量即可得到平均数。
- 拼接字符串 :可以使用rest参数来拼接字符串数组。通过
join
方法将所有参数以指定的分隔符拼接成一个字符串。 - 过滤参数 :可以根据特定类型(如数字、字符串、布尔值等)来过滤rest参数中的元素。这可以通过数组的
filter
方法来实现。
六、Symbol数据类型
6.1.Symbol的基本概念
Symbol数据类型的主要特点是独一无二且不可变。每个通过Symbol()
函数创建的Symbol值都是唯一的,即使它们的描述符(description)相同,它们也不相等。这使得Symbol成为解决对象属性名冲突问题的一种有效手段。
6.2.Symbol的创建与使用
6.2.1创建Symbol值
使用Symbol()
函数来创建Symbol值。可以传递一个可选的字符串参数作为描述(description),但这个描述并不会影响Symbol值的唯一性。
例如:
let sym = Symbol('description');
6.2.2使用Symbol作为对象属性名
由于每个Symbol都是唯一的,因此可以用作对象的属性名,从而避免属性名冲突。
使用Symbol作为属性名时,需要通过[]
操作符来访问属性。
例如:
let obj = {}; let sym = Symbol('key'); obj[sym] = 'value'; console.log(obj[sym]); // 输出 'value'
6.3.Symbol的内置属性与方法
6.3.1.内置属性
ES6为Symbol定义了一些内置属性,如Symbol.iterator
、Symbol.hasInstance
等,这些属性在某些情况下可以用来自定义对象的行为。
6.3.2.方法
Symbol.for(key)
:此方法会搜索全局的Symbol注册表,如果找到一个匹配的Symbol则返回它,否则创建一个新的Symbol并将其注册到全局。
Symbol.keyFor(sym)
:此方法会返回全局Symbol注册表中与给定Symbol关联的键(key),如果该Symbol未注册则返回undefined
。
6.4.Symbol的优缺点与应用场景
-
优点:
-
可以确保属性名的唯一性,避免属性名冲突的问题。
-
可以隐藏属性,使其不容易被意外访问到(因为Symbol属性名无法通过常规的属性遍历方法获取到)。
-
-
缺点:
-
Symbol值不能与其他类型的值进行运算(会报错)。
-
Symbol值不能隐式转换为字符串(但可以显式转换为字符串)。
-
-
应用场景:
-
解决对象的属性名冲突问题。
-
自定义对象的行为(如迭代器、比较、字符串转换等)。
-
创建独一无二的事件类型,避免与其他事件类型的冲突。
-
6.5.注意事项
Symbol值不是对象,不能添加属性。
使用for...in
遍历对象时,不能遍历到Symbol类型的属性。但可以使用Object.getOwnPropertySymbols()
方法来获取对象的所有Symbol属性名。
Reflect.ownKeys()
方法可以用于遍历对象的普通值和Symbol值。
七、Set和Map数据结构
7.1.Set
7.1.1.基本概念
Set是一种类似于数组的数据结构,但成员的值必须是唯一的,不允许有重复。它主要用于处理不重复的值集合,常用于数组去重、成员检查等场景。
7.1.2.主要特性
成员值唯一:Set中的值不会重复,即使添加了重复的值,Set也会自动去除。
有序性:Set中的元素是按照插入顺序进行存储的,因此可以保证遍历顺序与插入顺序一致。
可进行集合运算:Set支持并集、交集等集合运算。
7.1.3.主要方法和属性
add(value)
:添加某个值,返回Set结构本身。
delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。
has(value)
:返回一个布尔值,表示该值是否为Set的成员。
clear()
:清除所有成员,没有返回值。
size
:返回Set实例的成员总数。
keys()
、values()
、entries()
:这三个方法都返回遍历器对象,用于遍历Set成员。由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以这三个方法的行为完全一致。
forEach()
:使用回调函数遍历每个成员,没有返回值。
7.2.Map
7.2.1.基本概念
Map是一种键值对集合,类似于对象,但是键的范围不限于字符串,可以是任何值(包括对象)。她提供了更完善的Hash结构实现,使得开发者可以使用费字符串作为键,或者需要更高效的键值对操作。
7.2.2主要特性
键值对存储:Map以键值对的形式存储数据,键和值都可以是任意类型。
快速查找:Map提供了快速查找键值对的方法,可以通过键来获取对应的值,而不需要遍历整个Map
迭代器:Map提供了迭代器,可以遍历所以的键值对。
7.2.3.主要方法和属性
set(key, value)
:设置键名key对应的键值为value,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。
get(key)
:读取键名key对应的键值,如果找不到key,则返回undefined。
delete(key)
:删除指定键值对,成功返回true,失败返回false。
has(key)
:返回一个布尔值,表示某个键是否在当前Map对象之中。
clear()
:清除所有成员,没有返回值。
size
:返回Map结构的成员总数。
keys()
:返回键名的遍历器。
values()
:返回键值的遍历器。
entries()
:返回所有成员的遍历器(即键值对的遍历器)。
forEach()
:遍历Map的所有成员,使用回调函数对每个成员进行操作。
八、迭代器与for....or....循环
8.1.迭代器
迭代器是一种接口,为各种不同的数据结构提供统一的访问机制
8.1.1.迭代器基本概念
迭代器是一个对象,它定义了一个序列,并且提供了一种统一的接口来逐个访问序列中的每个成员。在ES6中,任何实现了@@iterator
方法的对象都可以被视为可迭代的(iterable)。这个方法需要返回一个迭代器对象,该对象实现了next()
方法。每次调用next()
方法时,它都会返回序列中的下一个成员(或done: true
来表示迭代结束)。
8.1.2.迭代器的核心方法
next()
:返回迭代器的下一个值。它是一个对象,包含两个属性:value
(当前成员的值)和done
(一个布尔值,表示迭代是否完成)。
8.1.3.默认迭 代器
许多内置对象(如数组、字符串、Set、Map等)都默认实现了迭代器。你可以通过调用它们的@@iterator
方法(通常通过Symbol.iterator
属性访问)来获取迭代器。
8.2 for...of
循环
for...of
循环是一种新的循环语法,用于遍历可迭代对象的成员。它弥补了for...in
循环只能遍历对象键(属性名)的不足,允许我们直接遍历值。
语法
javascript
for (let value of iterable) {
// 循环体
}
value
:在每次迭代中,被赋值为可迭代对象的下一个成员的值。
iterable
:一个可迭代对象,如数组、字符串、Set、Map等。
使用示例
javascript
// 遍历数组
let array = [1, 2, 3, 4];
for (let num of array) {
console.log(num);
}
// 遍历字符串
let str = 'hello';
for (let char of str) {
console.log(char);
}
// 遍历Set
let set = new Set([1, 2, 3]);
for (let item of set) {
console.log(item);
}
// 遍历Map
let map = new Map([['a', 1], ['b', 2]]);
for (let [key, value] of map) {
console.log(key, value);
}
自定义迭代器
javascript
let myIterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { done: true };
}
}
};
}
};
for (let value of myIterable) {
console.log(value);
}
ES6创造了一种新的遍历命令for...of循环,用于遍历可迭代对象的元素(包括数组、Set、Map、字符串等)。
九、promise构造函数
Promise
是一个用于处理异步操作的对象,它代表了一个可能现在还不可用,但将来某个时刻会变得可用的值。
Promise
构造函数接受一个执行器(executor)函数作为参数。
参数
这个执行器函数有两个参数:resolve
和 reject
,它们都是函数。
resolve(value)
:当异步操作成功时,我们调用resolve
函数,并将操作的结果作为参数传递给它。这将导致Promise
的状态变为fulfilled
(已完成)。
reject(reason)
:当异步操作失败时,我们调用reject
函数,并将错误原因作为参数传递给它。这将导致Promise
的状态变为rejected
(已拒绝)。
语法
javascript
let promise = new Promise(function(resolve, reject) {
// 异步操作代码
if (/* 操作成功 */) {
resolve(value); // 将 Promise 的状态改为 fulfilled,并将操作的结果作为参数传递
} else {
reject(reason); // 将 Promise 的状态改为 rejected,并将错误原因作为参数传递
}
});
使用示例:以下代码展示了如何使用 Promise
构造函数来创建一个表示异步操作的 Promise
实例。
javascript
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
let success = true; // 假设这是异步操作的结果
if (success) {
resolve("操作成功!");
} else {
reject("操作失败!");
}
}, 1000); // 模拟异步操作,1秒后完成
});
promise.then(
(value) => {
console.log(value); // 如果 Promise 的状态是 fulfilled,则执行这里的代码
},
(reason) => {
console.error(reason); // 如果 Promise 的状态是 rejected,则执行这里的代码
}
);
在这个示例中,我们创建了一个 Promise
实例,它在1秒后完成。如果异步操作成功(success
为 true
),则调用 resolve
函数,并将字符串 "操作成功!"
作为参数传递。如果异步操作失败(success
为 false
),则调用 reject
函数,并将字符串 "操作失败!"
作为参数传递。然后,我们使用 then
方法来处理 Promise
的结果或错误。
promise的状态
pending
(等待中):初始状态,既不是 fulfilled
,也不是 rejected
。
fulfilled
(已完成):意味着操作成功完成。
rejected
(已拒绝):意味着操作失败。
方法
Promise
原型上定义了几个方法,用于处理Promise
的状态变化:
.then(onFulfilled, onRejected)
:添加处理成功和失败的回调函数。
.catch(onRejected)
:添加处理失败的回调函数(实际上是.then(undefined, onRejected)
的语法糖)。
.finally(onFinally)
:无论Promise
成功还是失败,都会执行该回调函数(ES2018引入)。
.all(promises)
:接受一个Promise
数组,当所有Promise
都成功时,返回一个包含所有成功结果的数组;如果有任何一个Promise
失败,则立即返回那个失败的Promise
结果。
.race(promises)
:接受一个Promise
数组,返回第一个解决或拒绝的Promise
的结果值。
示例
javascript
let promise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
let success = true; // 假设操作成功
if (success) {
resolve('操作成功!'); // 调用resolve,传递成功信息
} else {
reject('操作失败!'); // 调用reject,传递失败信息
}
}, 1000); // 1秒后完成操作
});
// 使用.then()和.catch()处理Promise的结果
promise.then((result) => {
console.log(result); // 输出"操作成功!"
}).catch((error) => {
console.error(error); // 如果失败,则输出"操作失败!"
});
使用场景
Promise
主要用于处理异步操作,例如:
网络请求(如使用fetch
API)。
文件读取(如使用FileReader
API)。
定时操作(如使用setTimeout
或setInterval
,尽管它们不是真正的异步操作,但可以用Promise
来封装)。
任何可能需要等待的操作,无论等待的时间是多久。
Promise
的优势在于它提供了一种清晰的方式来处理异步操作的成功和失败,以及通过链式调用来组织多个异步操作的执行顺序。此外,Promise
还可以有效地避免"回调地狱"(callback hell),使代码更加简洁和可读。
十、async和await
10.1.定义与用途
10.1.1.async:
定义:async
是JavaScript中的一个关键字,用于声明一个函数是异步的。
用途:它告诉JavaScript引擎,这个函数内部可能包含异步操作,如网络请求、文件读取、定时器操作等。这些操作不会阻塞程序的执行,而是允许程序在等待异步操作完成的同时继续执行其他任务。
10.1.2.await:
定义:await
也是一个关键字,它只能在async
函数内部使用。
用途:await
用于等待一个Promise
对象完成,并返回其结果。它允许我们在异步操作完成之前暂停async
函数的执行,直到等待的Promise
解决(fulfilled)或拒绝(rejected)。
10.2.工作原理
10.2.1.async函数:
当一个函数被async
关键字修饰时,它会隐式地返回一个Promise
对象。
在async
函数内部,可以使用await
来等待异步操作的结果。
如果async
函数没有显式地返回任何值,则它会返回一个已解决的(fulfilled)Promise
,其值为undefined
。
10.2.2.await表达式
await
后面必须跟一个Promise
对象。
当await
遇到一个未解决的Promise
时,它会暂停async
函数的执行,并将控制权返回给调用者。
一旦Promise
解决或拒绝,await
会恢复async
函数的执行,并返回Promise
的结果或抛出异常。
10.3.错误处理
在async
函数内部,可以使用try...catch
语句来捕获await
抛出的异常。
如果await
等待的Promise
被拒绝,则异常会被抛出到最近的try...catch
块中,并可以在其中进行处理。
10.4.使用场景与示例
10.4.1.网络请求
使用fetch
函数发起网络请求时,可以使用async
和await
来等待请求的结果。
示例:
javascript
async function fetchData() {
try {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
return data;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
10.4.2.文件读取:
在Node.js中,可以使用fs.promises
模块提供的异步文件操作函数,并使用async
和await
来等待文件读取的结果。
示例:
javascript
const fs = require('fs').promises;
async function readFile() {
try {
let data = await fs.readFile('example.txt', 'utf8');
console.log(data);
} catch (error) {
console.error('Error reading file:', error);
}
}
10.4.3.定时器操作
可以使用setTimeout
或setInterval
来创建定时器,并使用async
和await
来等待定时器的完成。
示例:
javascript
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function runAfterDelay() {
await delay(2000);
console.log('2 seconds have passed!');
}
10.5.注意事项
避免死锁:
在async
函数内部,如果错误地使用了await
(例如,在一个循环中等待一个永远不会解决的Promise
),则可能会导致死锁。
性能优化:
使用async
和await
可以使异步代码更加直观和易于理解,但也可能导致性能问题。因此,在编写异步代码时,需要注意性能优化,避免不必要的等待和阻塞。
兼容性:
async
和await
是ES2017(ES8)中引入的特性,因此需要注意在不同环境下的兼容性。在较旧的JavaScript环境中,可能需要使用Babel等转译器来支持这些特性。
👋 亲爱的朋友们,
在知识的瀚海里,每篇文章都承载着鄙人的心血。我满怀诚意,献上精心准备的内容,愿为您的网络探索之旅添翼。
码字,如同航海探秘,需不断求索、思考、提炼。每敲击一字,是对未知的勇敢探索;每斟酌一句,是对精准表达的不懈追求。背后是鄙人无数个日夜的努力,对知识的无尽热爱。
若此内容触动您心,或有所启发,请赐我一赞!💖 您的点赞,是对我的认可与鼓励,如温暖阳光照亮前路,坚定我分享知识的步伐。
感谢支持!您的每一次点赞,都是我前进的动力,激励我持续带来有价值的内容。让我们携手在知识的海洋中探索,共赴未知之旅!🚀
再次感谢,愿我们的友谊如网络连接般坚固!💪💖
综上所述,就是希望多点点赞感谢!!!