ES6~10

68 阅读8分钟

块级绑定

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.箭头函数

  1. 没有 this 、 super 、 arguments ,也没有 new.target 绑定: this 、 super 、arguments 、以及函数内部的 new.target 的值由所在的、最靠近的非箭头函数来决定( super 详见第四章) 。
  2. 不能被使用 new 调用: 箭头函数没有 [[Construct]] 方法,因此不能被用为构造函数,使用 new 调用箭头函数会抛出错误。
  3. 没有原型: 既然不能对箭头函数使用 new ,那么它也不需要原型,也就是没有prototype 属性。
  4. 不能更改 this : this 的值在函数内部不能被修改,在函数的整个生命周期内其值会保持不变。
  5. 没有 arguments 对象: 既然箭头函数没有 arguments 绑定,你必须依赖于具名参数或剩余参数来访问函数的参数。
  6. 不允许重复的具名参数: 箭头函数不允许拥有重复的具名参数,无论是否在严格模式下;而相对来说,传统函数只有在严格模式下才禁止这种重复。

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

📎index.html📎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之间的整数,任何超出此范围的整数值都可能失去精度。

  1. 要创建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' ] ]