块级绑定
1.var声明与变量提升
使用var关键字声明的变量,无论其声明位置在何处,都会被视为在所属的函数顶部声明(不在函数内部的声明,则视为在全局顶部声明)。
2.块级声明
块级作用域在以下两种情况下创建:
1)函数内部声明
2)代码块( {******} )
3.let声明
let声明的变量会将作用域限制在当前代码块中,需要手动将声明置顶。
4.重复声明
在同一作用域内,let声明已经存在的变量会报错。
5.常量声明
const声明变量并且需要初始化值,和let一样是块级声明,无法被提升。
6.const声明对象
const声明对象后,可以修改变量成员。但不可以修改对象自身。
const person = {
name: "Nicholas"
};
// 工作正常
person.name = "Greg";
// 抛出错误
person = {
name: "Greg"
};
7.暂时性死区
当 JS 引擎检视接下来的代码块并发现变量声明时,它会在面对 var 的情况下将声明提升到函数或全局作用域的顶部,而面对 let 或 const 时会将声明放在暂时性死区内。任何在暂时性死区内访问变量的企图都会导致“运行时”错误( runtime error) 。只有执行到变量的声明语句时,该变量才会从暂时性死区内被移除并可以安全使用。
console.log(typeof value); // "undefined"
if (condition) {
let value = "blue";
}
当 typeof 运算符被使用时, value 并没有在暂时性死区内,因为这发生在定义 value 变量的代码块外部。这意味着此时并没有绑定 value 变量,而 typeof 仅单纯返回了"undefined"。
8.全局块级绑定
let RegExp = "Hello!";
console.log(RegExp); // "Hello!"
console.log(window.RegExp === RegExp); // false
const ncz = "Hi!";
console.log(ncz); // "Hi!"
console.log("ncz" in window);
let声明创建了 RegExp 的一个绑定,并屏蔽了全局的 RegExp。
const声明创建了 ncz 的一个绑定,但并未在全局对象上创建属性。
若想让代码能从全局对象中被访问,你仍然需要使用 var。
let和const只会屏蔽全局的变量,不会覆盖。
9.最佳实践
在默认情况下使用 const ,而只在你知道变量值需要被更改的情况下才使用 let 。
函数
1.参数默认值
与 let 声明相似,函数每个参数都会创建一个新的标识符绑定,它在初始化之前不允许被访问,否则会抛出错误。参数初始化会在函数被调用时进行,无论是给参数传递了一个值、还是使用了参数的默认值。
2.箭头函数
- 没有 this 、 super 、 arguments ,也没有 new.target 绑定: this 、 super 、arguments 、以及函数内部的 new.target 的值由所在的、最靠近的非箭头函数来决定( super 详见第四章) 。
- 不能被使用 new 调用: 箭头函数没有 [[Construct]] 方法,因此不能被用为构造函数,使用 new 调用箭头函数会抛出错误。
- 没有原型: 既然不能对箭头函数使用 new ,那么它也不需要原型,也就是没有prototype 属性。
- 不能更改 this : this 的值在函数内部不能被修改,在函数的整个生命周期内其值会保持不变。
- 没有 arguments 对象: 既然箭头函数没有 arguments 绑定,你必须依赖于具名参数或剩余参数来访问函数的参数。
- 不允许重复的具名参数: 箭头函数不允许拥有重复的具名参数,无论是否在严格模式下;而相对来说,传统函数只有在严格模式下才禁止这种重复。
Symbol.for() 全局注册
Symbol.keyFor() 获得对应的键值
ES7-ES10
1.ES7
1.1 includes
includes主要用于判断数组是否包含某个元素。
const arr = [1, 2, 3];
console.log(arr.indexOf(1) > -1); //true
console.log(arr.includes(1)); //true
1.2 **
**更简洁的幂运算
console.log(Math.pow(2, 3)); //8
console.log(2 ** 3); //8
2.ES8
2.1 Async/Await
2.1.1 一般操作异步代码有三种方式 1.嵌套回调 2.Promise 3.Generators
// async...await...写法
async function fn() {
await Promise.resolve();
console.log(1);
}
// then写法
async function add(num) {
const a = 1;
return num + a
}
console.log(add(2))
add(2).then(res => {
console.log(res)
})
2.1.2 async和await的错误捕获方法及区别
// 捕获错误
function promiseFn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("错误信息");
// resolve("result")
}, 1500)
})
}
1.错误下方的语句不会执行
// try...catch...写法
async function fn() {
try {
await promiseFn();
console.log("我不会执行~")
} catch (e) {
console.log(e)
}
console.log("异步代码执行完毕")
}
fn();
// 函数执行时catch
async function fn() {
await promiseFn();
console.log("我不会执行~");
}
fn().catch(e => {
console.log(e)
});
2.错误下方的语句会执行
async function fn() {
await promiseFn().catch(e => {
console.log(e)
});
console.log("会执行~")
}
fn();
2.1.3 多个await异步命令
function promiseFn1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("result")
}, 1000)
})
}
function promiseFn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("result")
}, 2000)
})
}
1.上一步对下一步有影响
async function fn1() {
console.time("fn1");
let res1 = await promiseFn1();
let res2 = await promiseFn2();
console.timeEnd("fn1")
}
fn1() // 约3000ms
2.每一步互不影响
async function fn2() {
console.time("fn2");
let [res1, res2] = Promise.all([promiseFn1(), promiseFn2()]);
console.timeEnd("fn2")
}
fn2() // 约2000ms
2.2 Object.values()
Object.values() 用于更快捷的遍历对象的值。
const obj = {name: "aaa", age: 4}
// ES8之前 我们需要通过遍历key,通过key来获取对应的value值
console.log(Object.keys(obj).map(key => obj[key])) //[ 'aaa', 4 ]
// ES8
console.log(Object.values(obj)) //[ 'aaa', 4 ]
2.3 Object.entries()
将对象拆解成[[key,value]]的格式
const obj = {name: "aaa", age: 4}
console.log(Object.entries(obj)) //[ [ 'name', 'aaa' ], [ 'age', 4 ] ]
console.log(Object.entries("jimmy")) //[[ '0', 'j' ],[ '1', 'i' ],[ '2', 'm' ],[ '3', 'm' ],[ '4', 'y' ]]
// 有了entries后我们就可以使用entries来遍历对象键值
for (const [key, value] of Object.entries(obj)) {
console.log(`${key} - ${value}`)
}
// name - aaa age - 4
2.4 String Padding
//1. padStart(targetLength,[padString]) 2. padEnd(targetLength,[padString])
//targetLength 目标长度 字符串过长会被阶段 字符串过短 会从左到右循环添加 padString缺省值为" "
console.log('123'.padStart(4, '20')) //2123
console.log('123'.padStart(7, '20')) //2020123
console.log('123'.padStart(7)) // 123 123前有四个空字符串
2.5 结尾允许逗号
function fn(
params1,
params2,
params3, //增加参数时git只会检测到一行改变) {
}
2.6 Object.getOwnPropertyDescriptors() 获取描述信息
const obj = {
name: "aaa",
get fn(){
return 'fn'
}
}
console.log(Object.getOwnPropertyDescriptors(obj));
//{"name":{"value":"aaa","writable":true,"enumerable":true,"configurable":true},"fn":{"enumerable":true,"configurable":true}}
2.7 SharedArrayBuffer 与 Atomics
// 给js带来了多线程的功能
// 共享内存: 把多线程引入js
// 新的全局变量 SharedArrayBuffer
// postMessage来进行通信
// 多线程 竞争 通过Atomics来进行管理
// new SharedArrayBuffer(length); length 数组缓冲区的大小 以字节byte为单位
// 例子见main.js worker.js
3.ES9
3.1 异步迭代器
Asynchronous iterator
1.什么是迭代器?
一个统一的接口去访问各种不同的数据结构,对内部的变量进行遍历
2.自己手动实现一个迭代器
const createIterator = (items) => {
const keys = Object.keys(items); //获取数据的key的数组集合
const len = keys.length; //获取数据的key的数组集合的长度
let pointer = 0;
return {
next() {
const done = pointer >= len;
const value = !done ? items[keys[pointer++]] : undefined
return {
value, done
}
}
}
}
const it1 = createIterator([1, 2, 3])
console.log(it1.next()) //{value:1, done:false}
console.log(it1.next()) //{value:2, done:false}
console.log(it1.next()) //{value:3, done:false}
console.log(it1.next()) //{value:undefined, done:true}
3.同步迭代器
Symbol.iterator 为每一个对象定义了默认的同步迭代器。该迭代器可以被 for...of 循环使用。同步迭代器返回值:next()=>{value:"",done:false}
3.1 数组原本就具有iterator接口。
const arr = [1, 2, 3]
console.log(typeof arr[Symbol.iterator]); //function
for (const val of arr) {
console.log(val) // 1 2 3
}
3.2 对象没有iterator接口。
const obj = {name: "aaa", age: 4};
console.log(typeof obj[Symbol.iterator]); // undefined
for (const val of obj) {
console.log(val)
}
// obj is not iterable
3.3 如何让对象也能被迭代?我们手动给对象加上迭代器
const obj = {name: "aaa", age: 4};
obj[Symbol.iterator] = function () {
const me = this;
const keys = Object.keys(me);
const len = keys.length
let pointer = 0;
return {
next() {
const done = pointer >= len;
const value = !done ? me[keys[pointer++]] : undefined
return {
value, done
}
}
}
}
for (const val of obj) {
console.log(val) // aaa 4
}
4.异步迭代器
Symbol.asyncIterator,异步迭代器返回值:next()=>Promise, 该迭代器可以被 for...await..of循环使用。
4.1 自己实现一个异步迭代器
4.1.1 遍历数组
const createAsyncIterator = (items) => {
const keys = Object.keys(items); //key的数组集合
const len = keys.length;
let pointer = 0;
return {
next() {
const done = pointer >= len;
const value = !done ? items[keys[pointer++]] : undefined
return Promise.resolve({
value, done
})
}
}
}
const asyncI = createAsyncIterator([1, 2, 3])
asyncI.next().then(res => {
console.log(res) //{ value: 1, done: false }
})
asyncI.next().then(res => {
console.log(res) //{ value: 2, done: false }
})
asyncI.next().then(res => {
console.log(res) //{ value: 3, done: false }
})
asyncI.next().then(res => {
console.log(res) //{ value: undefined, done: true }
})
4.1.2 遍历对象
const asyncItems = {
name: 'aaaa',
age: 4,
[Symbol.asyncIterator]() {
const me = this;
const keys = Object.keys(me);
const len = keys.length;
let pointer = 0;
return {
next() {
const done = pointer >= len;
const value = !done ? me[keys[pointer++]] : undefined
return new Promise((resolve => {
setTimeout(() => {
resolve({value, done})
}, 1000)
}
))
}
}
}
}
async function fn() {
for await (const val of asyncItems) {
console.log(val)
}
}
fn();
// 1s后输出 aaa 1s后输出4
3.2 异步生成器
Asynchronous generator
function* fn() {
console.log("正常函数我会执行")
yield 1;
yield 2;
yield 3;
console.log("执行结束")
}
const iteratorFn = fn(); //创建了一个迭代器
console.log(iteratorFn.next())
// 正常函数我会执行 { value: 1, done: false }
console.log(iteratorFn.next())
// 正常函数我会执行 { value: 1, done: false } { value: 2, done: false }
console.log(iteratorFn.next())
// 正常函数我会执行 { value: 1, done: false } { value: 2, done: false } { value: 3, done: false }
console.log(iteratorFn.next())
// 正常函数我会执行 { value: 1, done: false } { value: 2, done: false } { value: 3, done: false } 执行结束 { value: undefined, done: true }
async function* fn(){
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
const asyncI = fn();
async function fn1(){
for await (const val of asyncI){
console.log(val)
}
}
fn1();
// 1 2 3
3.3 Promise.finally()
function fn() {
return new Promise((resolve, reject) => {
resolve('value')
reject("error")
})
}
fn().then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}).finally(() => {
console.log("我总会执行")
})
3.4 Rest Spread
// ES6
function fn(a, b, ...c) {
console.log(a, b, ...c) // 1 2 3 4 5
}
fn(1, 2, 3, 4, 5)
const arr = [1, 2, 3]
console.log([11, 22, ...arr]) // [ 11, 22, 1, 2, 3 ]
// // ES9
const obj = {
name: 'aaa',
age: 4,
info: {
phone: '138****1826'
}
}
const {name, ...infos} = obj;
console.log(name, infos) // aaa { age: 4, info: { phone: '138****1826' } }
function fn({name, ...infos}) {
console.log(name, infos)
}
fn(obj) // aaa { age: 4, info: { phone: '138****1826' } }
const obj2 = {...obj, addres: "bj"};
console.log(obj2) //{ name: 'aaa', age: 4, info: { phone: '138****1826' }, addres: 'bj' }
3.5 正则表达式增强
// ES6
const reg = /[0-9]{4}-[0-9]{2}-[0-9]{2}/
const res = reg.exec(dateStr);
console.log(res) // [ '2018-08-01', index: 0, input: '2018-08-01', groups: undefined ]
// ES9
const reg = /([0-9]{4})-([0-9]{2})-([0-9]{2})/
const res = reg.exec(dateStr);
console.log(res)
//[ '2018-08-01', '2018', '08', '01', index: 0, input: '2018-08-01', groups: undefined ]
?
const reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/
const res = reg.exec(dateStr);
console.log(res.groups.year, res.groups.month, res.groups.day) // 2018 08 01
replace
const newDate = dateStr.replace(reg, `$<month>-$<day>-$<year>`)
console.log(newDate) //08-01-2018
反向断言
// 先行断言
// 获取货币符号
const str = "$123";
const reg = /\D(?=\d+)/
const result = reg.exec(str);
console.log(result[0]) //$
// 反向断言
const reg1 = /(?<=\D)\d+/
const res = reg1.exec(str);
console.log(res[0]) //123
4.ES10
4.1 BigInt
JS 中的Number类型只能安全地表示-9007199254740991 (-(2^53-1)) Number.Min_SAFE_INTEGER和9007199254740991(2^53-1)Number.MAX_SAFE_INTEGER之间的整数,任何超出此范围的整数值都可能失去精度。
-
要创建
BigInt,只需在整数的末尾追加n即可。console.log(9007199254740995n); // → 9007199254740995n console.log(9007199254740995); // → 9007199254740996
2.可以调用BigInt()构造函数
BigInt("9007199254740995");
4.2 flat() flatMap()
const arr = [1, 2, 3, [4, 5]]
const arr1 = [1, 2, 3, [4, 5, [6, 7]]]
console.log(arr.flat()) //[ 1, 2, 3, 4, 5 ]
// 指定深度
console.log(arr1.flat(2)) // [1, 2, 3, 4, 5, 6, 7]
// 指定任意深度
console.log(arr1.flat(Infinity)) // [1, 2, 3, 4, 5, 6, 7]
// 去除数组的空项
const arr2 = [1, 2, , , , , 3]
console.log(arr2.flat()) //[1, 2, 3]
const arr3 = [2, 3, 4, 5]
// ES6
console.log(arr3.map(x => [x * 2])) // [[4], [6], [8], [10]]
// ES10
console.log(arr3.flatMap(x => [x * 2])) //[ 4, 6, 8, 10 ]
4.3 Object.fromEntries()
Object.fromEntries()返回对象自身可枚举的键值数组
for...in 除自身外也会循环原型链中的属性
const map = new Map([['name', 'aaa'], ['age', '4']]);
console.log(Object.fromEntries(map)) // { name: 'aaa', age: '4' }
Object.entries() fromEntries()的反操作 返回数组
const obj = {name: 'aaa', age: '4'};
console.log(Object.entries(obj)) // [ [ 'name', 'aaa' ], [ 'age', '4' ] ]