本文很多参考 mdn,因为它相对权威 且含有非常多的示例。
var、 let、 const
- 不使用关键字声明的变量,默认为 window 属性。
- var 声明的变量,默认为 window 属性。
var
- var 声明的变量在执行上下文创建阶段就会被「创建」和「初始化」,因此对于执行阶段来说,可以在声明之前使用。
- 存在变量提升,JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行的运行,这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升
console.log(a); var a=1; // 打印结果不会报错 只会提示 undefined - var 没有块级作用域 只有函数作用域,块级作用域是指在{}中定义的语句。
- 允许重复声明
let
- 不属于顶层对象 window,创建的属性不会默认添加到window
- 不允许重复声明: 同一作用域不允许,声明同名变量。
- 不存在变量提升。
- 暂时性死区,let 声明的变量在执行上下文创建阶段只会被「创建」而不会被「初始化」,因此对于执行阶段来说,如果在其定义执行前使用,相当于使用了未被初始化的变量,会报错
xx is not defined - 块级作用域
const
- 拥有 let 特性, let 是变量可以重复赋值,const常量不能“重复赋值”
- const 用于声明一个或多个常量,声明时必须进行初始化
- const 不能重复赋值,是指值的地址不能改变。 js的基本数据类型不能重复赋值,但是js中的对象的元素可以进行重复赋值。
- 通过
Object.freeze()可以冻结对象,但是只能冻结一层。多层冻结需要手动递归处理。
解构赋值
数组解构赋值
// 数组解构
let [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1,2,3
// 接收参数多于 数组容量
let [a, b, c, d] = [1, 2, 3];
console.log(a, b, c, d); // 1,2,3, undefined 相当于声明未赋值
// 解构默认赋值
let [a, b, c=1] = [1, 2, 3];
console.log(a, b, c); // 1,2,3 数组中有对应元素 默认赋值无效 所以c = 3
对象解构赋值
// 对象解构赋值
let { name, age } = { name: "一个大胖子", age: 10 };
console.log(name, age); // 一个大胖子, 10
// 解构元素多于实际对象元素 多出的字段为 undefined
let { name, age, sex } = { name: "一个大胖子", age: 10 };
console.log(name, age, sex); // 一个大胖子, 10, undefined
// 默认赋值 对象存在对应元素 默认赋值无效, 所以age 依然等于10
// 如果被解构对象无对应元素 则 age 等于 20
let { name, age = 20 } = { name: "一个大胖子", age: 10 };
console.log(name, age); // 一个大胖子, 10
// 解构别名
// 解构时可以将对象元素重命名,此时访问name 等于空字符串 fullName 为解构值
let { name:fullName, age } = { name: "一个大胖子", age: 10 };
console.log(name, age, fullName); // "", 10, "一个大胖子"
字符串解构赋值
// 字符串解构 与数组基本相同 规则参考数组解构
let [a,b,c] = '123'
console.log(a,b,c) // 1,2,3
扩展运算符 (...)
- 如果两个对象中存在重复则后边的覆盖前边的。
- MDN
数组
- 把数组或者类数组,展开成用逗号隔开的值。
- 可以用来 合并 复制数组 等操作
let arr1 = [1,2,3,4,5,6]
console.log(...arr1) // 1 2 3 4 5 6
// 复制数组 互不影响 (数组内只有基本数据类型深拷贝,引用数据类型则是浅拷贝)
let arr2 = [...arr1]
arr2.push(1)
console.log(arr2, arr1) // [ 1, 2, 3, 4, 5, 6, 1 ] [ 1, 2, 3, 4, 5, 6 ]
// 合并数组
let arr3 = [3,4,5]
let arr4 = [...arr1, ...arr3]
console.log(arr4) // [ 1, 2, 3, 4, 5, 6, 3, 4, 5 ]
Object
- 可以用来合并 复制
Object对象。 - 直接展示对象 (
...obj) 会抛出错误!
let obj1 = {
a: '1',
b: '2',
c: '3'
}
let obj2 = {
b: '4',
a: '1',
c: '0'
}
// 存在重复key 以第二个参数为准
let obj3 = {...obj1, ...obj2}
console.log(obj3) // { a: '1', b: '4', c: '0' }
// 不存在重复值
let obj4 = {...obj1, ...obj2}
console.log(obj4) // { a: '1', b: '2', c: '3', d: '4', e: '1', f: '0' }
// 复制对象 互不影响
let obj5 = {...obj1}
obj5['b'] = '1111'
console.log(obj5, obj1) // { a: '1', b: '1111', c: '3' } { a: '1', b: '2', c: '3' }
// 解构剩余参数 需要注意的是 剩余参数一定要在最后边
const {a, ...res} = obj1
console.log(a, res) // 1 {b: '2', c: '3'}
数组遍历方法
es5 数组遍历方法
for 循环
- 支持
break continue关键字,可跳出 提前结束循环
const arr = [1,2,3,4,5,1,1,2,3,4]
for (let i=0; i< arr.length; i++) {
// 输出下标与对应的值
console.log(i, arr[i])
}
forEach
- 不支持
break continue不可跳出、不可提前结束循环 - 没有返回值, 所以不能
return - 接收三个参数 当前遍历元素,下标 ,数组本身
- 第三个参数 看起来有点迷,存在的意义或许是为了在某种情况 更方便的取到数组本身
const arr = [1,2,3,4,5,1,1,2,3,4]
arr.forEach((item, index, arrs) => {
console.log(item, index, arrs)
})
map
- 可以进行 return, 有返回值,不返回数据时,返回值的数组各项元素为
undefined - 返回一个新数组 不改变原有数组
- 接收三个参数 当前遍历元素,下标 ,数组本身
const arr = [1,2,3,4,5,1,1,2,3,4]
let ar = arr.map((item, index, arrs) => {
console.log(item, index, arrs)
return ++item
})
console.log(ar)
模拟 map 方法
- 需要注意的是 赋值给数组原型
prototype的函数不能是箭头函数,因为箭头函数的this指向不是调用方。
const arr = [1,2,3,4,5,1,1,2,3,4]
Array.prototype.testMap = function(fn) {
let index = 0,
len = this.length
res = new Array(len)
while(++index < len) {
res[index] = fn(this[index], index, this)
}
return res
}
arr.testMap(function(item, index) {
console.log(item, index)
})
filter
- 过滤 返回为真的数据
- 可以进行 return, 有返回值,不返回数据时,返回值的数组为空数组
- 返回一个新数组 不改变原有数组
- 接收三个参数 当前遍历元素,下标 ,数组本身
const arr = [1,2,3,4,5,1,1,2,3,4]
let ar = arr.filter((item, index) => {
console.log(item, index)
return item === 1
})
console.log(arr, ar) // [ 1, 2, 3, 4, 5, 1, 1, 2, 3, 4 ] [ 1, 1, 1 ]
some
- 遍历数组中是否有符合条件的数据, 返回布尔值。
- 只要有一个符合条件的就返回
true, 否则返回false - 可以进行 return, 有返回值,不返回数据时,返回值为
false - 接收三个参数 当前遍历元素,下标 ,数组本身
const arr = [1,2,3,4,5,1,1,2,3,4]
let ar = arr.some((item, index) => {
console.log(item, index)
// return item === 1
})
console.log(arr, ar) // [ 1, 2, 3, 4, 5, 1, 1, 2, 3, 4 ] true
every
- 遍历数组中是否有符合条件的数据, 返回布尔值。
- 只要有一个不符合条件的就返回
false, 否则返回true - 可以进行 return, 有返回值,不返回数据时,返回值为
false - 接收三个参数 当前遍历元素,下标 ,数组本身
const arr = [1,2,3,4,5,1,1,2,3,4]
let ar = arr.every((item, index) => {
console.log(item, index)
return item === 1
})
console.log(arr, ar) // [ 1, 2, 3, 4, 5, 1, 1, 2, 3, 4 ] false
reduce
- 接受两个参数 回调函数, 初始值。
- 回调函数接收四个参数, 上次回调返回值,当前元素,下标、当前数组
- 这个方法很神奇,但是 我用不好😁 总是想不起来应用场景。简单介绍一下。大佬总结了25个你不得不知道的数组reduce高级用法还是值得一看的。
- 初始值、上一次回调函数的返回值,这两个参数理解了 也就明白了怎么用。
// 最简单的示例 没错就是 数组累加😜
const arr = [1,2,3,4,5,1,1,2,3,4]
// 因为是累加 初始值 给个 0
// pre 是上次回调的返回值(相当于前缀和) cur 是当前的值 加入数组 是 1 2 3
// 第一次循环(0 是初始值) 0 + 1 = 1
// 第二次循环 1 + 2 = 3
// 第三次循环 3 + 3 = 6 循环结束 返回 6
let sum = arr.reduce((pre, cur, index, arr) => {
return pre + cur
},0)
console.log(arr, sum) // [ 1, 2, 3, 4, 5, 1, 1, 2, 3, 4 ] 26
for in
for in理论上是可以遍历数组的,但是存在一些问题, 它会把自定义在原型上的方法遍历出来,但是我们并不需要。
const arr = [1,2,3,4,5,1,1,2,3,4]
Array.prototype.testFor = function() {
console.log('testFor')
}
for (let index in arr) {
console.log(index, arr[index])
}
es6 数组遍历方法
find
- 返回第一个符合条件的值, 如果不存在符合条件的值 返回
undefined - 可以进行 return, 有返回值,不返回数据时,返回值为
undefined - 接收三个参数 当前遍历元素,下标 ,数组本身
const arr = [1,2,3,4,5,1,1,2,3,4]
let ar = arr.find((item, index, arrs) => {
return item === 2
})
console.log(ar) // 2
findIndex
- 返回第一个符合条件的值的下标, 如果不存在符合条件的值 返回
undefined - 可以进行 return, 有返回值,不返回数据时,返回值为
undefined - 接收三个参数 当前遍历元素,下标 ,数组本身
const arr = [1,2,3,4,5,1,1,2,3,4]
let ar = arr.findIndex((item, index, arrs) => {
console.log(item, index, arrs)
return item === 2
})
console.log(ar) // 1
for of
- 数组遍历方法,其遍历数组时 提供三个方法,
values keys entries- values 数组内每项的值, 默认调用
- keys 下标
- entries 每项的值 加 下标 是一个数组
for (let item of arr) {
console.log(item)
}
// 1234511234
for (let item of arr.values()) {
console.log(item)
}
// 输出与上边相同
// keys 输出下标
for (let item of arr.keys()) {
console.log(item)
}
// 0123456789
for (let [index, item] of arr.entries()) {
console.log(index, item)
}
数组的扩展
类数组 or 伪数组
- 一种类似数组的对象,它们不具有数组的标准方法 如
push,但是作为可迭代对象可以对它们像数组一样进行迭代,常见的伪数组对象:- 通过
document.getElementByxxx获取的数据 id除外。HTMLCollection - 通过
dom.querySelectorAll获取的数据。NodeList - 数组参数对象
Arguments
- 通过
- 创建一个 类数组对象 or 伪数组对象
let pseudoArray = {
0: '1',
1: '2',
2: '3',
length: 3
}
类数组 or 伪数组 转数组
- 类数组对象 、类数组 转对象
let pseudoArray = {
0: '1',
1: '2',
2: '3',
length: 3
}
例1 通过数组 `slice`
let arr = [].slice.call(pseudoArray)
console.log(arr) // ["1", "2", "3"]
例2 通过 es6 数组方法 `from`
let arr = Array.from(pseudoArray)
console.log(arr) // ["1", "2", "3"]
- 类数组转数组 (类数组 值 数组参数
Arguments, 通过document.getElementByxxx获取的数据等)
// 通过展开操作符
let divs = document.getElementsByTagName('div')
let arr = [...divs]
arr.push('1')
// Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,
// 其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)
let arr1 = Object.entries(divs)
arr1.push('1')
Array.of
Array.of()方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。Array.of()和Array构造函数之间的区别在于处理整数参数:Array.of(7)创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。
// 参数数量 不影响 数组生成结果 始终一致
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
// 参数数量 影响数组生成结果
Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]
Array.copyWithin
copyWithin()方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。copyWithin是一个可变方法,它不会改变this的长度length,但是会改变this本身的内容,且需要时会创建新的属性。- 接受三个参数
Array.copyWithin(target,start,end)- 参数 target、start 和 end 必须为整数。
- 如果 start 为负,则其指定的索引位置等同于 length+start,length 为数组的长度。end 也是如此。
- ie不支持
// 如果是负数,target 将从末尾开始计算。 从几开始复制几位
[1, 2, 3, 4, 5].copyWithin(-2)
// [1, 2, 3, 1, 2]
// 0 将数据复制到下标为0的位置
// 3 从数组下标3开始复制,因为没有end 结束 复制到数组结尾 即 45
// 复制两位 从下标为0处替换两位
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
// 0 将数据复制到下标为0的位置
// 3 从数组下标3开始复制,复制到数组下标4的位置 即 4
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
// -2 数组从右至左 2位 即是下标为 4
// -3 开始复制 数组从右至左 3位 即是下标为 3
// -1 结束复制 数组从右至左 1位 即是下标为 5
[1, 2, 3, 4, 5].copyWithin(-2, -3, -1)
// [1, 2, 3, 3, 4]
Array.fill
fill()方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。fill()方法是个可变方法, 它会改变调用它的this对象本身, 然后返回它, 而并不是返回一个副本。- ie不支持
const array1 = [1, 2, 3, 4];
// 从位置 2 到位置 4 用 0 填充
console.log(array1.fill(0, 2, 4));
// [1, 2, 0, 0]
// 从位置 1 填充 5
console.log(array1.fill(5, 1));
// [1, 5, 5, 5]
// 如果只有一个参数 则数组所有内容均填充为此参数
console.log(array1.fill(6));
// [6, 6, 6, 6]
Array.includes
includes()方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。includes()比较字符串和字符时是区分大小写。- 第二个参数可选,表示从那个位置 开始检索。默认等于 0 ,值为负是 等于其length + 第二个参数。
- 与
indexOf方法类似,但是indexOf无法判断Nan。 - ie不支持
const array1 = [1, 2, 3];
console.log(array1.includes(2));
// true
const pets = ["cat", "Dog", "bat"];
console.log(pets.includes("dog"));
// false
console.log(pets.includes("cat"));
// true
console.log(pets.includes("at"));
// false
Array.flat
flat()方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。flat()方法会移除数组中的空项。- 接受一个参数,表示摊平的层级 默认是1
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 移除数组中的空项
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]
Array.flatMap
flatMap()方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。- 可能在处理 二维数组转一维数组 比map 更高效吧!
var arr1 = [1, 2, 3, 4];
arr1.map(x => [x * 2]);
// [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]
// only one level is flattened
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]
函数
函数的参数
- 函数参数不能同名,会抛出错误。
- 在函数内部不能声明与函数参数同名的变量,会抛出错误。
- 函数参数不传 是
undefined
function test (x,y) {
console.log(x,y) // 1 undefined
}
test(1)
函数的默认参数
- 默认参数不传,是设置的默认值。传了 是传的值
- 请把默认参数放到函数参数的最后。你会理解为啥这样做的。
// 不传 默认参数
function test (x,y=2) {
console.log(x,y) // 1 2
}
test(1)
// 传递默认参数
function test (x,y=2) {
console.log(x,y) // 1 3
}
test(1,3)
函数参数为对象时 与解构结合
- 当函数参数 为对象时,解构后不存在的参数 为
undefined, 例1 - 当函数参数 为对象时,且参数使用了解构,若不传参数 会抛出错误, 例2
- 例2 的错误解决方法为 默认赋值一个 空对象(默认参数)例3
// 例1
function test ({x=1, y}) {
console.log(x,y) // 1 undefined
}
test({})
// 例2
function test ({x=1, y}) {
console.log(x,y)
}
test()
// 例3
function test ({x=1, y} = {}) {
console.log(x,y) // 1 undefined
}
test()
函数的length
- 函数的
length是参数的个数。 - 从参数第一位开始,到第一个指定默认参数的参数为止 如果没有指定默认参数的参数,则参数的总数和
- 不包含 指定默认参数的的参数个数, 若第一个参数 指定了默认参数 那么
length= 0
function test(a = 1, b, c, d) {}
console.log(test.length) // 0
function test(a, b = 1, c, d) {}
console.log(test.length) // 1
function test(a, b, c, d) {}
console.log(test.length) // 4
函数参数作用域
- 函数参数 存在作用域
// 参数本身 存在一个作用域 优先使用 作用域内变量 (每个参数相当于一个变量)
let a = 1;
function test(a, b = a) {
console.log(a, b); // 1 1
}
test(3);
// 若参数不存在于参数作用域,则向上查找,若 使用变量不存在 则抛出错误。
let x = 1;
function test(a, b = x) {
let x = 3;
console.log(a, b); // undefined 1
}
test();
函数的name
- 有名字的函数name 为函数名,匿名函数为
anonymous
function test() {}
console.log(test.name) // test
console.log((new Function).name) // anonymous
rest 参数
- rest 参数,剩余参数语法允许我们将一个不定数量的参数表示为一个数组。
- 如果函数的最后一个命名参数以
...为前缀,则它将成为一个由剩余参数组成的真数组,其中从0(包括)res.length(排除)的元素由传递给函数的实际参数提供。
// 当传递的参数不确定时
function test (...res) {
console.log(res) // [ 1, 2, 3, 4 ]
}
test(1,2,3,4)
// 或者 部分参数已知 存在不确定参数
function test (a, ...res) {
console.log(a,res) // 1 [ 2, 3, 4 ]
}
test(1,2,3,4)
箭头函数
- 箭头函数没有自己的
this。箭头函数会捕获其所在上下文的this值,作为自己的this值。 - 箭头函数
this不可变。call()、apply()、bind()、这些方法也 无法改变 箭头函数this的指向。 - 箭头函数 不能用
new关键字来实例化对象,不然会报错。 - 箭头函数没有
arguments对象。 - 语法
() => {}
const getData = () => {
return 'abc'
}
对象的扩展
对象的 kv 相同 可以简写
let name = '张大哥'
let data = {name: name}
// 可以简写为
let data = {name}
- 变量作为对象的 k 需要使用
[]如let data = {[name]: '1234'}
对象中的函数
// 如需使用this获取对象自身。不可使用箭头函数
let data = {
geName(){
// 函数逻辑
console.log(this)
}
}
Object.is
Object.is()用来判断两个值是否完全相等,但是与===存在差异,如Object.is(NaN,NaN)返回true而===返回false。- mdn Object.is
in 用来判断对象是否存在某个key,返回布尔值。
let c = {a:1, b:2}
console.log('c' in c) // false
console.log('a' in c) // true
对象的遍历
let data = {a:1, b:2}
for (item in data) {
console.log(item) // a, b
}
Object.keys(data) // ['a' , 'b']
Object.values(data) // [1, 2]
Object.getOwnPropertyNames(data) // ["a", "b"]
Reflect.ownKeys(data) // ["a", "b"]
Object.keys
Object.keys()方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
- 注意 在ES5里,如果此方法的参数不是对象(而是一个原始值),那么它会抛出 TypeError。在ES2015中,非对象的参数将被强制转换为一个对象。
Object.keys("foo");
// TypeError: "foo" is not an object (ES5 code)
Object.keys("foo");
// ["0", "1", "2"] (ES2015 code)
Object.values
Object.values()方法返回一个给定对象自身的所有可枚举属性值的数组。
var obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']
Object.entries
Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组。
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
Object.fromEntries
Object.fromEntries()方法把键值对列表转换为一个对象。- 序列化
Object.entries为对象
// map 转obj
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }
// k v 数组转 obj
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }
Object.getOwnPropertyDescriptors 属性描述符
Object.getOwnPropertyDescriptors()方法用来获取一个对象的所有自身属性的描述符。- 所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
value对象字段的值configurable对象的属性描述是否可以被改变 或者 属性是否可以被删除enumerable是否可以被枚举writable属性的值是否可以改变
// writable 为false 值不能被修改
const obj = {}
Reflect.defineProperty(obj, 'name', {
value: 'xiaoliu',
writable: false,
configurable: true,
enumerable: true
})
obj.name = 'xiahua'
console.log(obj)
// configurable 为false 不能被删除
const obj = {}
Reflect.defineProperty(obj, 'name', {
value: 'xiaoliu',
writable: true,
configurable: false,
enumerable: true
})
delete obj.name
console.log(obj)
// enumerable 为false 不能被枚举
const obj = {}
Reflect.defineProperty(obj, 'name', {
value: 'xiaoliu',
writable: true,
configurable: true,
enumerable: false
})
for(let key in obj){
console.log(key)
}
深拷贝与浅拷贝
- 深拷贝和浅拷贝都只针对引用数据类型。
- 浅拷贝会对对象逐个成员依次拷贝,但只复制内存地址,而不复制对象本身,新旧对象成员还是共享同一内存;
- 深拷贝会另外创建一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
- 区别:浅拷贝只复制对象的第一层属性,而深拷贝会对对象的属性进行递归复制。
浅拷贝
Object.assign
Object.assign方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。Object.assign方法只会拷贝源对象自身的并且可枚举的属性到目标对象。- 注意,
Object.assign不会在那些source对象值为null或undefined的时候抛出错误。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
深拷贝
JSON.parse(JSON.stringify())
- 用JSON.stringify()将对象转成字符串(引用数据类型变为基本数据类型 使其不存在引用关系),再用JSON.parse()把字符串解析成对象。
- 注意: JSON.stringify() 转换数据时存在到一定问题 具体请 参考mdn JSON.stringify() 描述
let data = [1,2,3,4]
let data2 = JSON.parse(JSON.stringify(data))
data.push(5)
console.log(data2)
// [1,2,3,4]
当然这里只是抛砖引玉,关于深拷贝浅拷贝的文章、例子有很多。合理的使用库是一个很不错的方法比如 lodash。
es5中的类与继承
定义类 属性及方法
// 定义一个类
function Person(name, age) {
// 示例属性
this.name = name;
this.age = age;
}
// 静态属性 不需要实例化即可使用
Person.a = '1111111111111'
console.log(Person.a);
// 静态方法
Person.log = function (info) {
// 静态方法的 this 指向类 所以无法获取 this.name
console.log(info)
}
Person.log('这是一个log 11111111111111111111 ')
// 添加原型方法
Person.prototype.logInfo = function () {
console.log("姓名:", this.name, "年龄:", this.age);
};
// 实例化对象
let p1 = new Person("xiaoliu", "18");
p1.logInfo();
类的继承
// 父类
function Animal(name) {
this.name = name
}
Animal.prototype.showName = function () {
console.log('名字是:' + this.name)
}
// 子类
function Dog(name, color) {
// 继承属性
Animal.call(this, name)
this.color = color
}
// 继承父类方法
Dog.prototype = new Animal()
Dog.prototype.constuctor = Dog
let d1 = new Dog('xiaoliu', 'orange')
console.log(d1)
d1.showName()
es6中的类与继承
定义一个类 class
// class 关键字 定义一个类
class Person {
// 实例属性
constructor(name, age) {
this.name = name;
this.age = age;
}
logName() {
console.log("输出姓名:", this.name);
}
// 目前class 不支持定义 静态属性
// 但是由提案提出 通过 static a = 1 来定义静态属性
// 静态方法
static logAge(age) {
console.log("输出年龄:", age);
}
}
// 静态方法 类似es5实现方式
Person.a = '111111111'
console.log( Person.a)
let p1 = new Person("xiaoliu", "19");
p1.logName();
Person.logAge(p1.age);
类的继承
// 继承Person父类
class Man extends Person {
constructor(name, age, sex) {
// 实例属性 name, age 继承自Person父类
super(name, age);
// sex 为Man实例属性
this.sex = sex;
}
logSex() {
console.log("性别:", this.sex);
}
}
let M1 = new Man("xiaozhang", "20", "man");
M1.logName();
M1.logSex();
Man.logAge(M1.age);
新的数据类型
BigInt 原始数据类型
BigInt是一种内置对象,可以表示任意大的整数。它提供了一种方法来表示大于253 - 1的整数。- 定义
BigInt在一个整数字面量后面加n如:10n,或者调用函数BigInt()。 - 当使用
BigInt时,带小数的运算会被取整。 BigInt值使用JSON.stringify()都会引发TypeError,因为默认情况下BigInt值不会在JSON中序列化。如果需要,可以手动实现toJSON方法。BigInt在某些方面类似于Number,但是也有几个关键的不同点:- 不能用于
Math对象中的方法。 - 不能和任何
Number实例混合运算,两者必须转换成同一种类型。 - 在两种类型来回转换时要小心,因为
BigInt变量在转换成Number变量时可能会丢失精度。
- 不能用于
const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
原始数据类型 Symbol
symbol是一种基本数据类型Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。- 每个从
Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符; - 接受一个参数,字符串类型。对symbol的描述,可用于调试但不是访问symbol本身。
Symbol('foo');
var sym1 = Symbol();
var sym2 = Symbol('foo');
var sym3 = Symbol('foo');
- 注意,
Symbol("foo")不会强制将字符串 “foo” 转换成symbol类型。它每次都会创建一个新的 symbol类型:
Symbol("foo") === Symbol("foo"); // false
数据结构 Set
Set对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。add在Set对象尾部添加一个元素。返回该Set对象。delete移除Set中与这个值相等的元素,返回一个布尔值。clear()移除Set对象内的所有元素。size返回set长度。has返回一个布尔值,表示该值在Set中存在与否。entries、keys、values均返回一个可迭代对象。
let mySet = new Set();
mySet.add(1); // Set [ 1 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add("some text"); // Set [ 1, 5, "some text" ]
let o = {a: 1, b: 2};
mySet.add(o);
mySet.add({a: 1, b: 2}); // o 指向的是不同的对象,所以没问题
mySet.has(1); // true
mySet.has(3); // false
mySet.has(5); // true
mySet.has(Math.sqrt(25)); // true
mySet.has("Some Text".toLowerCase()); // true
mySet.has(o); // true
mySet.size; // 5
mySet.delete(5); // true, 从set中移除5
mySet.has(5); // false, 5已经被移除
mySet.size; // 4, 刚刚移除一个值
// 迭代整个set
// 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
for (let item of mySet) console.log(item);
WeakSet
WeakSet只能是对象的集合,而不能是任何类型的任意值。不支持遍历(通过添加迭代器协议 可以使其支持迭代)。WeakSet对象允许你将 弱保持对象 存储在一个集合中。- 如果传入一个 可迭代对象 作为参数, 则该对象的所有迭代值都会被自动添加进生成的
WeakSet对象中。null被认为是undefined。
var ws = new WeakSet();
var foo = {};
var bar = {};
ws.add(foo);
ws.add(bar);
ws.has(foo); // true
ws.has(bar); // true
ws.delete(foo); // 从set中删除 foo 对象
ws.has(foo); // false, foo 对象已经被删除了
ws.has(bar); // true, bar 依然存在
- 注意,
foo !== bar。 尽管它们是相似的对象,但是它们不是同一个对象。因此,它们都可以被加入到set中。
数据结构 Map
Map对象保存键值对,并且能够记住键的原始插入顺序。任何值都可以作为一个键或一个值。set在Map对象尾部添加一个键值对。get获取Map对象的键值对。不存在返回undefineddelete移除Map中与这个值相等的元素,返回一个布尔值。clear()移除Map对象内的所有元素。size返回 Map 长度。has返回一个布尔值,表示该值在Map中存在与否。entries、keys、values均返回一个可迭代对象。
let myMap = new Map();
let keyObj = {};
let keyFunc = function() {};
let keyString = 'a string';
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键keyObj关联的值");
myMap.set(keyFunc, "和键keyFunc关联的值");
myMap.size; // 3
// 读取值
myMap.get(keyString); // "和键'a string'关联的值"
myMap.get(keyObj); // "和键keyObj关联的值"
myMap.get(keyFunc); // "和键keyFunc关联的值"
myMap.get('a string'); // "和键'a string'关联的值"
// 因为keyString === 'a string'
myMap.get({}); // undefined, 因为keyObj !== {}
myMap.get(function() {}); // undefined, 因为keyFunc !== function () {}
Map可以使用for..of循环来实现迭代:
let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (let [key, value] of myMap) {
console.log(key + " = " + value);
}
// 将会显示两个log。一个是"0 = zero"另一个是"1 = one"
for (let key of myMap.keys()) {
console.log(key);
}
// 将会显示两个log。 一个是 "0" 另一个是 "1"
for (let value of myMap.values()) {
console.log(value);
}
// 将会显示两个log。 一个是 "zero" 另一个是 "one"
for (let [key, value] of myMap.entries()) {
console.log(key + " = " + value);
}
// 将会显示两个log。 一个是 "0 = zero" 另一个是 "1 = one"
WeakMap
- 不支持遍历
WeakMap对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象(Object类型,Symbol不行),而值可以是任意的。
const wm1 = new WeakMap(),
wm2 = new WeakMap(),
wm3 = new WeakMap();
const o1 = {},
o2 = function(){},
o3 = window;
wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // value可以是任意值,包括一个对象或一个函数
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个WeakMap对象
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined,wm2中没有o2这个键
wm2.get(o3); // undefined,值就是undefined
wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使值是undefined)
wm3.set(o1, 37);
wm3.get(o1); // 37
wm1.has(o1); // true
wm1.delete(o1);
wm1.has(o1); // false
- 实现map一些方法
class ClearableWeakMap {
constructor(init) {
this._wm = new WeakMap(init)
}
clear() {
this._wm = new WeakMap()
}
delete(k) {
return this._wm.delete(k)
}
get(k) {
return this._wm.get(k)
}
has(k) {
return this._wm.has(k)
}
set(k, v) {
this._wm.set(k, v)
return this
}
}
字符串的扩展
字符的 Unicode表示法
- 将码点放在{}中使其不受 unicode 码点最大 ffff 的限制
console.log('\u20bb7') // ₻7 分开解析 输出 ₻ 与 7
console.log('\u{20bb7}') // 𠮷
字符串遍历 for of
let str = 'pikaqiu-'
for (item of str) {
console.log(item)
}
模板字符串
let str = "锄禾日当午 \r\n" + "汗滴禾下土。"
// 使用反引号 保留文本格式 与 str 输出相同
let str1 = `锄禾日当午,
汗滴禾下土。`
// 动态字符
let num = 1
let str2 = `今天周${num}`
console.log(str2) // 今天周1
带标签的模板字符串
function foo (a,b,c,d) {
console.log(a)
console.log(b)
console.log(c)
console.log(d)
}
const num1 = '十八'
const num2 = '二'
const num3 = '二年级'
const num4 = '一'
foo`我今年${num1}岁,读${num2}年级了,在${num3}${num4}班`
fromCodePoint
- 转换 unicode 编码
// es5 码点 不能超过 ffff
String.fromCharCode(0x20bb7) // ஷ
// es6 没有 ffff限制
String.fromCodePoint(0x20bb7) // 𠮷
includes
- 字符串是否包含指定字符 返回布尔值
let str = 'pikaqiu'
console.log(str.includes('pi')) // true
startsWith 与 endsWith
startsWith()方法用来判断当前字符串是否以另外一个给定的子字符串开头,并根据判断结果返回true或false。endsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“结尾”的,根据判断结果返回true或false。
let str = 'pikaqiu'
console.log(str.startsWith('pi')) // true
console.log(str.endsWith('iu')) // true
padStart 与 padEnd
- 两个方法定义在
String.prototype实例方法 padStart()方法用另一个字符串填充当前字符串(如果需要的话,会重复多次),以便产生的字符串达到给定的长度。从当前字符串开头开始填充。padEnd()方法会用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾开始填充。- 当前字符串填充的目标长度。如果小于当前字符串的长度,则返回当前字符串本身。(字符串长度是3 填充 <= 3 就返回字符串本身)
let te = 'xxx'
console.log(te.padStart(5, 'y')) // yyxxx
console.log(te.padEnd(5, 'y')) // xxxyy
repeat
repeat()构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。- 重复次数不能为负数。
- 重复次数必须小于 infinity,且长度不会大于最长的字符串。
let str = 'pikaqiu-'
// 10 表示重复次数
console.log(str.repeat(10))
// pikaqiu-pikaqiu-pikaqiu-pikaqiu-pikaqiu-pikaqiu-pikaqiu-pikaqiu-pikaqiu-pikaqiu-
trimStart、trimEnd trimLeft、trimRight
trim()去除前后空格trimStart()方法从字符串的开头删除空格。trimLeft()是此方法的别名。trimEnd()方法从一个字符串的末端移除空白字符。trimRight()是这个方法的别名。
const greeting = ' Hello world! ';
console.log('trimStart',greeting.trimStart())
console.log('trimEnd',greeting.trimEnd())
console.log('trimLeft',greeting.trimLeft())
console.log('trimRight',greeting.trimRight())
String.matchAll
matchAll()方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。- 如果所传参数不是一个正则表达式对象,则会隐式地使用
new RegExp(obj)将其转换为一个RegExp。 RegExp必须是设置了全局模式g的形式,否则会抛出异常TypeError。
var regexp = /t(e)(st(\d?))/g;
var str = 'test1test2';
str.match(regexp);
// Array ['test1', 'test2']
let array = [...str.matchAll(regexp)];
array[0];
// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
array[1];
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]
正则表达式的扩展
y 修饰符
y粘连修饰符 剩余的第一个开始匹配,g全局匹配
let str = 'eee-e-ee-eee-ee'
let reg = /e+/y // 修饰符 剩余的第一个开始匹配
// 从开始位置查找
console.log(reg.exec(str)) // eee
// 从匹配到 eee 后边的第一个开始匹配, 匹配不到返回 null
console.log(reg.exec(str)) // null
// 从头开始匹配
console.log(reg.exec(str)) // eee
u 修饰符
- 修饰符
u在正则表达式中提供对 Unicode 的支持。 - 4 个字节长的字符被以正确的方式处理:被看成单个的字符,而不是 2 个 2 字节长的字符。
unicode码点范围\u0000~\uffff
const str = '\uD842\uDFB7' // 表示一个字符
console.log(/^\uD842/.test(str)) // es5 true 无法正确识别 unicode 编码匹配
console.log(/^\uD842/u.test(str)) // es6 false
// .除了换行符以外的任意单个字符
console.log(/^.$/.test(str)) // false
console.log(/^.$/u.test(str)) // true
console.log(/\u{61}/.test('a')) // false
console.log(/\u{61}/u.test('a')) // true
console.log(/𠮷{2}/.test('𠮷𠮷')) // false
console.log(/𠮷{2}/u.test('𠮷𠮷')) // true
s 修饰符
dotAll属性表明是否在正则表达式中一起使用"s"修饰符(引入/s修饰符,使得.可以匹配任意单个字符)。dotAll是一个只读的属性,属于单个正则表达式实例。
具名组匹配
- 为分组提供一个名字
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
console.log(reg.exec('2021-10-09'))
const groups = reg.exec('2021-10-09').groups
console.log(groups)
后行断言
- 匹配以xxx结尾,且以xxx 或不以xxx开头的字符串
const str = 'ecmascript'
// 先行断言
console.log('先行断言 匹配以ecma 开头,以 script 结尾',str.match(/ecma(?=script)/))
console.log('先行断言 匹配以ecma 开头,不以 script 结尾',str.match(/ecma(?=!script)/))
// es9后行断言
console.log('后行断言 匹配以 script 结尾,以 ecma 开头',str.match(/(?<=ecma)script/))
console.log('后行断言 匹配以 script 结尾,不以 ecma 开头',str.match(/(?<!ecma)script/))
数值的扩展
进制
0B开头表示二进制0O开头表示八进制
// 十进制 -> 二进制
const a = 5 // 101
console.log(a.toString(2))
// 二进制 -> 十进制
const b = 101
console.log(parseInt(b, 2))
Number.isFinite
Number.isFinite()方法用来检测传入的参数是否是一个有穷数。- 与全局的
isFinite()函数相比,这个方法不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有穷的(finite),才返回true。
console.log(Number.isFinite(5)) // true
console.log(Number.isFinite(0.5)) // true
console.log(Number.isFinite(Infinity)) // false
console.log(Number.isFinite('asjdajkla')) // false
console.log(Number.isFinite(true)) // false
Number.isNaN
Number.isNaN()方法确定传递的值是否为NaN,并且检查其类型是否为Number。它是原来的全局isNaN()的更稳妥的版本。- 和全局函数
isNaN()相比,Number.isNaN()不会自行将参数转换成数字,只有在参数是值为NaN的数字时,才会返回true。
console.log(Number.isNaN(NaN)) // true
console.log(Number.isNaN(15)) // false
Number.isInteger
Number.isInteger()方法用来判断给定的参数是否为整数。- 如果被检测的值是整数,则返回
true,否则返回false。注意NaN和正负Infinity不是整数。
Number.isInteger(0); // true
Number.isInteger(1); // true
Number.isInteger(-100000); // true
Number.isInteger(0.1); // false
Number.isInteger(Math.PI); // false
Number.isInteger(Infinity); // false
Number.isInteger(-Infinity); // false
Number.isInteger("10"); // false
Number.isInteger(true); // false
Number.isInteger(false); // false
Number.isInteger([1]); // false
Math.trunc
Math.trunc()方法会将数字的小数部分去掉,仅仅是删除掉数字的小数部分和小数点,不管参数是正数还是负数。只保留整数部分。- 传入该方法的参数会被隐式转换成数字类型。
Math.trunc(13.37) // 13
Math.trunc(42.84) // 42
Math.trunc(0.123) // 0
Math.trunc(-0.123) // -0
Math.trunc("-1.123") // -1
Math.trunc(NaN) // NaN
Math.trunc("foo") // NaN
Math.trunc() // NaN
Math.sign
Math.sign()函数返回一个数字的符号, 指示数字是正数,负数还是零。- 传入的参数会被隐式转换成数字类型。
- 函数共有5种返回值, 分别是 1, -1, 0, -0, NaN. 代表的各是正数, 负数, 正零, 负零, NaN。
Math.sign(3); // 1
Math.sign(-3); // -1
Math.sign("-3"); // -1
Math.sign(0); // 0
Math.sign(-0); // -0
Math.sign(NaN); // NaN
Math.sign("foo"); // NaN
Math.sign(); // NaN
幂等运算符 **
- 求幂运算符(
**)返回将第一个操作数加到第二个操作数的幂的结果。它等效于Math.pow,不同之处在于它也接受BigInts作为操作数。
2 ** 3 // 8
3 ** 2 // 9
3 ** 2.5 // 15.588457268119896
10 ** -1 // 0.1
NaN ** 2 // NaN
Proxy
Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。- 语法
const p = new Proxy(target, handler) - 接受两个参数
target代理对象,handler对象是一个容纳一批特定属性的占位符对象。它包含有Proxy的各个捕获器(trap)。所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。
常用方法
get属性读取操作的捕捉器。set属性设置操作的捕捉器。hasin操作符的捕捉器。deleteProperty删除操作的捕捉器。ownKeysObject.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。apply函数调用操作的捕捉器。constructnew操作符的捕捉器。
get set 捕捉器示例
const handler = {
get: function (obj, prop) {
// 当对象中不存在属性名时,默认返回值为 37。
return prop in obj ? obj[prop] : 37;
},
set: function (obj, prop, value) {
// 赋值字段等于 age
if (prop === "age") {
// 是一个整数
if (!Number.isInteger(value)) {
throw new TypeError("The age is not an integer");
}
// 大于 200
if (value > 200) {
throw new RangeError("The age seems invalid");
}
}
// 为对象赋值
obj[prop] = value;
// 需要返回一个布尔值 表示成功
return true;
}
};
const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b); // 1, undefined
console.log("c" in p, p.c); // false, 37 字段 c 不存在默认返回 37
p.age = 100
console.log('age', p.age); // age 100 正确赋值
p.age = 300
console.log('age1', p.age); // The age seems invalid 抛出错误
Reflect 反射
- 反射机制指的是程序在运行时能够获取自身的信息
Reflect是一个内建的对象,用来提供方法去拦截JavaScript的操作。Reflect不是一个函数对象,所以它是不可构造的,也就是说它不是一个构造器,你不能通过new操作符去新建或者将其作为一个函数去调用Reflect对象。Reflect的所有属性和方法都是静态的。- Reflect 对象的一些方法与
Object相同, 但是二者之间存在 某些细微上的差别 . - 修改某些
Object的方法的返回结果,让其变得合理。 - 让
Object操作变成函数行为。 Reflect与Proxy的方法一一对应。
// 检测一个对象是否存在特定属性
const duck = {
name: 'Maurice',
color: 'white',
greeting: function() {
console.log(`Quaaaack! My name is ${this.name}`);
}
}
Reflect.has(duck, 'color');
// true
Reflect.has(duck, 'haircut');
// false
// 返回这个对象自身的属性
Reflect.ownKeys(duck);
// [ "name", "color", "greeting" ]
// 为对象新增一个属性
Reflect.set(duck, 'eyes', 'black');
// returns "true" if successful
// "duck" now contains the property "eyes: 'black'"
更多示例 及为什么使用 Reflect 可以查看这篇文章
Promise
Promise对象用于表示一个异步操作的最终完成 (或失败)及其结果值。- 一个
Promise必然处于以下几种状态之一且:- 待定(pending) : 初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled) : 意味着操作成功完成。
- 已拒绝(rejected) : 意味着操作失败。
- 成功或失败一旦确定 不可在更改
then 、catch
new Promise((resolve, reject) =>{
// resolve reject 只能返回一个 存在多个以第一个为准
resolve('成功!')
reject('失败!')
})
.then(res => {
// resolve 返回的值 在这里
console.log('then', res)
})
.catch(err => {
// reject 返回的值 在这里
console.log('catch', res)
})
finally
finally()方法返回一个Promise在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。- 避免了同样的语句需要在
then()和catch()中各写一次的情况。
const testPromise = new Promise((resolve, reject) =>{
// resolve reject 只能返回一个 存在多个以第一个为准
resolve('成功!')
reject('失败!')
})
.finally(() => {
// resolve || reject 都会触发这里
console.log('finally')
})
console.log(await testPromise)
Promise.resolve 静态方法
Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。- 警告:不要在解析为自身的thenable 上调用
Promise.resolve。这将导致无限递归,因为它试图展平无限嵌套的promise。
Promise.resolve("Success").then(function(value) {
console.log(value); // "Success"
}, function(value) {
// 不会被调用
});
Promise.reject 静态方法
Promise.reject()方法返回一个带有拒绝原因的Promise对象。
Promise.reject(new Error('fail')).then(function() {
// not called
}, function(error) {
console.error(error); // Stacktrace
});
Promise.all 静态方法
Promise.all()方法接收一个promise的iterable(通俗点就是可以遍历)类型(注:Array,Map,Set都属于ES6的iterable类型)的参数。返回一个promise实例- 参数中的所有
promise执行完成,返回一个由结果组成的数组。 - 只要任何一个输入的
promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
Promise.allSettled
Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。- 通俗就是说 参数中的 promise 全部执行完毕的结果 不关心成功或者失败
const promiseList = [
Promise.resolve({
code: 200,
data: [1, 2, 3],
}),
Promise.reject({
code: 500,
data: [],
}),
Promise.resolve({
code: 200,
data: [7, 8, 9],
}),
];
Promise.allSettled(promiseList)
.then((res) => {
console.log("成功", res);
})
.catch((err) => {
console.log("失败", err);
});
Promise.race(iterable)
- 接收一个可迭代的
iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
Promise.any(iterable)
- 接收一个
Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。
Generator 生成器对象
- Generator 生成器对象 由以下部分组成
function*这种声明方式(function关键字后跟一个星号)会定义一个生成器函数 (generator function) ,它返回一个Generator对象。可迭代协议允许 JavaScript 对象定义或定制它们的迭代行为,要成为可迭代对象, 一个对象必须实现@@iterator方法。 即原型上存在key为[Symbol.iterator]迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。- mdn 迭代协议,generator function 生成器函数
function *gen(){
yield 10;
x=yield 'foo';
yield x;
}
var gen_obj=gen();
console.log(gen_obj.next());// 执行 yield 10,返回 10
console.log(gen_obj.next());// 执行 yield 'foo',返回 'foo'
console.log(gen_obj.next(100));// 将 100 赋给上一条 yield 'foo' 的左值,即执行 x=100,返回 100
console.log(gen_obj.next());// 执行完毕,value 为 undefined,done 为 true
- 当在生成器函数中显式
return时,会导致生成器立即变为完成状态,即调用next()方法返回的对象的done为true。如果return后面跟了一个值,那么这个值会作为当前调用next()方法返回的 value 值。 - 可以理解为一个可以控制的函数,因为在调用
next()才会继续执行(老实说 这段写的确实水😪)。
module 模块
- 导入
import导出export default||export export default只能有一个,export可以有多个,两者可以同时存在。export default必须先定义在使用。
export 关键字
// 导出一个
export const a = 5
// 导出多个
export const a = 5
export const b = '123456'
// 或者直接导出一个对象
export {
a,
b
}
export default 关键字
// 导出一个
const a = 5
export default a
// 或者直接 export default 5
// 导出多个
const a = 5
const b = '123456'
export default {
a,
b
}
import 导入
./module为导出文件
// export 导入 名称必须相同
import { a, b } from './module'
// export default 导入 假如导出的是一个对象 mod 就是导出的对象
// 比如上边导出了 { a, b } 那么 mod 就是 { a:5, b: '123456' }
import mod from './module'
动态import
- 关键字import可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个
promise。 - 请不要滥用动态导入(只有在必要情况下采用)。静态框架能更好的初始化依赖,而且更有利于静态分析工具和
tree shaking发挥作用 - 支持
await关键字。
import('/modules/my-module.js')
.then((module) => {
// Do something with the module.
});
// `await` 关键字。
let module = await import('/modules/my-module.js');
导入别名 as 关键字
// export 导入 将 a 重命名为 aa 使用时也是 aa
import { a as aa, b } from './module'
// export default 导入 重命名为 mod
import * as mod from './module'
混合导入导出
// 两种类型导出
export const name = 'xiaohu'
const a = 5
export default a
// 两种类型导入
import mod, { name } from './module'
async 与 await
async关键字 放在函数前使其成为 异步函数 返回一个promise对象await关键字 调用 异步函数 使其同步执行
使用 async/await 重写 promise 代码
fetch('coffee.jpg')
.then(response => response.blob())
.then(myBlob => {
let objectURL = URL.createObjectURL(myBlob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
})
.catch(e => {
console.log('There has been a problem with your fetch operation: ' + e.message);
});
async function myFetch() {
let response = await fetch('coffee.jpg');
let myBlob = await response.blob();
let objectURL = URL.createObjectURL(myBlob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
}
myFetch()
.catch(e => {
console.log('There has been a problem with your fetch operation: ' + e.message);
});
错误处理 try...catch
async function myFetch() {
try {
let response = await fetch('coffee.jpg');
let myBlob = await response.blob();
let objectURL = URL.createObjectURL(myBlob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
} catch(e) {
console.log(e);
}
}
myFetch();
for await...of 异步迭代器
for await...of语句创建一个循环,该循环遍历异步可迭代对象以及同步可迭代对象。- 只能在一个async function内部使用。
for await...of不适用于不是异步可迭代的异步迭代器。Symbol.asyncIterator符号指定了一个对象的默认异步迭代器。如果一个对象设置了这个属性,它就是异步可迭代对象,可用于for await...of循环。
// 创建异步可迭代对象
var asyncIterable = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
// 自执行函数 触发异步遍历
(async function() {
for await (num of asyncIterable) {
console.log(num);
}
})();
globalThis 全局对象
globalThis提供了一个标准的方式来获取不同环境下的全局this对象(也就是全局对象自身)。不像window或者self这些属性,它确保可以在有无窗口的各种环境下正常工作。可以安心的使用globalThis,不必担心它的运行环境。为便于记忆,只需要记住,全局作用域中的this就是globalThis。
可选链操作符 ?. es2020
- 可选链操作符(
?.)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。 ?.操作符的功能类似于.链式操作符,不同之处在于,在引用为空null或者undefined的情况下不会引起错误,该表达式短路返回值是undefined。- 与函数调用一起使用时,如果给定的函数不存在,则返回
undefined。如果存在与函数同名的字段 则抛出错误 - 可选链操作符 不能用于赋值
let customer = {
name: "Carl",
details: {
age: 82,
location: "Paradise Falls" // details 的 address 属性未有定义
}
};
let customerCity = customer.details?.address?.city;
// … 可选链也可以和函数调用一起使用
let duration = vacations.trip?.getTime?.();
可选链访问数组元素
let arr = [1]
let arrayItem = arr?.[42];
console.log(arrayItem) // undefined
let arrayItem = arr?.[0];
console.log(arrayItem) // 1
空值合并运算符 ?? es2020
-
空值合并操作符(
??)是一个逻辑操作符,当左侧的操作数为null或者undefined时,返回其右侧操作数,否则返回左侧操作数。 -
与逻辑或操作符(
||)不同,逻辑或操作符会在左侧操作数为假值时返回右侧操作数。
const nullValue = null;
const emptyText = ""; // 空字符串,是一个假值,Boolean("") === false
const someNumber = 42;
const valA = nullValue ?? "valA 的默认值";
const valB = emptyText ?? "valB 的默认值";
const valC = someNumber ?? 0;
console.log(valA); // "valA 的默认值"
console.log(valB); // ""(空字符串虽然是假值,但不是 null 或者 undefined)
console.log(valC); // 42
不能与 AND 或 OR 操作符共用 否则抛出异常。
// 错误
null || undefined ?? "foo"; // 抛出 SyntaxError
true || undefined ?? "foo"; // 抛出 SyntaxError
// 正确 使用括号来显式表明运算优先级
(null || undefined ) ?? "foo"; // 返回 "foo"