ES特性
- ES
- ES6
- 循环体中不能使用var关键字
- const对于对象和数组来说,不能修改引用,但是可以修改值
- 箭头函数
- 形参结构
- ...rest参数
- ...扩展运算符
- Symbol不能和其他数据进行运算和比较
- for...of工作原理
- 生成器函数
- Promise
new Promise
中的回调函数会立即执行,不会因为调用resolve或reject而终止后面代码执行- Promise.then()方法可以只指定一个resolve回调函数,如果没有reject回调函数,而promise中又调用了reject函数则会报错
- Promise读取文件内容
- promise异步行为
- promise.then()返回值
- 1.then方法始终返回一个新的Promise对象,无论执行resolve还是reject,都会返回一个新的Promise对象
- 2.then的回调函数如果没有return Promise类型对象,那么自动返回Promise对象,其的PromiseState状态为fulfilled,PromiseResult为then回调函数的返回值,如果then无返回值,那么返回的Promise对象的PromiseResult为undefined,如果then有返回值,那么返回的Promise对象的PromiseResult为then回调函数的返回值
- 3.then的回调函数如果没有return Promise类型对象,那么自动返回Promise对象,其状态根据then回调函数中有误错误决定,如果回调函数中无错误,那么返回的promise状态为fulfilled,如果回调函数中有错误,那么返回的promise状态为rejected,并把错误内容作为PromiseResult的值
- 4.then的回调函数如果返回的是Promise类型对象,那么返回的promise对象的状态和PromiseResult由返回的Promise对象决定
- 5.then方法可以链式调用
- promise.catch()方法
- Set
- 覆盖父类方法
- 数值扩展
- 对象方法扩展
- 模块化
- ES8
- ES9
- ES10
- ES11
ES
ECMA: European Computer Manufacturers Association 欧洲计算机制造商协会
ECMAScript: 是由ecma通过ecma-262标准化的脚本语言规范
ES6
循环体中不能使用var关键字
如下代码var i为全局变量,当循环体结束时i的值变为3,而elements没有脚标3,因此undefined
for (var i = 0; i < 3; i++) {
elements[i].addEventListener('click', function () {
console.log(elements[i]); // undefined
})
}
const对于对象和数组来说,不能修改引用,但是可以修改值
const a = 2;
// a = 3; // 报错: Assignment to constant variable.
// console.log(a);
const b = { name: '张三' };
b.name = '李四';
console.log(b); // { name: '李四' }
const arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [1, 2, 3, 4]
箭头函数
不可改变箭头函数中的this值
箭头函数没有自己的this,它会捕获箭头函数外层作用域中的this
普通函数可以通过call和apply改变this的值,但是箭头函数中的this值不能改变
箭头函数不能作为构造函数使用
let Person = function (name) {
this.name = '张三';
}
console.log(new Person('张三'));
let Person2 = (name) => {
this.name = '李四';
}
console.log(new Person2('李四'));
箭头函数中没有arguments对象
定时器回调函数中的this
定时器(如 setTimeout 和 setInterval)的回调函数中的 this 默认指向 window 对象
由于定时器的回调函数无法指定自定义调用者,默认情况下,其中的this就是window对象
document.querySelector('.div').onclick = function () {
console.log(this); // 事件中的this指向触发事件的元素对象
setTimeout(function () {
console.log(this); // setTimeout中的this指向window对象
}, 1000);
}
在定时器中使用箭头函数操作事件源对象
因此我们可以通过定时器使箭头函数作为回调函数,从而解决定时器中的this指向window对象的问题
我们就可以触发事件事件源进行操作,如设置样式等
document.querySelector('.div').onclick = function () {
console.log(this); // 事件中的this指向触发事件的元素对象 div对象
setTimeout(() => {
console.log(this); // 箭头函数中的this指向其外层作用域的this,即事件回调中的this,就是触发事件的元素对象
}, 1000);
}
箭头函数适合场景
箭头函数的使用适合和this无关的场景,比如定时器回调函数、数组回调函数、对象方法等
对象方法
const obj = {
name: '张三',
getName: () => {
console.log(this); // window对象
console.log(this.name); // undefined
}
}
形参结构
function fn({name='张三', age'}){
console.log(name);
};
fn({age: 18});
...rest参数
形参使用...rest
参数: 将实参剩余部分收集到一个数组中
而且rest参数必须放在形参的最后一个位置
function fn(a, b, ...args){
console.log(args); //args是一个数组,
};
...扩展运算符
let arr = [1, 2, 3];
let arr2 = [...arr, 4, 5]; // [1, 2, 3, 4, 5]
实现原理:
将实现了Iterator 接口的对象中的每个元素一个个迭代取出单独使用
Symbol不能和其他数据进行运算和比较
let s = Symbol('a');
let s2 = Symbol('a');
console.log(s + 'a'); // 报错
console.log(s > 'a'); // 报错
console.log(s === s2); // false
symbol的作用
1.安全的为对象添加方法或属性
假如有一个复杂的对象,我并不知道里面有什么属性,假如我想添加一个name属性,使用obj.name = '李四',那么就破坏了对象的原来的name属性
let obj = {name: '张三'};
obj.name = '李四'; // 破坏了obj对象原有的name属性
使用symbol添加唯一的属性,不会破坏原有的属性
let obj = {name: '张三'};
let methods = {
name: Symbol(),
age: Symbol()
}
obj[methods.name] = '李四';
obj[methods.age] = 18;
console.log(obj); // { name: '张三', [Symbol()]: '李四', [Symbol()]: 18 }
// 使用
console.log(obj[methods.name]); // 李四
console.log(obj[methods.age]); // 18
Symbol内置属性
Symbol内置属性可以扩展对象方法的功能
Symbol.hasInstance: 用于判断某对象是否为某构造函数的实例
相当于在类中重写了instanceof的实现,在使用x instanceof 类
时,会调用类中的[Symbol.hasInstance]
方法
class Person {
static [Symbol.hasInstance](param) {
console.log(param);
return true;
}
}
let obj = { name: '张三' }
console.log(obj instanceof Person); // 输出: { name: '张三' } true
Symbol.isConcatSpreadable: 用于判断对象在使用concat方法时,是否展开对象
let arr = [1, 2, 3];
let arr2 = [4, 5, 6];
let arr3 = [7, 8, 9];
arr3[Symbol.isConcatSpreadable] = false;
let arr4 = arr.concat(arr2, arr3);
console.log(arr4); // [1, 2, 3, 4, 5, 6, [7, 8, 9]]
for...of工作原理
for...of循环用于遍历可迭代对象(如数组、字符串、Map、Set等)
Array.prototype[Symbol.iterator]()
: 返回一个数组迭代器对象,通过迭代器对象获取数组的元素
for...of循环的工作原理如下:
1.调用可迭代对象的Symbol.iterator方法,返回该对象的迭代器对象
2.使用迭代器对象的next方法,获取下一个迭代项
3.将迭代项的值赋给循环变量,并执行循环体
4.重复步骤2和3,直到迭代器对象的next方法返回的迭代项的done属性为true
let arr = [1, 2, 3];
let 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 }
根据for...of循环的工作原理,我们可以自定义可迭代对象
通常情况下我们使用for...in循环来遍历对象的属性名,然后通过obj[item]获取属性值
我们可以自定义迭代器对象遍历对象中的属性值
1.在对象中创建一个[Symbol.iterator]方法,返回一个迭代器对象
2.该迭代器对象中包含一个next方法
3.next方法中返回一个对象,对象中包含一个value属性和一个done属性
自定义的对象迭代器后,使用for...of时,对象就会自动调用[Symbol.iterator]方法,
let obj = {
name: '张三',
age: 18,
gender: '男',
arr: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
let keys = Object.keys(this);
return {
next: () => {
if (index < keys.length) {
let key = keys[index++];
return {
value: this[key],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
}
for (const element of obj) {
console.log(element);
}
生成器函数
生成器函数是一种特殊的函数,它可以暂停执行并返回一个值,然后在需要时恢复执行
生成器函数使用function*
定义,函数体内部使用yield
语句定义一个或多个暂停点
调用生成器函数会返回一个带迭代器的对象,可调用该迭代器对象的next方法
function* gen() {
console.log(111);
yield 1;
console.log(222);
yield 2;
console.log(333);
yield 3;
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
在调用生成器函数时不会执行生成器函数中的代码,只有调用next方法时才会执行生成器函数中的代码
next()方法
:
1.每次调用生成器函数的next方法,就会执行生成器函数中yield之前的语句和yield之后的表达式语句,并返回一个对象
2.对象中包含一个value属性和一个done属性
3.value值是yield语句后面的表达式的值,done属性表示生成器函数是否执行完毕
next执行片段
注意:
yield语句后面表达式最后会自动添加分号,作为本次分段函数结尾,并不会执行下一个yield前的语句
第一个next只输出111和yield1
function* gen() {
console.log(111);
console.log(1111);
yield console.log('yield1')
console.log(222);
}
let iterator = gen();
iterator.next();
执行对应次数的yield之前的语句和yield之后当前行的表达式
生成器函数传参
生成器函数返回的迭代器对象
第一次调用next(传参无效)方式时,即使传入了参数,生成器函数也不会接受到参数,而是将调用生成器函数时的参数作为实参
如果调用生成器函数gen()时没有传参,那么第一次next()也不会传参
在调用next方式时才会执行gen的代码逻辑,并且以调用gen时的实参作为第一次调用next方式的参数,因此第一次next即使没有传参,但在调用gen时
传入了AAA,因此next()的参数就是AAA
第二次next时,才会把传入的参数作为第一个yield语句的返回值,同时next也会返回一个对象,该对象value值就是第二个yield后面表达式的值
第三次next时,才会把传入的参数作为第二个yield语句的返回值,同时next也会返回一个对象,该对象value值就是第三个yield后面表达式的值
function* gen(args) {
console.log('执行了生成器函数', args);
let one = yield 1;
console.log('第一个yield返回的值是:' + one);
let two = yield 2;
console.log('第二个yield返回的值是:' + one);
let three = yield 3;
console.log('第三个yield返回的值是:' + one);
}
let iterator = gen('AAA');
iterator.next(); // 输出: 执行了生成器函数 AAA
let nextTwoResult = iterator.next('BBB'); // 输出: 第一个yield返回的值是:BBB
console.log(nextTwoResult); // 输出: { value: 2, done: false }
let nextThreeReuslt = iterator.next('CCC'); // 输出: 第二个yield返回的值是:CCC
console.log(nextThreeReuslt); // 输出: { value: 3, done: false }
let nextFourResult = iterator.next('DDD'); // 输出: 第三个yield返回的值是:CCC
console.log(nextFourResult); // 输出: { value: undefined, done: true }
总结
1.在调用生成器函数时,不会执行生成器中代码逻辑,但会返回一个迭代器对象
2.调用迭代器对象的next方法,才会执行生成器函数中对应的代码逻辑,并返回一个对象,该对象中value值是yield语句后面的表达式的值,done属性表示生成器函数是否执行完毕
3.调用next会执行对应次数的yield语句前面的代码和yeild后面当前行的语句
4.第一次next传参无效,并将调用生成器函数时的参数,作为第一次其迭代器对象调用next方法时的参数
5.从第二次迭代器调用开始,第n次调用next传入的参数做为n - 1次yield语句的返回值,并且yeild语句前可使用变量接收
6.生成器函数可以指定某段特定函数执行,并且可以暂停执行,等待调用next方法时才会继续执行
使用场景
解决回调地狱问题
需求: 异步实现,1秒后输出1,而后2s后输出2,而后3s后输出3
通过setTimeout定时实现,如果嵌套很深就会出现回调地狱
setTimeout(() => {
console.log(1)
setTimeout(() => {
console.log(2)
setTimeout(() => {
console.log(3)
}, 3000)
}, 2000)
}, 1000)
通过生成器函数异步执行对应的任务,解决回调地狱问题
function one() {
setTimeout(() => {
console.log(1);
g.next();
}, 1000)
}
function two() {
setTimeout(() => {
console.log(2);
g.next();
}, 2000)
}
function three() {
setTimeout(() => {
console.log(3);
g.next();
}, 3000)
}
function* gen() {
yield one();
yield two();
yield three(); b
}
let g = gen();
g.next();
由于g已经初始化了,因此在任务函数中可以使用g.next()方法,执行第一个任务one,而后在one中执行g.next()方法,执行第二个任务two,以此类推
异步解决数据依赖
需求: 异步获取用户数据,当获取到数据后,再根据数据获取其他数据,再根据其他数据获取其他数据,以此类推
function getuser() {
console.log('获取用户信息');
setTimeout(() => {
user = {
userName: '张三'
}
g.next(user);
}, 1000)
}
function getorder(user) {
console.log('获取订单信息入参', user);
setTimeout(() => {
let order = {
orderId: '123456'
}
g.next(order);
}, 1000)
}
function getprouct(order) {
console.log('获取商品信息入参', order);
setTimeout(() => {
let product = {
productId: '7890'
}
g.next(product);
}, 1000)
}
function* gen() {
let user = yield getuser();
let order = yield getorder(user);
let product = yield getprouct(order);
return product;
}
let g = gen();
g.next();
Promise
Promise是一个构造函数,是用于处理异步操作的对象,Promise对象代表了一个异步操作,该操作有三种状态:
1.pending: 初始状态,不是成功或失败状态
2.fulfilled: 意味着操作成功完成
3.rejected: 意味着操作失败
创建一个Promise对象,入参为一个函数,该函数有两个参数resolve函数和reject函数,在调用resolve方法时会改变Promise对象的状态为fulfilled,调用reject方方时会改变Promise对象的状态为rejected
创建Promise语法: let promise = new Promise(fn(resolve, reject))
异步执行resolveFn或rejectFn: promise.then(resolveFn, rejectFn)
当执行resolve(data)时,promise状态变为fulfilled,其PromiseResult属性值为调用回调函数时入参,并且执行then方法中第一个函数,并将data作为参数传入
也就是说resolve变量指向then方法中第一个函数,reject指向then方法中第二个函数
let promise = new Promise((resolve, reject) => {
// 请求接口获取数据
let data = "接口返回数据";
resolve(data);
// reject('接口请求失败');
});
console.log(promise); // 输出: Promise {<fulfilled>: '接口返回数据'}
promise.then((value) => {
console.log(value); // 输出: 接口返回数据
}, (reason) => {
console.log(reason); // 输出: 接口请求失败
});
resolve(入参)决定了promise的状态为fulfilled,reject(入参)决定了promise的状态为rejected,其PromiseResult的值为调用回调函数的入参
new Promise
中的回调函数会立即执行,不会因为调用resolve或reject而终止后面代码执行
new Promise(fn(resolve, reject)): new Promise就会执行fn代码中的代码,不会因为调用resolve或reject而终止后面代码执行
log(123)仍会执行
let promise = new Promise((resolve, reject) => {
reject('err');
console.log(123);
});
Promise.then()方法可以只指定一个resolve回调函数,如果没有reject回调函数,而promise中又调用了reject函数则会报错
then方法中可以只有一个resolve回调函数
promise.then(res => {})
let promise = new Promise((resolve, reject) => {
reject('错误内容'); // Uncaught (in promise) 错误内容
});
promise.then((value) => {
});
指定了一个reject回调函数,则不会报错
let promise = new Promise((resolve, reject) => {
reject('错误内容');
});
promise.then((value) => {
}, (reason) => {
console.log(reason); // 输出: 错误内容
});
Promise读取文件内容
安装node环境,js文件中可直接导入fs模块使用
普通方式:
import fs from 'fs';
fs.readFile('../static/test.txt', 'utf-8', (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
})
文件内容:
执行js,输出内容
➜ ES git:(master) ✗ node 13_promise_读取文件内容.js
这是txt文本中内容
promise方式:
import fs from 'fs';
let promise = new Promise((resolve, reject) => {
fs.readFile('../static/test.txt', 'utf-8', (err, data) => {
if (err) {
reject(err);
}
resolve(data);
})
})
promise.then(data => {
console.log(data);
}).catch(err => {
console.log(err);
})
➜ ES git:(master) ✗ node 13_promise_promise方式读取文件内容.js
这是txt文本中内容
promise异步行为
js是单线程运行,和java相比并不是真正意义上的异步执行,java中可以开启线程执行一段逻辑,该逻辑不会阻塞主线程执行
而js的异步更像是触发一个事件,指定回调函数执行的时机
let promise = new Promise((resolve, reject) => {
console.log('执行promise逻辑');
// 阻塞5s
let currentTime = Date.now();
while(Date.now() - currentTime < 5000) {}
resolve('接口返回数据');
});
function after() {
console.log('执行后续代码');
}
promise.then((value) => {
console.log(value);
});
after();
在promise中阻塞,那么后续的代码after函数仍然不会先执行,而是等待promise执行结束后继续
promise.then()返回值
promise.then()返回值为一个新的Promise对象
1.then方法始终返回一个新的Promise对象,无论执行resolve还是reject,都会返回一个新的Promise对象
let promise = new Promise((resolve, reject) => {
let data = "接口返回数据";
// resolve(data);
let err = "接口返回错误";
reject(err);
});
let p = promise.then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
return 123;
});
console.log(p === promise); // false
2.then的回调函数如果没有return Promise类型对象,那么自动返回Promise对象,其的PromiseState状态为fulfilled,PromiseResult为then回调函数的返回值,如果then无返回值,那么返回的Promise对象的PromiseResult为undefined,如果then有返回值,那么返回的Promise对象的PromiseResult为then回调函数的返回值
let promise = new Promise((resolve, reject) => {
let data = "接口返回数据";
reject('err');
});
let p = promise.then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
});
console.log(p);
then回调函数有返回值,但不是Promise对象,那么then返回的Promise对象中PromiseResult属性值为then回调函数的返回值
let promise = new Promise((resolve, reject) => {
let data = "接口返回数据";
reject("err");
});
let p = promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
return 'then回调返回内容'
}
);
console.log(p);
then回调函数有return值,且不是promise对象,那么then方法返回的promise对象中PromiseResult属性值就为then回调函数的返回值
3.then的回调函数如果没有return Promise类型对象,那么自动返回Promise对象,其状态根据then回调函数中有误错误决定,如果回调函数中无错误,那么返回的promise状态为fulfilled,如果回调函数中有错误,那么返回的promise状态为rejected,并把错误内容作为PromiseResult的值
let promise = new Promise((resolve, reject) => {
reject('错误内容');
});
let p = promise.then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
throw new Error('错误');
});
console.log(p);
then回调函数没有返回Promise类型对象,并且其中抛出错误,因此then返回的新的promise对象的状态为rejected,并把错误内容作为PromiseResult的值
4.then的回调函数如果返回的是Promise类型对象,那么返回的promise对象的状态和PromiseResult由返回的Promise对象决定
let promise = new Promise((resolve, reject) => {
reject('错误内容');
});
let p = promise.then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
return new Promise((resolve, reject) => {
resolve('成功');
});
});
console.log(p);
回调函数返回了Promise类型对象,因此then方法就会返回该对象,并且p对象中的状态受回调函数返回的Promise对象状态决定
由于返回的Promise对象中调用了resolve方法,因此new Promise
的状态为fullfilled,PromiseResult为resolve的入参'成功'
5.then方法可以链式调用
let promise = new Promise((resolve, reject) => {
console.log('逻辑1');
resolve();
});
let p = promise.then((value) => {
return new Promise((resolve, reject) => {
console.log('逻辑2');
resolve();
});
}).then((value) => {
console.log('逻辑3');
return new Promise((resolve, reject) => { })
});
promise.catch()方法
catch()方法用于指定Promise对象失败的回调函数
then的第二个函数和catch()方法作用一样,如果Promise指定的回调函数出错或执行reject回调函数,都会执行then的第二个函数和promise.catch()方法
let promise = new Promise((resolve, reject) => {
reject('错误内容');
});
promise.then(() => {
}, (reason) => {
console.log(reason); // 输出: 错误内容
});
promise.catch((reason) => {
console.log(reason); // 输出: 错误内容
});
reject后,then第二个函数和catch将都会执行
Set
交集
var arr1 = [1, 2, 3, 4, 5];
var arr2 = [4, 5, 6, 7, 8];
let result = [...new Set(arr1)].filter(item => new Set(arr2).has(item));
console.log(result); // [4, 5]
并集
var arr1 = [1, 2, 3, 4, 5];
var arr2 = [4, 5, 6, 7, 8];
let result = [...new Set([...arr1, ...arr2])];
console.log(result); // [1, 2, 3, 4, 5, 6, 7, 8]
差集
求arr1数组与arr2数组之差
var arr1 = [1, 2, 3, 4, 5];
var arr2 = [4, 5, 6, 7, 8];
let result = [...new Set(arr1)].filter(item => !new Set(arr2).has(item));
console.log(result); // [1, 2, 3]
覆盖父类方法
子类可以覆盖父类方法,使用super.methodName()调用父类方法
class Person {
say() {
console.log('父类say方法');
}
}
class Student extends Person {
say() {
super.say();
console.log('子类say方法');
}
}
new Student().say(); // 输出: 父类say方法 子类say方法
数值扩展
Number.EPSILON
解决小数判断不准确问题
Number.EPSILON是js中一个极小的数,用于浮点数计算精度问题
console.log(0.1 + 0.2 === 0.3); // 输出: false
// 使用最小精度来比较两个小数是否相等
function equal(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(equal(0.1 + 0.2, 0.3)); // 输出: true
各个进制字面量
// 二进制、八进制、十进制、十六进制字面量语法
console.log(0b1011); // 输出: 11
console.log(0o13); // 输出: 11
console.log(11); // 输出: 11
console.log(0xB); // 输出: 11
Number.isFinite()判断是否为有限数值
console.log(Number.isFinite(Infinity)); // 输出: false
console.log(Number.isFinite(0)); // 输出: true
console.log(Number.isFinite(100/0)); // 输出: false
parseInt()和parseFloat()方法
parseInt()和parseFloat()方法用于将字符串转换为数值,如果转换失败,则返回NaN
console.log(parseInt('123abc')); // 输出: 123
console.log(parseFloat('123.45abc')); // 输出: 123.45
console.log(parseInt('a123bc')); // 输出: NaN
console.log(parseFloat('.1abc')); // 输出: 0.1
Number.isInteger()方法
Number.isInteger()方法用于判断一个数值是否为整数,返回一个布尔值
console.log(Number.isInteger(0.2)); // 输出: false
console.log(Number.isInteger('0.2')); // 输出: false
对象方法扩展
Object.is()方法用于判断两个值是否相等,返回一个布尔值
console.log(Object.is(0, -0)); // 输出: false
console.log(0 === -0); // 输出: true
console.log(Object.is(NaN, NaN)); // 输出: true
console.log(NaN === NaN); // 输出: false
console.log(Object.is(1, 1)); // 输出: true
console.log(Object.is('a', 'a')); // 输出: true
let obj = { name: 1 };
let obj2 = { name: 1 };
console.log(Object.is(obj, obj2)); // 输出: false
Object.is()和===的区别:
- is(): NaN和NaN相等,====: NaN和NaN不相等
- is(): +0和-0不相等,====: +0和-0相等
模块化
分别暴露
export let name = '张三';
export let obj = {
name: '李四',
age: 18
};
export function say() {
console.log('hello');
}
统一暴露
let name = '张三';
let obj = {
name: '李四',
age: 18
};
export function say() {
console.log('hello');
}
export { name, obj };
默认暴露
export default {
name: '张三',
age: 18,
say() {
console.log('hello');
}
}
通用导入模块方式
<script type="module">
// 分别暴露
import * as module from './js/分别暴露.js';
console.log(module);
// 统一暴露
import * as module2 from './js/统一暴露.js';
console.log(module2.say()); // 输出: hello
// 默认暴露
import * as module3 from './js/默认暴露.js';
console.log(module3.default.say()); // 输出: hello
</script>
解构赋值导入模块方法
<script type="module">
import { name, obj, say } from './js/分别暴露.js';
console.log(name, obj, say());
</script>
引入模块指定数据使用别名
如果引入多个模块中具有相同变量名时,执行时,会报错Identifier 'name' has already been declared
,此时可以别名
{变量 as 别名}
<script type="module">
import { name, obj } from './js/分别暴露.js';
import { name as name1, obj as obj1 } from './js/统一暴露.js'; // Identifier 'name' has already been declared
console.log(name, name1); // 输出: 张三 张三
</script>
默认暴露使用别名
{default as 别名}
: 将default对象命名为别名
<script type="module">
import { default as default1 } from './js/默认暴露.js';
console.log(default1.name); // 输出: 张三
</script>
简便形式导入模块方法
简便形式只能用于默认暴露的导入,不能用于分别暴露和统一暴露
import 模块名 from '模块路径'
<script type="module">
import module from './js/默认暴露.js';
console.log(module.name); // 输出: 张三
</script>
业务逻辑单独抽离出一个js文件,页面再引入该js文件,由于app.js使用了模块化,所以需要使用模块化导入,type="module"
把业务逻辑抽离到app.js
import * as m1 from './统一暴露.js';
import * as m2 from './默认暴露.js';
import * as m3 from './分别暴露.js';
// 业务逻辑
console.log(m1);
console.log(m2);
console.log(m3);
// 业务逻辑
页面中引入app.js,加载页面后立即执行app.js中的代码
<body>
<script src="./js/app.js" type="module"></script>
</body>
小结
1.分别暴露和统一暴露可以同时使用,但是默认暴露只能使用一次
2.默认暴露其实就是以default命名的对象类型
3.使用模块时需要指定script标签的type属性为module
4.使用通用导入模块形式时,需要加default,module.default.数据
5.简易形式导入模块可以直接引入模块名,不需要加default,如: module.数据
ES8
async函数
async function 声明创建一个绑定到给定名称的新异步函数。函数体内允许使用 await 关键字,这使得我们可以更简洁地编写基于 promise 的异步代码,并且避免了显式地配置 promise 链的需要
async关键字标记的函数为异步函数,总是返回一个promise对象。
1.如果该异步函数返回值不是promise对象,那么返回的promise对象的状态为fullfilled,PromiseResult的值返回值为该异步函数的返回值
async function fn() {
return 'hello';
}
let result = fn();
console.log(result); // 输出: Promise {<fulfilled>: 'hello'}
2.如果异步函数中出错,则会返回一个失败的promise,状态为reject,PromiseResult的值为出错信息
3.如果返回值是一个promise对象,则返回的promise对象的状态和值与返回的promise对象一致
async function fn() {
return new Promise((resolve, reject) => {
reject('error');
});
}
let result = fn();
console.log(result); // 输出: Promise {<rejected>: 'error'}
async
和new Promise
区别
new Promise
的返回promise状态受函数体执行的结果决定,不受函数是否返回promise对象的影响,因此new Promise函数中返回的值无效
async函数返回的promise对象状态受async函数体执行结果影响,同时也受返回值影响
let promise = new Promise((resolve, reject) => {
resolve('成功内容');
return new Promise((resolve, reject) => {
throw new Error('错误内容');
});
});
console.log(promise); // 输出: Promise {<resolved>: '成功内容'}
await关键字
1.await关键字必须写在async函数中才能生效
2.一般情况下await后面是一个promise对象
3.await后面promise执行完成后如果返回的promise状态为成功会把promise对象中的result值返回给await的左侧变量
成功:
let promise = new Promise((resolve, reject) => {
resolve('用户数据');
});
async function fn() {
let result = await promise;
console.log(result); // 输出: 用户数据
}
fn();
4.如果返回的promise状态为失败,会抛出异常,需要通过try...catch捕获异常,catch中的参数值为promise对象中result的值
失败:
let promise = new Promise((resolve, reject) => {
reject('错误信息');
});
async function fn() {
try {
let result = await promise;
console.log(result);
} catch (e) {
console.log(e); // 输出: 错误信息
}
}
fn();
5.await修饰的语句也会阻塞线程,等待await后面的逻辑执行完毕,才会执行后续代码
let promise = new Promise((resolve, reject) => {
let currentTime = Date.now();
console.log('睡眠5s');
while(Date.now() - currentTime < 5000){}
resolve('成功');
})
async function fn() {
let p1 = await promise;
console.log(1);
}
fn();
async和await异步特点
let promise = new Promise((resolve, reject) => {
// 睡眠5s
console.log("promise开始睡眠");
let date = new Date();
while (new Date() - date < 5000) {}
console.log("promise睡眠结束");
console.log("promise开始执行");
// 请求api
let data = { code: 200, msg: "请求成功", data: {} };
if (data.code === 200) {
resolve(data);
} else {
reject(data.msg);
}
console.log("promise结束执行");
});
async function getData() {
console.log("getDate执行");
console.log("promise状态:", promise);
console.log("getData开始睡眠");
// 睡眠5s
let date = new Date();
while (new Date() - date < 5000) {}
console.log("getData睡眠结束");
// 一旦遇到await关键字当前函数就会异步等待promise状态,在等待过程中,当前函数返回新的promise对象,外层代码将继续执行,直到promise状态改变,当前函数才会继续执行
let result = await promise;
console.log("异步-promise状态:", promise);
console.log('异步-', result); // 输出: { code: 200, msg: "请求成功", data: {} }
}
function doOther() {
console.log("doOther执行");
}
console.log(getData()); // 输出: Promise { <pending> }
doOther();
async和await的作用
await作用
: 直接将异步函数返回的promise对象中的结果赋值给左侧变量
async作用
: 标记为异步函数将异步函数转为同步函数,在执行async函数时,函数体如果有await关键字,则需要等待await的结果,而后续执行下一行代码
标记为async函数意味着函数内部有异步操作,遇到await时,该函数立即返回一个promise对象,继续执行外层逻辑,等待await后的promise状态改变时,则继续执行该异步函数中await后面的逻辑
let promise = new Promise((resolve, reject) => {
// 请求api
let data = { code: 200, msg: "请求成功", data: {} };
if (data.code === 200) {
resolve(data);
} else {
reject(data.msg);
}
});
// 通过then方法获取promise的结果
let p = promise.then((res) => {
// res: api返回的数据
console.log(res); // 输出: { code: 200, msg: "请求成功", data: {} }
});
async function getData() {
// 通过await获取promise的结果
let result = await promise;
console.log(result); // 输出: { code: 200, msg: "请求成功", data: {} }
}
console.log(getData()); // 输出: Promise { <pending> }
then回调函数和await可同时使用来获取promise结果
对象方法扩展
Object.entries(): 返回一个数组,数组的元素是当前对象的可枚举属性的键值对数组。
let obj = {
name: '张三',
age: 18
[Symbol(1)]: 1
}
console.log(Object.entries(obj)); // [ [ 'name', '张三' ], [ 'age', 18 ] ]
对象转map
let obj = {
name: '张三',
age: 18
}
let map = new Map(Object.entries(obj));
console.log(map); // Map(2) { 'name' => '张三', 'age' => 18 }
ES9
ES6已经引入了扩展运算符,但只适用于数组,在ES9中引入了扩展运算符,用于扩展数组或对象
...扩展运算符
rest参数: 将多个元素或属性值,转为一个数组或一个对象
function fn([a, b, ...c]) {
console.log(a, b, c);
}
fn([1, 2, 3, 4]); // 输出: 1 2 [3, 4]
function fnObj({ name, age, ...c }) {
console.log(name, age, c);
}
fnObj({ name: '张三', age: 18, gender: '男', hobby: '篮球' }); // 输出: 张三 18 { gender: '男', hobby: '篮球' }
...展开元素或对象属性
let arr = [1, 2, 3];
let arr1 = [...arr, 4, 5, 6];
console.log(arr1); // 输出: [1, 2, 3, 4, 5, 6]
let obj = { name: '张三', age: 18 };
let obj1 = { ...obj, gender: '男', hobby: '篮球' };
console.log(obj1); // 输出: { name: '张三', age: 18, gender: '男', hobby: '篮球' }
正则表达式命名命名捕获分组
常规提取字符串正则表达式:
let a = '<a href="https://www.baidu.com">百度</a>';
let reg = /<a href="(.*)">(.*)<\/a>/;
let res = reg.exec(a);
console.log(res);
console.log(res[1]);
console.log(res[2]);
命名捕获:
let a = '<a href="https://www.baidu.com">百度</a>';
let reg = /<a href="(?<url>.*)">(?<name>.*)<\/a>/;
let res = reg.exec(a);
console.log(res);
console.log(res.groups.url);
console.log(res.groups.name);
正则表达式dotAll模式
dotAll: .可以匹配任意字符,包括换行符
let str = `
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
`;
// 提取li标签中文字
let result;
let reg = /<li>(.*?)<\/li>\s/gs;
while (result = reg.exec(str)) {
console.log(result);
}
ES10
Object.fromEntries()
将一个键值对数组或可迭代对象转换为对象
它和Object.entries()是相反的操作,Object.entries()是将对象转换为键值对数组
let arr = [
['name', '张三'],
['age', 18]
];
let obj = Object.fromEntries(arr);
console.log(obj); // 输出: { name: '张三', age: 18 }
数组扩展方法flat()和flatMap()
flat(深度): 创建一个新的数组,并根据指定深度递归地将所有子数组元素拼接到新的数组中
深度: 默认值为1,表示只扁平化到第2层,如果值为Infinity,表示扁平化所有层
let arr = [1, 2, [3, 4, [5, 6, [7, 8]]]];
let arr1 = arr.flat(2); // 扁平化到第3层
console.log(arr1); // 输出: [1, 2, 3, 4, 5, 6, [7, 8]]
flatMap(): 先对数组中的每个元素执行一个函数,然后对结果进行flat()操作
将函数所有返回结果进行flat()操作
let arr = [1, 2, 3, 4];
let arr1 = arr.flatMap(item => [item, item * 2]);
console.log(arr1); // 输出: [1, 2, 2, 4, 3, 6, 4, 8]
Symbol.prototype.description
获取Symbol的描述
let s = Symbol('张三');
console.log(s.description); // 输出: 张三
ES11
可选链操作符
可选链操作符: 变量?.属性名
作用: 当属性不存在时,不会报错,而是返回undefined
通常情况下获取对象中不存在的属性会返回undefined
再获取不存在属性的属性时,会报错
在可能不存在的属性后添加?
,那么再获取该属性的属性时也不会报错,而是返回undefined
let obj = {
name: "张三",
};
console.log(obj.age); // undefined
console.log(obj.age?.name); // undefined
console.log(obj.age.name); // 报错: Uncaught TypeError: Cannot read properties of undefined (reading 'name')
动态导入模块
静态导入: 无论后续逻辑中有没有使用模块,模块都会被导入,影响页面加载速度
动态导入模块: import()
,返回一个promise对象,并把导入的模块作为promise对象的result值,因此可以直接使用await import()
来导入模块
作用: 按需加载模块,提高页面加载速度
// 静态导入
// import * as module from './module.js';
// 动态导入
let btn = document.querySelector('button');
btn.addEventListener('click', async () => {
let module = await import('./module.js');
module.fn();
})
Bigint大整形类型
超过js中最大安全正数时则出现运算错误,使用BigInt可以解决这个问题
let maxint = Number.MAX_SAFE_INTEGER;
console.log(maxint + 1); // 输出: 9007199254740992
console.log(maxint + 1); // 输出: 9007199254740992
// 使用bigint来处理大整数
let bigint = BigInt(maxint);
console.log(bigint + 1n); // 输出: 9007199254740993n
console.log(bigint + 1n); // 输出: 9007199254740994n
globalThis
globalThis: 不同环境中全局this指向,浏览器中指向window,node中指向global
在浏览器中,globalThis指向window对象
<script>
console.log(globalThis); // 输出: Window {window: Window, self: Window, document: document, name: "", location: Location, …}
</script>
在node中,全局globalThis指向global对象
globalThis.js
console.log(globalThis);