ES7
Array.prototype.includes()
includes()
该方法用于判断一个数组是否包含指定值,如果包含,则返回 true
,否则返回 false
arr.includes(valueToFind[, fromIndex])
valueToFind
要找到的元素值。
fromIndex
可选 从fromIndex 开始搜索索引 valueToFind。如果是负值(即从末端开始向前跳) fromIndex 绝对值索引,然后搜索)。 0。
const arr = ['es6', 'es7', 'es8'] console.log(arr.includes('es7')) // true console.log(arr.includes('es7', 1)) // true console.log(arr.includes('es7', 2)) // false console.log(arr.includes("es7", -1)); // fsle console.log(arr.includes("es7", -2)); // true
使用 includes()
找字符串区分大小写。
const arr = ["es6", "es7", "es8", "a"]; console.log(arr.includes("A")); // false
使用 includes()
只能判断简单类型的数据,对于复杂类型的数据,比如对象类型的数组,二维数组,这些是无法判断的
const arr = ['es6', ['es7', 'es8'], 'es9',{
name:"jimmy"}]
console.log(arr.includes(["es7", "es8"])); // false
console.log(arr.includes({
name:"jimmy"})); // false
能识别NaN,indexOf是不能识别NaN的
const arr = ['es6', 'es7', NaN, 'es8']
console.log(arr.includes(NaN)) // true
console.log(arr.indexOf(NaN)) // -1
最后,如果只想知道某个值是否在数组中存在,而并不关心它的索引位置,建议使用includes()
,如果想获取一个值在数组中的位置,那么使用 indexOf
方法。
幂运算符 **
比如我们想求2的10次方
function pow(x, y) {
let result = 1
for (let i = 0; i < y; i++) {
result *= x
}
return result
}
console.log(pow(2, 10)) // 1024
console.log(Math.pow(2, 10)); // 1024
console.log(2 ** 10); // 1024
示例
2 ** 3 // 8
3 ** 2 // 9
3 ** 2.5 // 15.588457268119896
10 ** -1 // 0.1
NaN ** 2 // NaN
幂运算符的两个*号之间不能出现空格,否则语法会报错。
ES8
Object.values()
Object.values
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
const obj = {
name: "jimmy",
age: 18,
height: 188,
};
console.log(Object.values(obj)); // [ 'jimmy', 18, 188 ]
Object.entries()
Object.entries()
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。
const obj = {
name: "jimmy",
age: 18,
height: 188,
};
console.log(Object.entries(obj)); // [ [ 'name', 'jimmy' ], [ 'age', 18 ], [ 'height', 188 ] ]
console.log(Object.entries([1, 2, 3])); // [ [ '0', 1 ], [ '1', 2 ], [ '2', 3 ] ]
Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors()
方法用来获取一个对象的所有自身属性的描述符。为一个对象
const obj = {
name: "jimmy",
age: 18,
};
const desc = Object.getOwnPropertyDescriptors(obj);
console.log(desc);
// 打印结果
{
name: {
value: 'jimmy',
writable: true,
enumerable: true,
configurable: true
},
age: {
value: 18,
writable: true,
enumerable: true,
configurable: true
}
}
上面打印结果中的
value
表示当前对象的默认值writable
表示对象属性是否可以修改enumerable
表示当前这个属性是否可以出现在对象的枚举属性中configurable
表示当前对象的属性能否用delete删除
那这些对象的属性我们怎么设置和修改他们呢,我们可以使用es5的 Object.defineProperty()
const obj = {
};
Object.defineProperty(obj, "name", {
value: "jimmy",
writable: true,
configurable: true,
enumerable: true,
});
Object.defineProperty(obj, "age", {
value: 34,
writable: true,
configurable: true,
enumerable: true,
});
console.log(obj); // { name: 'jimmy', age: 34 }
接下来我们演示下,一些属性设置为false的情况
const obj = {
};
Object.defineProperty(obj, "name", {
value: "jimmy",
writable: false,
configurable: false,
enumerable: true,
});
console.log(obj); // { name: 'jimmy' }
obj.name = "chimmy";
console.log(obj); // { name: 'jimmy' }
delete obj.name
console.log(obj); // { name: 'jimmy' }
我们可以看到设置 writable: false和configurable: false时,对象的name对象的值不能改变和不能被删除,打印出来还是原来的对象。
设置enumerable为false时
const obj = {
};
Object.defineProperty(obj, "name", {
value: "jimmy",
writable: true,
configurable: true,
enumerable: false,
});
console.log(obj); // { }
for (let key in obj) {
console.log(key); // ""
}
当设置enumerable: false时,表示对象的属性不可被枚举,这时打印对象为空,遍历对象的键也为空。
String.prototype.padStart
把指定字符串填充到字符串头部,返回新字符串。
str.padStart(targetLength [, padString])
-
targetLength
当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。 -
padString
可选 填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的默认值为 " "
'abc'.padStart(10); // " abc"
'abc'.padStart(10, "foo"); // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0"); // "00000abc"
'abc'.padStart(1); // "abc"
日期格式化:yyyy-mm-dd的格式:
const now = new Date()
const year = now.getFullYear()
// 月份和日期 如果是一位前面给它填充一个0
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const day = (now.getDate()).toString().padStart(2, '0')
console.log(year, month, day)
console.log( `${
year}-${
month}-${
day}` ) //输入今天的日期 2021-12-31
数字替换(手机号,银行卡号等)
const tel = '18781268679'
const newTel = tel.slice(-4).padStart(tel.length, '*')
console.log(newTel) // *******5678
String.prototype.padEnd
把指定字符串填充到字符串尾部,返回新字符串。
语法同上
'abc'.padEnd(10); // "abc "
'abc'.padEnd(10, "foo"); // "abcfoofoof"
'abc'.padEnd(6, "123456"); // "abc123"
'abc'.padEnd(1); // "abc"
在JS前端我们处理时间戳的时候单位是ms毫秒,但是,后端同学返回的时间戳则不一样是毫秒,可能只有10位,以s秒为单位。所以,我们在前端处理这个时间戳的时候,保险起见,要先做一个13位的补全,保证单位是毫秒。
// 伪代码
console.log(new Date().getTime()) // 时间戳 13位的
timestamp = +String(timestamp).padEnd(13, '0')
async/await
我们都知道使用 Promise 能很好地解决回调地狱的问题,但如果处理流程比较复杂的话,那么整段代码将充斥着 then,语义化不明显,代码不能很好地表示执行流程,那有没有比 Promise 更优雅的异步方式呢?那就是async/await!我们一起来揭开它神秘的面撒吧!
前面添加了async的函数在执行后都会自动返回一个Promise对象:
function foo() {
return 'jimmy'
}
console.log(foo()) // 'jimmy'
添加async后
async function foo() {
return 'jimmy' // Promise.resolve('jimmy')
}
console.log(foo()) // Promise
async函数中使用await,那么await这里的代码就会变成同步的了,意思就是说只有等await后面的Promise执行完成得到结果才会继续下去,await就是等待。请看下面的示例:
function timeout() {
return new Promise(resolve => {
setTimeout(() => {
console.log(1)
resolve()
}, 1000)
})
}
// 不加async和await是2、1 加了是1、2
async function foo() {
await timeout()
console.log(2)
}
foo()
假如有这样一个使用场景:需要先请求 a 链接,等返回信息之后,再请求 b 链接的另外一个资源。下面代码展示的是使用 fetch 来实现这样的需求,fetch 被定义在 window 对象中,它返回的是一个 Promise 对象。
fetch('https://blog.csdn.net/')
.then(response => {
console.log(response)
return fetch('https://juejin.im/')
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
虽然上述代码可以实现这个需求,但语义化不明显,代码不能很好地表示执行流程。基于这个原因,ES8 引入了 async/await,这是 JavaScript 异步编程的一个重大改进,提供了在不阻塞主线程的情况下使用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰。
async function foo () {
try {
let response1 = await fetch('https://blog.csdn.net/')
console.log(response1)
let response2 = await fetch('https://juejin.im/')
console.log(response2)
} catch (err) {
console.error(err)
}
}
foo()
通过上面代码,你会发现整个异步处理的逻辑都是使用同步代码的方式来实现的,而且还支持 try catch 来捕获异常,这感觉就在写同步代码,所以是非常符合人的线性思维的。
-
await 只能在 async 标记的函数内部使用,单独使用会触发 Syntax error。
-
await后面需要跟异步操作,不然就没有意义,而且await后面的Promise对象不必写then,因为await的作用之一就是获取后面Promise对象成功状态传递出来的参数。
了解Async/await是非常有用的,但还有一些缺点需要考虑。
Async/await 让你的代码看起来是同步的,在某种程度上,也使得它的行为更加地同步。 await 关键字会阻塞其后的代码,直到promise完成,就像执行同步操作一样。它确实可以允许其他任务在此期间继续运行,但您自己的代码被阻塞。
这意味着您的代码可能会因为大量await的promises相继发生而变慢。每个await都会等待前一个完成,而你实际想要的是所有的这些promises同时开始处理(就像我们没有使用async/await时那样)。
有一种模式可以缓解这个问题——通过将 Promise 对象存储在变量中来同时开始它们,然后等待它们全部执行完毕。例如:
async func() {
await getFunc1()
await getFunc2()
......
// 因为getFunc()需要等待getFuc1()执行完毕后才会调用,如果多个await会花很多时间
}
async func() {
const res1 = getFunc1()
const res2 = getFunc2()
// 这里await的是getFunc1()中Promise resolve/reject后的新的promise
await res1
await res2
// 这样就可以同时执行了
}
// 方法二
async func2() {
const [res1, res2] = await Promise.allSettled([getFunc1(),getFunc2()])
}
ES9
Object Rest & Spread
在 ES9 新增 Object 的 Rest & Spread 方法,直接看下示例:
const input = {
a: 1,
b: 2,
c: 3,
}
const output = {
...input,
c: 4
}
console.log(output) // {a: 1, b: 2, c: 4}
这块代码展示了 spread 语法,可以把 input 对象的数据都拓展到 output 对象,这个功能很实用。需要注意的是,
const obj = {
x: {
y: 10 } };
const copy1 = {
...obj };
const copy2 = {
...obj };
obj.x.y = "jimmy";
console.log(copy1, copy2); // x: {y: "jimmy"} x: {y: "jimmy"}
console.log(copy1.x === copy2.x); // → true
如果属性的值是一个对象的话,该对象的引用会被拷贝,而不是生成一个新的对象。
我们再来看下 Object rest 的示例:
const input = {
a: 1,
b: 2,
c: 3
}
let {
a, ...rest } = input
console.log(a, rest) // 1 {b: 2, c: 3}
当对象 key-value 不确定的时候,把必选的 key 赋值给变量,用一个变量收敛其他可选的 key 数据,这在之前是做不到的。注意,,否则将抛出错误。
for await of
异步迭代器(for-await-of):循环等待每个Promise对象变为resolved状态才进入下一步。
我们知道 for…of 是同步运行的,看如下代码
function TimeOut(time){
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(time)
}, time)
})
}
async function test() {
let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]
for (let item of arr) {
console.log(Date.now(),item.then(console.log))
}
}
test()
打印结果为:
1653297181279 Promise {<pending>}
1653297181279 Promise {<pending>}
1653297181279 Promise {<pending>}
1000
2000
3000
上述代码证实了 for of 方法不能遍历异步迭代器,得到的结果并不是我们所期待的,于是 for await of 就粉墨登场啦!
function TimeOut(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(time)
}, time)
})
}
async