目录
[箭头函数内部没有 this,箭头函数的 this 是上下文的 this](#箭头函数内部没有 this,箭头函数的 this 是上下文的 this)
[箭头函数内部没有 arguments 这个参数集合](#箭头函数内部没有 arguments 这个参数集合)
[函数的行参只有一个的时候可以不写 () 其余情况必须写](#函数的行参只有一个的时候可以不写 () 其余情况必须写)
[函数体只有一行代码的时候,可以不写 {} ,并且会自动 return](#函数体只有一行代码的时候,可以不写 {} ,并且会自动 return)
[2.Symbol()函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述。这主要是为了在控制台显示,比较容易区分。](#2.Symbol()函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述。这主要是为了在控制台显示,比较容易区分。)
[4.Symbol.for()可以重新使用同一个 Symbol 值](#4.Symbol.for()可以重新使用同一个 Symbol 值)
[Promise 对象的状态](#Promise 对象的状态)
[Generator 函数](#Generator 函数)
[for await of](#for await of)
[trimStart() and trimEnd()](#trimStart() and trimEnd())
[Symbol 对象的 description 属性](#Symbol 对象的 description 属性)
[可选的 catch](#可选的 catch)
[动态导入 import()](#动态导入 import())
[export * as obj from 'module'](#export * as obj from 'module')
初识ES6
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
-
1997年:ECMAScript 1.0
-
1998年:ECMAScript 2.0
-
1999年:ECMAScript 3.0
-
2006年:ECMAScript 4.0 未通过
-
2009年:ECMAScript 5.0
-
2015年:ECMAScript 6.0
-
至今,版本号改用年号的形式。
变量声明
1、let与const不可重复声明(数据不会覆盖)
javascript
// var
var a = 10;
var a = 20;
console.log(a); // 20
// let
let b = 10;
// let b = 20; // SyntaxError: Identifier 'b' has already been declared
// const
const c = 10;
// const c = 20; // SyntaxError: Identifier 'c' has already been declared
2、let
和 const
声明的变量不会在预解析的时候解析(也就是没有变量提升)
javascript
console.log(num1)
var num1 = 10; // undefined
console.log(num2)
let num2 = 20; // ReferenceError: Cannot access 'num2' before initialization
console.log(num3)
const num3 = 30; // ReferenceError: Cannot access 'num3' before initialization
3、let
和 const
声明的变量会被所有代码块限制作用范围
(var只有函数限制作用域,let、const 声明的变量,除了函数可以限制,所有的代码块都可以限制其作用域(if/while/for/...)
javascript
if(true){
var numVar = 10;
}
console.log(numVar); // 10
if(true){
let numLet = 20;
}
// console.log(numLet); // ReferenceError: numLet is not defined
if(true){
const numConst = 30;
}
// console.log(numConst); // ReferenceError: numConst is not defined
4、let声明的变量的值可以改变,const声明的值不可改变
javascript
//let和const区别
let lnum = 100
lnum = 200
console.log(lnum)
const cnum = 100
cnum = 200
5、let声明的时候可以不赋值,const声明时必须赋值
解构赋值
对象解构
javascript
const Ian = {
name:'IIIIIan',
age:0
}
// console.log(name,age) // 报错,因为name和age是Ian对象的属性,不是全局变量
const {name,age} = Ian
console.log(name,age)
数组解构
javascript
const arr = [1,2,3]
console.log(arr[0],arr[1],arr[2])// 1 2 3, 传统写法
console.log(arr)// [1,2,3]
console.log(...arr)// 1 2 3, 展开运算符
console.log([...arr])// [1,2,3]
// 解构赋值
const [a,b,c] = arr
console.log('a,b,c',a,b,c)
模版字符串
ES5 中我们表示字符串的时候使用 ''
或者 ""
在 ES6 中,我们还有一个东西可以表示字符串,就是 ``(反引号)
javascript
let str = `hello world`
console.log(typeof str) // string
1、``允许字符串换行
2、反引号可以直接在字符串里面拼接变量
javascript
// 模版字符串拼接变量
let num = 100
let str = `hello${num}world${num}`
console.log(str) // hello100world100
字符串扩展
includes()
判断字符串中是否存在指定字符
repeat()
返回一个新字符串,表示将原字符串重复n次。
startsWith()
返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith()
返回布尔值,表示参数字符串是否在原字符串的尾部。
javascript
let str = 'hello world'
console.log(str.includes('hello')) // true
console.log(str.startsWith('hello')) // true
console.log(str.endsWith('world')) // true
console.log(str.startsWith('ian')) // false
console.log(str.repeat(3)) // hello worldhello worldhello world
数值扩展
二进制和八进制表示法
javascript
let count1 = 100
let count2 = 0x100
let count3 = 0o100
let count4 = 0b100
(Number.)isFinite()与isNaN()
javascript
// isFinite 和 isNaN
// isFinite():用来检查一个数值是否为有限的(finite),即不是Infinity。
// isNaN():用来检查一个值是否为NaN。
console.log('1/3',isFinite(1/3))
console.log('0',isFinite(0)) // true
console.log('Infinity',isFinite(Infinity)) // false
console.log('NaN',isFinite(NaN)) // false
console.log('NaN',isNaN(NaN)) // true
console.log('0',isNaN(0)) // false
let num1 = Number.isFinite(100) //true
let num2 = Number.isFinite(100/0) //false
let num3 = Number.isFinite(Infinity) // false
let num4 = Number.isFinite("100") //false
let num1 = Number.isNaN(100) // false
let num2 = Number.isNaN(NaN) //true
let num3 = Number.isNaN("kerwin") //false
let num4 = Number.isNaN("100") // false
减少全局性方法,使得语言逐步模块化,它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。
Number.isInteger()
判断是否为整数
javascript
console.log('NaN',Number.isInteger('NaN')) // false
console.log('NaN',Number.isInteger(NaN)) // false
console.log('0',Number.isInteger(0)) // true
console.log('0.1',Number.isInteger(0.1)) // false
Math.trunc
将小数部分抹掉,返回一个整数。
javascript
console.log(Math.trunc(1.2)) //1
console.log(Math.trunc(1.8))// 1
console.log(Math.trunc(-1.8)) //-1
console.log(Math.trunc(-1.1))//-1
Math.sign
Math.sign
方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
javascript
console.log(Math.sign(1)) // 1
console.log(Math.sign(-1)) // -1
console.log(Math.sign(0)) // 0
console.log(Math.sign(-0)) // -0
console.log(Math.sign(NaN)) // NaN
console.log(Math.sign('ian')) // NaN
数组扩展
...扩展运算符
javascript
let arr1 = [1,2,3]
let arr2 = [4,5,6]
console.log([...arr1,...arr2])
Array.from
将类数组对象转换为真正数组
javascript
function test(){
console.log(Array.from(arguments))
}
test(1,2,3)
let oli = document.querySelectorAll("li")
console.log(Array.from(oli))
Array.of
javascript
let arr1 = Array(3)
console.log('arr1',arr1);
let arr2 = Array.of(3)
console.log('arr2',arr2);
find方法
1)该方法主要应用于查找第一个符合条件的数组元素
2)它的参数是一个回调函数。在回调函数中可以写你要查找元素的条件,当条件成立为true时,返回该元素。如果没有符合条件的元素,返回值为undefined
javascript
let arr = [11,12,13,14,15]
let res1 = arr.find(function(item){
return item>13
})
let res2 = arr.findIndex(function(item){
return item>13
})
let res3 = arr.find(function(item){
return item>16
})
console.log(res1) //14
console.log(res2) //3
console.log(res3) //undefined
fill方法
使用自己想要的参数替换原数组内容,但是会改变原来的数组
方法用一个固定值填充一个数组中从起始索引(默认为 0
)到终止索引(默认为 array.length
)内的全部元素。它返回修改后的数组。
javascript
let arr11 = new Array(3).fill("Ian")
let arr22 = ['a', 'b', 'c'].fill("Ian", 1, 2)
console.log(arr11)//['Ian', 'Ian', 'Ian']
console.log(arr22)// ['a', 'Ian', 'c']
// MDN示例
const array1 = [1, 2, 3, 4];
// Fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4));
// Expected output: Array [1, 2, 0, 0]
// Fill with 5 from position 1
console.log(array1.fill(5, 1));
// Expected output: Array [1, 5, 5, 5]
console.log(array1.fill(6));
// Expected output: Array [6, 6, 6, 6]
flat与flatMap方法
flatMap()按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回
flat()
方法创建一个新的数组,并根据指定深度递归地将所有子数组元素拼接到新的数组中。
javascript
// MDN示例
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());
// expected output: Array [0, 1, 2, 3, 4]
const arr2 = [0, 1, [2, [3, [4, 5]]]];
console.log(arr2.flat());
// expected output: Array [0, 1, 2, Array [3, Array [4, 5]]]
console.log(arr2.flat(2));
// expected output: Array [0, 1, 2, 3, Array [4, 5]]
console.log(arr2.flat(Infinity));
// expected output: Array [0, 1, 2, 3, 4, 5]
javascript
var obj = [{
name: "A",
list: ["鞍山", "安庆", "安阳"]
},
{
name: "B",
list: ["北京", "保定", "包头"]
}
]
console.log(obj.flatMap(item => item.list))//["鞍山", "安庆", "安阳", "北京", "保定", "包头"]
对象扩展
对象简写
javascript
let name ="moduleA"
let obj = {
name,
test1(){
console.log('test1',this.name)
},
test2(){
console.log('test2',this.name)
}
}
obj.test1()
obj.test2()
console.log(obj)
console.log(obj.name)
obj.name = "moduleB"
console.log(obj.name)
属性名表达式
javascript
let name1 ="moduleA"
let obj1 = {
name1,
[name1+"test1"](){
},
[name1+"test2"](){
}
}
console.log(obj1)
Object.assign
Object.assign(target, object1,object2)的第一个参数是目标对象,后面可以跟一个或多个源对象作为参数。
target:参数合并后存放的对象
object1:参数1
object2:参数2
javascript
const a = {
name:"Ian"
}
const b = {
age:18
}
const c = {
height:180
}
Object.assign(a,b,c)
console.log(a)
Object.is
方法判断两个值是否是相同的值
javascript
console.log(NaN===NaN) //false
console.log(+0===-0) //true
console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false
函数扩展
箭头函数
-
箭头函数是 ES6 里面一个简写函数的语法方式
-
重点: 箭头函数只能简写函数表达式,不能简写声明式函数
javascript
function fn() {} // 不能简写
const fun = function () {} // 可以简写
const obj = {
fn: function () {} // 可以简写
}
语法: (函数的行参) => { 函数体内要执行的代码 }
javascript
const fn = function (a, b) {
console.log(a)
console.log(b)
}
// 可以使用箭头函数写成
const fun = (a, b) => {
console.log(a)
console.log(b)
}
const obj = {
fn: function (a, b) {
console.log(a)
console.log(b)
}
}
// 可以使用箭头函数写成
const obj2 = {
fn: (a, b) => {
console.log(a)
console.log(b)
}
}
箭头函数的特殊性
箭头函数内部没有 this,箭头函数的 this 是上下文的 this
javascript
// 在箭头函数定义的位置往上数,这一行是可以打印出 this 的
// 因为这里的 this 是 window
// 所以箭头函数内部的 this 就是 window
const obj = {
fn: function () {
console.log(this)
},
// 这个位置是箭头函数的上一行,但是不能打印出 this
fun: () => {
// 箭头函数内部的 this 是书写箭头函数的上一行一个可以打印出 this 的位置
console.log(this)
}
}
obj.fn()
obj.fun()
-
按照我们之前的 this 指向来判断,两个都应该指向 obj
-
但是 fun 因为是箭头函数,所以 this 不指向 obj,而是指向 fun 的外层,就是 window
箭头函数内部没有 arguments
这个参数集合
javascript
const obj = {
fn: function () {
console.log(arguments)
},
fun: () => {
console.log(arguments)
}
}
obj.fn(1, 2, 3) // 会打印一个伪数组 [1, 2, 3]
obj.fun(1, 2, 3) // 会直接报错
函数的行参只有一个的时候可以不写 ()
其余情况必须写
javascript
const obj = {
fn: () => {
console.log('没有参数,必须写小括号')
},
fn2: a => {
console.log('一个行参,可以不写小括号')
},
fn3: (a, b) => {
console.log('两个或两个以上参数,必须写小括号')
}
}
函数体只有一行代码的时候,可以不写 {}
,并且会自动 return
javascript
const obj = {
fn: a => {
return a + 10
},
fun: a => a + 10
}
console.log(fn(10)) // 20
console.log(fun(10)) // 20
函数传递参数的时候的默认值
javascript
function fn(a) {
a = a || 10
console.log(a)
}
fn() // 不传递参数的时候,函数内部的 a 就是 10
fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
- 在 ES6 中我们可以直接把默认值写在函数的行参位置
javascript
function fn(a = 10) {
console.log(a)
}
fn() // 不传递参数的时候,函数内部的 a 就是 10
fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
// 箭头函数也可以使用
const fn = (a = 10) => {
console.log(a)
}
fn() // 不传递参数的时候,函数内部的 a 就是 10
fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
注意: 箭头函数如果需要使用默认值,那么一个参数的时也需要写 ()
Symbol
ES6 引入了一种新的原始数据类型
Symbol
,表示独一无二的值。它属于 JavaScript 语言的原生数据类型之一,其他数据类型是:undefined
、null
、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
1.使用Symbol作为对象属性名
javascript
let name = Symbol()
let age = Symbol()
var obj = {
[name]: '张三',
[age]: 18
}
age = 20// 这里的 age 是一个普通的字符串,不是 Symbol 类型的
console.log(obj)
let a = Symbol('A')
let b = Symbol('A')
console.log(a === b) // false
2.Symbol()函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述。这主要是为了在控制台显示,比较容易区分。
javascript
let NickName = Symbol('NickName')
let obj1 = {
[name]: '张三',
[NickName]: 'ian'
}
console.log(obj1)
3.遍历问题
javascript
let keys = {
name:Symbol("name"),
age:Symbol("age")
}
var obj ={
[keys.name]:"Ian",
[keys.age]:100,
a:1,
b:2,
c:3
}
Reflect.ownKeys(obj).forEach(item=>{
console.log(item,obj[item])
})
4.Symbol.for()可以重新使用同一个 Symbol 值
javascript
var obj ={
[Symbol.for("name")]:"Ian",
[Symbol.for("age")]:100
}
console.log(obj[Symbol.for("name")])
Iterator迭代器
Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令for...of 循环,Iterator 接口主要供for...of循环
javascript
let arr = ["kerwin", "tiechui", "gangdaner"]
for(let i of arr){
console.log(i)
}
Iterator 的遍历过程:
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
let i = arr[Symbol.iterator]()
console.log(i.next())
console.log(i.next())
console.log(i.next())
console.log(i.next())
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是"可遍历的"(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。
原生默认具备 Iterator 接口的数据结构如下。
-
Array
-
Set
-
Map
-
String
-
arguments 对象
-
NodeList 对象
javascript
let obj = {
0: "kerwin",
1: "tiechui",
2: "gangdaner",
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
}
for (let i of obj) {
console.log(i)
}
let obj2 = {
data: ['kerwin', 'tiechui', "gangdaner"],
[Symbol.iterator]() {
// let _this = this
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return {
value: this.data[index++],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
};
for (let i of obj2) {
console.log(i)
}
Set结构
它类似于数组,但成员的值都是唯一的,没有重复的值。
初识Set
javascript
let s1 = new Set([1, 2, 3, 2, 3])
console.log(s1)
let s2 = new Set()
s2.add(1)
s2.add(2)
s2.add(3)
s2.add(3)
console.log(s2)
实例的属性和方法
-
size:返回Set实例的成员总数。
-
Set.prototype.add(value)
:添加某个value。 -
Set.prototype.delete(value)
:删除某个value,返回一个布尔值,表示删除是否成功。 -
Set.prototype.has(value)
:返回一个布尔值,表示该值是否为Set
的成员。 -
Set.prototype.clear()
:清除所有成员,没有返回值。
遍历
-
Set.prototype.keys()
:返回键名的遍历器 -
Set.prototype.values()
:返回键值的遍历器 -
Set.prototype.entries()
:返回键值对的遍历器 -
Set.prototype.forEach()
:遍历每个成员
复杂数据结构去重
javascript
function uni(arr) {
let res = new Set()
return arr.filter(item => {
let id = JSON.stringify(item)
if (res.has(id)) {
return false
} else {
res.add(id)
return true
}
})
}
var arr = [1, 2, 3, "data", {
name: "Ian"
}, {
name: "Ian"
},
[1, 2],
[3, 4],
[3, 4]
]
console.log(uni(arr))
Map结构
类似于对象,也是键值对的集合,但是"键"的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
初识Map
javascript
let m1 = new Map()
m1.set("name","Ian")
m1.set({a:1},"XX")
console.log(m1)
let m2= new Map([
["name","Ian"],
[{a:1},"XX"]
])
console.log(m2)
实例的属性和方法
-
size:返回 Map 结构的成员总数。
-
Map.prototype.set(key,value)
:添加key对应得value,返回 Map 结构本身。 -
Map.prototype.get(key)
:获取key对应的value -
Map.prototype.delete(key)
:删除某个键(键名+键值) -
Map.prototype.has(key)
:某个键是否在当前 Map 对象之中。 -
Map.prototype.clear()
:清除所有成员,没有返回值。
遍历
-
Map.prototype.keys():返回键名的遍历器。
-
Map.prototype.values():返回键值的遍历器。
-
Map.prototype.entries():返回所有成员的遍历器。
-
Map.prototype.forEach():遍历 Map 的所有成员。
Proxy代理
Proxy如其名, 它的作用是在对象和和对象的属性值之间设置一个代理,获取该对象的值或者设置该对象的值, 以及实例化等等多种操作, 都会被拦截住, 经过这一层我们可以统一处理,我们可以认为它就是"代理器"
get()
javascript
let target = {}
let proxy = new Proxy(target,{
get(target,prop){
return target[prop]
}
})
set()
javascript
let target = {}
let proxy = new Proxy(target,{
get(target,prop){
return target[prop]
},
set(target,prop,value){
if(prop==="data"){
box.innerHTML = value
}
target[prop] = value;
}
})
has()
javascript
let target = {
_prop: "内部数据"
}
let proxy = new Proxy(target, {
get(target, prop) {
return target[prop]
},
set(target, prop, value) {
if (prop === "data") {
box.innerHTML = value
}
target[prop] = value;
},
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
})
this
javascript
let target = new Set()
const proxy = new Proxy(target, {
get(target, key) {
const value = target[key]
// 遇到 Function 都手动绑定一下 this
if (value instanceof Function) {
console.log(`访问${value}方法了`)
return value.bind(target)
//不能 是 call apply
}
return value
}
})
proxy.add(1)
Proxy本质上属于元编程非破坏性数据劫持,在原对象的基础上进行了功能的衍生而又不影响原对象,符合松耦合高内聚的设计理念。
Reflect对象
Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
代替Object的某些方法
javascript
const obj = {
};
Reflect.defineProperty(obj, 'name', {
value: 'kerwin',
writable: false,
configurable:false
});
修改某些Object方法返回结果
javascript
// 老写法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// fail
}
// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// fail
}
命令式变为函数行为
javascript
const obj = {
name:"Ian"
};
//老写法
console.log("name" in obj) //true
//新写法
console.log(Reflect.has(obj, 'name')) //true
//老写法
delete obj.name
//新写法
Reflect.deleteProperty(obj, "name")
配合Proxy
javascript
let target = new Set()
const proxy = new Proxy(target, {
get(target, key) {
const value = Reflect.get(target,key)
// 遇到 Function 都手动绑定一下 this
if (value instanceof Function) {
console.log(`访问${value}方法了`)
return value.bind(target)
//不能 是 call apply
}
return value
},
set() {
return Reflect.set(...arguments)
}
})
proxy.add(1)
javascript
let arr = [1, 2, 3]
let proxy = new Proxy(arr, {
get(target, key) {
console.log('get', key)
return Reflect.get(...arguments)
},
set(target, key, value) {
console.log('set', key, value)
return Reflect.set(...arguments)
}
})
proxy.push(4)
// 能够打印出很多内容
// get push (寻找 proxy.push 方法)
// get length (获取当前的 length)
// set 3 4 (设置 proxy[3] = 4)
// set length 4 (设置 proxy.length = 4)
Promise
Promise 是异步编程的一种解决方案,比传统的解决方案回调函数, 更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象 。
回调地狱
-
当一个回调函数嵌套一个回调函数的时候
-
就会出现一个嵌套结构
-
当嵌套的多了就会出现回调地狱的情况
-
比如我们发送三个 ajax 请求
-
第一个正常发送
-
第二个请求需要第一个请求的结果中的某一个值作为参数
-
第三个请求需要第二个请求的结果中的某一个值作为参数
-
javascript
javascript
ajax({
url: '我是第一个请求',
success (res) {
// 现在发送第二个请求
ajax({
url: '我是第二个请求',
data: { a: res.a, b: res.b },
success (res2) {
// 进行第三个请求
ajax({
url: '我是第三个请求',
data: { a: res2.a, b: res2.b },
success (res3) {
console.log(res3)
}
})
}
})
}
})
Promise使用
javascript
new Promise(function (resolve, reject) {
// resolve 表示成功的回调
// reject 表示失败的回调
}).then(function (res) {
// 成功的函数
}).catch(function (err) {
// 失败的函数
})
Promise 对象的状态
Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。
异步操作未完成(pending)
异步操作成功(fulfilled)
异步操作失败(rejected)
这三种的状态的变化途径只有两种。
从“未完成”到“成功”
从“未完成”到“失败”
一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是"承诺",一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。
因此,Promise 的最终结果只有两种。
异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。
Promise.all
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
javascript
const p = Promise.all([p1, p2, p3]);
p的状态由p1,p2,p3 决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
Promise.race
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
javascript
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
Generator 函数
Generator 函数是 ES6 提供的一种异步编程解决方案
Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
一般使用
javascript
function *gen(){
console.log(1)
yield;
console.log(2)
yield;
console.log(3)
}
let g = gen()
g.next()
g.next()
g.next()
yield(产出)表达式是暂停执行的标记,而next方法可以恢复执行。
javascript
function *gen(){
yield 1;
yield 2;
}
let g = gen()
let res1 = g.next()
console.log(res1)
let res2 = g.next()
console.log(res2)
let res3 = g.next()
console.log(res3)
javascript
function *gen(){
let res1 = yield;
console.log(res1)
let res2 = yield;
console.log(res2)
}
let g = gen()
g.next("data-1")
g.next("data-2")
g.next("data-3")
异步流程
javascript
// 手动代码
function *gen(){
let res1 = yield ajax("1.json")
console.log(res1)
let res2 = yield ajax("2.json")
console.log(res2)
}
let g = gen()
g.next().value.then(data=>{
g.next(data).value.then(data=>{
g.next(data)
})
})
// 自动代码
function* gen() {
let res1 = yield ajax("1.json")
console.log(res1)
let res2 = yield ajax("2.json")
console.log(res2)
}
function AutoRun(gen) {
let g = gen();
function next(data) {
let res = g.next(data);
if (res.done) return
res.value.then(function (data) {
next(data);
});
}
next();
}
AutoRun(gen);
Class语法
类的写法
javascript
// 创造一个自行车类
class Bike{
constructor(name, price){
this.name = name
this.price = price
}
// 方法
show(){
console.log('这是一辆自行车'+this.name+'价格是'+this.price)
}
}
// 实例化一个自行车
let bike = new Bike('Giant', 2000)
bike.show()
console.log(bike)
Getter和Setter
javascript
class List{
constructor(element){
this.element = element
}
get html(){
return this.element.innerHTML
}
set html(arr){
this.element.innerHTML = arr.map(item=>`<li>${item}</li>`).join('')
}
}
let list = new List(document.querySelector('#list'))
list.html = ['a', 'b', 'c']
静态属性、静态方法
javascript
class Person {
static name = "Person这个类"
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log(this.name,this.age)
}
static eat(){
console.log("eat")
}
}
let obj = new Person("Ian",100)
console.log(Person.name)
Person.eat()
继承
ES6 规定,子类必须在
constructor()
方法中调用super()
,否则就会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()
方法,子类就得不到自己的this
对象。
javascript
class Person {
static name = "Person这个类"
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log(this.name,this.age)
}
static eat(){
console.log("eat")
}
}
class Student extends Person{
constructor(name,age,score){
super(name,age)
this.score = score
}
// 方法重写
say(){
super.say()
console.log(this.score)
}
static eat(){
super.eat();
console.log("student eat")
}
}
let obj1 = new Student("Ian",100,200)
console.log(obj1)
obj1.say()
Student.eat()
JavaScript 现在有两种模块。一种是 ES6 模块,简称 ESM;另一种是 CommonJS 模块,简称 CJS。
CommonJS 模块是 Node.js 专用的,与 ES6 模块不兼容。语法上面,两者最明显的差异是,CommonJS 模块使用
require()
和module.exports
,ES6 模块使用import
和export
。
javascript
// 方法一
export default A1
import a1 from "./1.js"
// 方法二
export {A1,A2}
import {A1,A2} from "./1.js"
import {A1 as a1,A2 as a2} from "./1.js"
import * as obj from "./1.js"
export function A1(){
console.log("A1")
}
export function A2(){
console.log("A2")
}
import {A1,A2} from "./1.js"
import {A1 as a1,A2 as a2} from "./1.js"
import * as obj from "./1.js"
// 混合应用
export {A1}
export default A2
import A2,{A1} from "./1.js"
初识ES7
求幂运算符
javascript
console.log(Math.pow(2, 3))
console.log(2**3)
数组的includes方法
javascript
[1, 2, NaN].includes(NaN) // true
[1, 2, NaN].indexOf(NaN) // -1
如果仅仅查找数据是否在数组中,建议使用includes,如果是查找数据的索引位置,建议使用indexOf更好一些
初识ES8
async
async 函数,使得异步操作变得更加方便。
-
更好的语义。
-
返回值是 Promise。
javascript
async function test(){
}
test()
Await
await
命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
javascript
async function test(){
var res1 = await ajax("http://localhost:3000/news1")
var res2 = await ajax("http://localhost:3000/news2")
return res2
}
test().then(res=>{
console.log("返回结果",res)
}).catch(err=>{
console.log("err",err)
})
错误处理
javascript
try{
var res1 = await ajax("http://localhost:3000/news1")
var res2 = await ajax("http://localhost:3000/news2")
}catch(err){
console.log("err",err)
}
对象方法扩展
Object.values
javascript
let obj = {
name:"Ian",
age:100
}
console.log(Object.values(obj))
Object.entries
javascript
let obj1 = {
name:"Ian",
age:100
}
console.log(Object.entries(obj1))
getOwnPropertyDescriptors
javascript
let obj = {
name:"Ian",
age:100
}
console.log(Object.values(obj))
console.log(Object.getOwnPropertyDescriptors(obj))
克隆对象
javascript
let obj1 = {
name:"Kerwin",
age:100,
location:{
provice:"辽宁",
city:"大连"
},
//只设置city,防止破坏province
get city(){
return this.location.city
},
set city(value){
this.location.city = value
},
set nameset(value){
this.name = value.substring(0,1).toUpperCase()+value.substring(1)
},
get nameset(){
return this.name
}
}
console.log(Object.getOwnPropertyDescriptors(obj1))
var obj2= {}
//Object.assign(obj2,obj1)//无法克隆 get set方法
Object.defineProperties(obj2,Object.getOwnPropertyDescriptors(obj1))
字符串填充
padStart()、padEnd()方法可以使得字符串达到固定长度,有两个参数,字符串目标长度和填充内容。
javascript
let str = 'Ian'
console.log(str.padStart(10, 'I'))
console.log(str.padEnd(10, 'I'))
函数参数的末尾加逗号
『末尾逗号』在添加新的参数、属性、元素时是有用的,你可以直接新加一行而不必给上一行再补充一个逗号,这样使版本控制工具的修改记录也更加整洁
javascript
function test(
a,
b,
c,
){
console.log(a,b)
}
test(
1,
2,
3,
)
初识ES9
对象的剩余参数与扩展运算符
对象的剩余参数
javascript
let obj = {
name:"kerwin",
age:100,
location:"dalian"
}
let {name,...other} = obj
console.log(name) //kerwin
console.log(other) //{age: 100, location: 'dalian'}
对象的扩展运算符
javascript
let obj1 = {
name:"kerwin"
}
let obj2 = {
age:100
}
console.log({...obj1,...obj2})
正则表达式命名捕获组
JS正则表达式可以返回一个匹配的对象, 一个包含匹配字符串的类数组, 比如: 以 YYYY-MM-DD的格式解析日期,
这样的代码可读性很差, 并且在改变正则表达式的结构的时候很有可能就会改变匹配对象的索引
ES9允许使用命名捕获 ?<name> , 在打开捕获括号后立即命名
javascript
let str = "今天是2022-10-10"
let reg = /([0-9]{4})-([0-9]{2})-([0-9]{2})/g
let res1 = reg.exec(str)
console.log(res1)
let str = "今天是2022-10-10"
let reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/g
let res1 = reg.exec(str)
console.log(res1)
Promise.finally()
无论是成功还是失败, 都运行同样的代码, 比如隐藏对话框, 关闭数据连接
javascript
function ajax(){
return new Promise((resolve,reject)=>{
reject(1111)
})
}
//showloading
ajax().then(res=>{
}).catch(err=>{
}).finally(()=>{
//hideloading
console.log("finally")
})
异步遍历器
同步遍历器的问题
javascript
function* fn() {
yield 1111
yield 2222
}
const syncI = fn();
console.log(syncI.next())
console.log(syncI.next())
console.log(syncI.next())
异步遍历器
javascript
function* fn() {
yield new Promise(resolve=>resolve("1111"))
yield new Promise(resolve=>resolve("2222"))
}
const syncI = fn();
syncI.next().value.then(res=>{console.log(res)})
syncI.next().value.then(res=>{console.log(res)})
value
属性的返回值是一个 Promise 对象,用来放置异步操作。但是这样写很麻烦,不太符合直觉,语义也比较绕。
异步遍历器生成函数
Generator 函数返回一个同步遍历器,异步 Generator 函数的作用,是返回一个异步遍历器对象。在语法上,异步 Generator 函数就是async函数与 Generator 函数的结合。
javascript
async function* fn() {
yield new Promise(resolve=>resolve("1111"))
yield new Promise(resolve=>resolve("2222"))
}
const asyncI = fn();
asyncI.next().then(res=>{
console.log(res)
return asyncI.next()
}).then(res=>{
console.log(res)
return asyncI.next()
})
.then(res=>{
console.log(res)
})
for await of
for...of
循环用于遍历同步的 Iterator 接口。新引入的for await...of
循环,则是用于遍历异步的 Iterator 接口。
javascript
function timer(t) {
return new Promise(resolve => {
setTimeout(() => {
resolve(t)
}, t)
})
}
async function* fn() {
yield timer(1000)//任务1
yield timer(2000)//任务2
yield timer(3000)//任务3
}
// 使用 for await ...of
async function fn1() {
for await(const val of fn()) {
console.log("start",Date.now())
console.log(val);
console.log("end",Date.now())
}
}
fn1();
nodejs用法
javascript
// 传统写法
function main(inputFilePath) {
const readStream = fs.createReadStream(
inputFilePath,
{ encoding: 'utf8', highWaterMark: 1024 }
);
readStream.on('data', (chunk) => {
console.log('>>> '+chunk);
});
readStream.on('end', () => {
console.log('### DONE ###');
});
}
// 异步遍历器写法
async function main(inputFilePath) {
const readStream = fs.createReadStream(
inputFilePath,
{ encoding: 'utf8', highWaterMark: 1024 }
);
for await (const chunk of readStream) {
console.log('>>> '+chunk);
}
console.log('### DONE ###');
}
初识ES10
Object.fromEntries
将键值对列表转换为对象(数组和字符串)
javascript
const arr = [["name", "kerwin"], ["age", 100]];
console.log(Object.fromEntries(arr))//{name: 'kerwin', age: 100}
let str ="name=kerwin&age=100"
let searchParams = new URLSearchParams(str)
console.log(Object.fromEntries(searchParams))//{name: 'kerwin', age: '100'}
trimStart() and trimEnd()
javascript
let str = " IAN "
console.log("|"+str.trimStart(str)+"|")
console.log("|"+str.trimEnd(str)+"|")
console.log("|"+str.trimLeft(str)+"|")
console.log("|"+str.trimRight(str)+"|")
Symbol 对象的 description 属性
为Symbol对象添加了只读属性 description ,该对象返回包含Symbol描述的字符串。
javascript
let s = Symbol("Ian")
console.log(s)
console.log(s.description) //Ian
可选的 catch
javascript
let pro1 = new Promise(function (resolve, reject) {
//执行器函数
setTimeout(() => {
resolve("成功的结果")
}, 30000)
})
let pro2 = new Promise(function (resolve, reject) {
//执行器函数
setTimeout(() => {
reject()
}, 2000)
})
async function test() {
try {
await Promise.race([pro1, pro2])
} catch {
console.log("不关心错误结果,网络超时")
}
}
test()
初识ES11
Promise.allSettled
Promise.allSettled() 方法返回一个在所有给定的 promise 都已经 fulfilled 或 rejected 后的 promise ,并带有一个对象数组,每个对象表示对应的 promise 结果。
javascript
const promises = [ ajax('/200接口'), ajax('/401接口') ];
Promise.allSettled(promises).then(results=>{
// 过滤出成功的请求
results.filter(item =>item.status === 'fulfilled');
过滤出失败的请求
results.filter(item=> item.status === 'rejected');
})
module新增
动态导入 import()
标准用法的 import 导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。
javascript
<body>
<button>login</button>
<script type="module">
let role1 = "管理员"
let role2 = "普通用户"
function login(){
return "普通用户"
}
async function render(role){
if(role===role1){
let res1 = await import("./1.js")
console.log(res1.default)
}else{
let res2 = await import("./2.js")
console.log(res2.default)
}
}
let obtn = document.querySelector("button")
obtn.onclick = function(){
let role = login()
render(role)
}
</script>
</body>
import.meta
import.meta 会返回一个对象,有一个 url 属性,返回当前模块的url路径,只能在模块内部使用。
javascript
<script type="module">
import obj from './1.js'
</script>
//1.js
console.log(import.meta)
export default {
}
export * as obj from 'module'
字符串的matchAll方法
matchAll() 方法返回一个包含所有匹配正则表达式的结果的迭代器。可以使用 for...of 遍历,或者使用 展开运算符(...) 或者 Array.from 转换为数组.
javascript
let str = `
<ul>
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
</ul>
`
let reg = /<li>(.*)<\/li>/g
console.log(str.match(reg))
//'<li>1111</li>', '<li>2222</li>', '<li>3333</li>', '<li>4444</li>'
let str = `
<ul>
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
</ul>
`
let reg = /<li>(.*)<\/li>/g
let match = null;
while(match = reg.exec(str)){
console.log(match[0])
console.log(match[1])
}
let str = `
<ul>
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
</ul>
`
let reg = /<li>(.*)<\/li>/g
for(let i of str.matchAll(reg)){
console.log(i)
}
BigInt
JavaScript 能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值,这使得 JavaScript 不适合进行科学和金融方面的精确计算。
javascript
9007199254740992 //9007199254740992
9007199254740993 //9007199254740992
Math.pow(2,53) === Math.pow(2,53)+1
// 为了与 Number 类型区别,BigInt 类型的数据必须添加后缀`n`。
1234 // 普通整数
1234n // BigInt
// BigInt 的运算
1n + 2n // 3n
globalThis
globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)。不像 window 或者 self这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用 globalThis,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的 this 就是 globalThis。
javascript
//以前
var getGlobal = function () {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};
let globals = getGlobal()
if (globals.document) {
console.log("进行dom操作相关")
} else {
console.log("不能进行dom操作")
}
//现在
if (globalThis.document) {
console.log("进行dom操作相关")
} else {
console.log("不能进行dom操作")
}
空值合并运算符
**空值合并运算符(??)**是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。
javascript
console.log(obj.introduction || "这个人很懒")
console.log(obj.introduction ?? "这个人很懒")
??和 || 的区别是什么呢?
他们两个最大的区别就是 ' '和 0,??的左侧 为 ' '或者为 0 的时候,依然会返回左侧的值;
|| 会对左侧的数据进行boolean类型转换,所以' '和 0 会被转换成false,返回右侧的值
可选链操作符
可选链前面的值如果是null或undefined,则不再执行后面的,之前返回可选链前面的值
javascript
let obj = {
name:"kerwin",
introduction:0,
// location:{
// city:"dalian"
// }
}
console.log(obj && obj.location && obj.location.city)
console.log(obj?.location?.city)
初识ES12
逻辑赋值操作符
逻辑赋值操作符 ??=、&&=、 ||=
javascript
let a = true
let b = false
//a &&= b //false
a ||= b ; //true
console.log(a)
let obj = {
name:"kerwin",
}
obj.introduction = obj.introduction??"很懒"
obj.introduction??="很懒"
数字分隔符
分隔符不仅可以分割十进制,也可以分割二净值或者十六净值的数据
javascript
const number = 1_000_000_000_000;
const binary = 0b1010_0101_1111_1101;
const hex = 0xA1_B2_C3;
replaceAll
replaceAll(被替换的,用来替换掉的)
所有匹配都会被替代项替换。模式可以是字符串或正则表达式,而替换项可以是字符串或针对每次匹配执行的函数。并返回一个全新的字符串
javascript
const str =
"I wish to wish the wish you wish to wish, but if you wish the wish the witch wishes, I won't wish the wish you wish to wish. ";
const newStr = str.replaceAll("wish", "Ian");
console.log(newStr);
Promise.any
只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态。
Promise.any()
跟Promise.race()
方法很像,只有一点不同,就是Promise.any()
不会因为某个 Promise 变成rejected
状态而结束,必须等到所有参数 Promise 变成rejected
状态才会结束。
WeakRef
在一般情况下,对象的引用是强引用的,这意味着只要持有对象的引用,它就不会被垃圾回收。只有当该对象没有任何的强引用时,垃圾回收才会销毁该对象并且回收该对象所占的内存空间。
而
WeakRef
允许您保留对另一个对象的弱引用,而不会阻止被弱引用对象被垃圾回收
javascript
let target = {};
let wr = new WeakRef(target);
console.log('wr.deref()',wr.deref());
console.log('wr',wr);
WeakRef 实例对象有一个deref()
方法,如果原始对象存在,该方法返回原始对象;如果原始对象已经被垃圾回收机制清除,该方法返回undefined
。
javascript
let like = new WeakRef(document.getElementById("like"))
let mymap = new WeakMap()
mymap.set(like.deref(), {
click: 0
})
like.deref().onclick = function () {
let times = mymap.get(like.deref())
times.click++
}
setTimeout(() => {
document.body.removeChild(like.deref())
}, 2000)
FinalizationRegistry
清理器注册表功能 FinalizationRegistry,用来指定目标对象被垃圾回收机制清除以后,所要执行的回调函数。
javascript
const registry = new FinalizationRegistry(data => {
// ....
});
registry.register(obj, "some value");
registry.unregister(obj);
let like = new WeakRef(document.getElementById("like"))
let mymap = new WeakMap()
mymap.set(like.deref(), {
click: 0
})
like.deref().onclick = function () {
let times = mymap.get(like.deref())
times.click++
}
setTimeout(() => {
// registry.register(document.getElementById("like"), mymap.get(like.deref()));
registry.register(like.deref(), mymap.get(like.deref()));
document.body.removeChild(like.deref())
}, 2000)
const registry = new FinalizationRegistry(data => {
// ....
console.log("被销毁了", data)
});
初识ES13
私有属性和方法
javascript
class Cache{
#obj ={}
get(key){
return this.#obj[key]
}
set(key,value){
this.#obj[key] =value
}
}
let cache = new Cache()
cache.set("name","Ian")
console.log(cache.name)
console.log(cache.get("name"))
静态成员的私有属性和方法
我们还可以给类定义静态成员和静态私有函数。类的静态方法可以使用
this
关键字访问其他的私有或者公有静态成员
javascript
class Cache{
#obj ={}
static #count = 0
static getCount(){
return this.#count
}
get(key){
return this.#obj[key]
}
set(key,value){
this.#obj[key] =value
}
}
let cache = new Cache()
cache.set("name","Ian")
console.log(cache.name)
console.log(cache.get("name"))
console.log(Cache.getCount())
静态代码块
ES13允许在类中通过
static
关键字定义一系列静态代码块,这些代码块只会在类被创造的时候执行一次。这其实有点像一些其他的如C#和Java等面向对象的编程语言的静态构造函数的用法
一个类可以定义任意多的静态代码块,这些代码块会和穿插在它们之间的静态成员变量一起按照定义的顺序在类初始化的时候执行一次。我们还可以使用super
关键字来访问父类的属性。
javascript
class Cache{
static obj = new Map()
static {
this.obj.set("name","Ian")
this.obj.set("age",100)
}
static{
console.log(this.obj)
}
}
console.log(Cache.obj)
使用in来判断某个对象是否拥有某个私有属性
javascript
class Cache {
#obj = {}
get(key) {
return this.#obj[key]
}
set(key, value) {
this.#obj[key] = value
}
hasObj(){
return #obj in this
}
}
let cache = new Cache()
console.log(cache.hasObj())
支持在最外层写await
顶层
await
只能用在 ES6 模块,不能用在 CommonJS 模块。这是因为 CommonJS 模块的require()
是同步加载,如果有顶层await
,就没法处理加载了。
javascript
<script type="module">
function ajax() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("data-1111");
}, 1000);
})
}
let res = await ajax();
console.log(res)
</script>
at函数来索引元素
javascript
let arr = ["kerwin","tiechui","gangdan","xiaoming"]
console.log(arr[1])
console.log(arr[arr.length-1]) //变丑了
console.log(arr[arr.length-2]) //变丑了
console.log(arr.at(1))
console.log(arr.at(-1))
console.log(arr.at(-2))
正则匹配的开始和结束索引
javascript
let str = "今天是2022-11-10"
let reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/d
//exec
let res = reg.exec(str)
console.log(res)
findLast()和findLastIndex()函数
javascript
let arr = [11,12,13,14,15]
// let res = arr.find(function(value){
// return value % 2 === 0
// })
// let res = arr.findIndex(function(value){
// return value % 2 === 0
// })
// let res = arr.findLast(function(value){
// return value % 2 === 0
// })
let res = arr.findLastIndex(function(value){
return value % 2 === 0
})
console.log(res)
Error对象的Cause属性
Error对象多了一个
cause
属性来指明错误出现的原因。这个属性可以帮助我们为错误添加更多的上下文信息,从而帮助使用者们更好地定位错误。
javascript
function getData(){
try{
console.log(kerwin)
}
catch(e){
throw new Error('New error 1111111',{cause:"这是因为,,,,,,,,,"});
}
}
try{
getData()
}catch(e){
console.log(e.cause)
}