• JS高级 之 ES6~ES13 新特性


    目录

    一、ECMA新描述概念

    ES6之前概念

    ES6开始

    1. 词法环境

    LexicalEnvironment

    VariableEnvironment

    2. 环境记录

    二、let 和 const

    1. 基本使用

    2. 暂时性死区

    3. 块级作用域 

    01 - 变量

    02 - 函数 

    三、let && const 和 var 的区别 

    1. 重复声明

    2. 作用域提升

    3. 挂载到window上 

    4. 块级作用域

    四、模板字符串 ` `

    1. 基本使用

    2. 标签模版字符串

    五、Symbol

    1. 基本用法

    2. 获取symbol对应的key

    3. description

    4. Symbol.for 

    六、Set 数据结构 

    1. 基本使用

    2. 数组去重

    01 - 循环

    02 - 使用Set去重

    七、weakSet 数据结构 

    1. 解析 

    2. 应用场景

    八、Map 数据结构

    基本使用

    九、weakMap 数据结构  

    十、可选链 ?. 可选链操作符 - JavaScript | MDN

    1. 直接调用 : 很危险

    2. 判断调用 : 很麻烦

    3. 链式调用 : 很可以

    十一、空值合并运算符 ?? 

    十二、逻辑赋值运算符

    1 -  | |  逻辑赋值运算符

    2 - ?? 逻辑赋值运算符

    3 - && 逻辑赋值运算符 不常用~

    十三、.at ( )

    1. 数组

    2. 字符串

    十四、FinalizationRegistry监听GC

    十五、WeakRef 弱引用

    1. 建立弱引用 : WeakRef

    2. 还原原始值 : deref


    一、ECMA新描述概念

    ES6之前概念

    • 执行上下文栈:Execution Context Stack,用于执行上下文的栈结构

    • 执行上下文:Execution Context,代码在执行之前会先创建对应的执行上下文

    • 变量对象:Variable Object,上下文关联的VO对象,用于记录函数和变量声明

    • 全局对象:Global Object,全局执行上下文关联的VO对象

    • 激活对象:Activation Object,函数执行上下文关联的VO对象

    • 作用域链:scope chain,作用域链,用于关联指向上下文的变量查找

    ES6开始

    • 基本思路是相同的,只是对于一些词汇的描述发生了改变
    • 执行上下文站和执行上下文也是相同的

    1. 词法环境

    词法环境是一种规范类型,用于在词法嵌套结构中定义关联的变量、函数等标识符

    • 一个词法环境是由环境记录(Environment Record)和一个外部词法环境(oute;r Lexical Environment)组成
      • ​​Environment Records(环境记录):这个就是变量登记的地方了
      • outer:outer 是个指向,包含本词法环境的外部词法环境,是作用域链能够链起来的关键,指向外部Lexical Environments(词法环境)的引用
    • 一个词法环境经常用于关联一个函数声明、代码块语句、try-catch语句、evel语句当它们的代码被执行时,词法环境被创建出来

    执行上下文其实会关联两个词法环境 : LexicalEnvironment 和 VariableEnvironment

    创建执行上下文时,其LexicalEnvironment和VariableEnvironment组件最初具有相同的值,他们的结构完全一样

    LexicalEnvironment

    LexicalEnvironment : 用于处理 let、const 和 function 声明的标识符

    词法环境一旦被创建,let、const声明的变量也会同时被创建出来,但是从该作用域开始的位置一直到该变量被赋值的这块区域,都不能访问这个变量,否则就会报错,这个区域也被称之为 => 暂时性死区

    VariableEnvironment

    VariableEnvironment : 用于处理 var 声明的的标识符

    变量环境一旦被创建,变量也会同时被创建出来,同时会赋值为undefined,直到被赋值

    2. 环境记录

    在这个规范中有两种主要的环境记录值 : 声明式环境记录 和 对象环境记录

    • 声明式环境记录:声明性环境记录用于定义ECMAScript语言语法元素的效果,如函数声明、变量声明和直接将标识符绑定与ECMAScript语言值关联起来的Catch子句  let age = 19
    • 对象式环境记录:对象环境记录用于定义ECMAScript元素的效果,例如WithStatement,它将标识符绑定与某些对象的属性关联起来  with(obj){}

    二、let 和 const

    let : 从直观的角度来说,let和var是没有太大的区别的,都是用于声明一个变量

    const : 

    • const关键字是constant的单词的缩写,表示常量、衡量的意思
    • 它表示保存的数据一旦被赋值,就不能被修改
    • 但是如果赋值的是引用类型,那么可以通过引用找到对应的对象,修改对象的内容

    1. 基本使用

    1. // let
    2. let message = "你好"
    3. message2 = "世界"
    4. console.log(message2)
    5. // -----------------------------------------------------
    6. // const
    7. const str = "start"
    8. str = "coder" // 报错
    9. // const赋值引用类型
    10. const info = {
    11. name: "star",
    12. age: 18
    13. }
    14. // info = {} // 报错
    15. info.name = "kobe" // 可修改

    2. 暂时性死区

    词法环境一旦被创建,let、const声明的变量也会同时被创建出来,但是从该作用域开始的位置一直到该变量被赋值的这块区域,都不能访问这个变量,否则就会报错,这个区域也被称之为 => 暂时性死区

    变量会被创建在包含他们的词法环境被实例化时,会提前创建,但是是不可以访问它们的,直到词法绑定被求值

    3. 块级作用域 

    ES6中新增了块级作用域,并且通过let、const、function、class声明的标识符是具备块级作用域的限制的

    01 - 变量

    1. console.log(message); // undefined
    2. console.log(age); // 报错,没有定义
    3. {
    4. var message = 'Hello World';
    5. let age = 18;
    6. const height = 1.88;
    7. }
    8. console.log(message); // Hello World
    9. console.log(age); // 报错,没有定义

    02 - 函数 

    函数不一样,有作用域提升,但不是像var那样的提升 

    1. foo(); // 这里是报错的
    2. {
    3. let age = 18;
    4. function foo() {
    5. console.log('foo function');
    6. }
    7. }
    8. // 这里是可以访问的
    9. foo();
    10. // --------------------------------------
    11. bar(); // 这里是报错的
    12. if (1) {
    13. function bar() {
    14. console.log('foo function');
    15. }
    16. }
    17. // 这里是可以访问的
    18. bar();
    19. // --------------------------------------
    20. baz(); // 这里是报错的
    21. let str = 1;
    22. switch (str) {
    23. case 1:
    24. function baz() {
    25. console.log('foo function');
    26. }
    27. break;
    28. }
    29. // 这里是可以访问的
    30. baz();

    三、let && const 和 var 的区别 

    1. 重复声明

    • let、const不允许重复声明
    • var 可以
    1. // var变量可以重复声明
    2. var message = "Hello World"
    3. var message = "你好, 世界"
    4. // let/const不允许变量的重复声明
    5. let address = "广州市"
    6. // let address = "上海市" // 报错
    7. const info = {}
    8. // const info = {} // 报错

    2. 作用域提升

    • let、const没有作用域提升,但是会在解析阶段被创建出来
    • var 有
    1. // 1.var声明的变量会进行作用域的提升
    2. console.log(message) // 可以使用,值为undefined
    3. var message = "Hello World"
    4. // 2.let/const声明的变量: 没有作用域提升
    5. console.log(address) // 在赋值前访问,报错
    6. let address = "广州市"

    3. 挂载到window上 

    • let、const不会挂载上去
    • var 会
    1. // 1.var定义的变量是会默认添加到window上的
    2. var a = "Hello World"
    3. var b = "广州市"
    4. console.log(window.a) // Hello World
    5. console.log(window.b) // 广州市
    6. // 2.let/const定义的变量不会添加到window上的
    7. let c = "Hello World"
    8. let d = "广州市"
    9. console.log(window.c) // undefined
    10. console.log(window.d) // undefined

    4. 块级作用域

    • let、const 有
    • var 没有
    1. console.log(message); // undefined
    2. console.log(age); // 报错,没有定义
    3. {
    4. var message = 'Hello World';
    5. let age = 18;
    6. const height = 1.88;
    7. }
    8. console.log(message); // Hello World
    9. console.log(age); // 报错,没有定义

    var所表现出来的特殊性:比如作用域提升、window全局对象、没有块级作用域等都是一些历史遗留问题,其实是JavaScript在设计之初的一种语言缺陷 

    四、模板字符串 ` `

    • 使用 `` 符号来编写字符串,称之为模板字符串
    • 在模板字符串中,我们可以通过 ${expression} 来嵌入动态的内容

    1. 基本使用

    1. const name = 'star';
    2. const age = 18;
    3. // 1. 进行数据拼接
    4. const info = `my name is ${name}, age is ${age}`;
    5. console.log(info); // my name is star, age is 18
    6. // 2. 简单表达式运算
    7. const bool = `我是成年人吗?${age > 19 ? '是' : '否'}`;
    8. console.log(bool); // 我是成年人吗?否
    9. // 3. 调用函数
    10. function foo() {
    11. return 'abc';
    12. }
    13. console.log(`my function is ${foo()}`); // my function is abc

    2. 标签模版字符串

    1. const name = 'star';
    2. const age = 18;
    3. // 标签模板字符串的用法
    4. function foo(...args) {
    5. /**
    6. * args
    7. * 第一个参数 : 被${}截取出来的字符串的数组
    8. * 后面的参数 : ${}中传递过来的变量值
    9. */
    10. console.log('参数:', args); // [ ['my name is ', ', age is ', ', height is ', ''] , "star", '18', '1.88' ]
    11. }
    12. // 使用 : 在函数名后面跟上模版字符串
    13. foo`my name is ${name}, age is ${age}, height is ${1.88}`;

    五、Symbol

    Symbol是ES6中新增的一个基本数据类型,翻译为符号

    • 在ES6之前,对象的属性名都是字符串形式,那么很容易造成属性名的冲突
    • Symbol可以用来生成一个独一无二的值,通过Symbol函数来生成的,生成后可以作为属性名
    • Symbol即使多次创建,值也是不同的:Symbol函数执行后每次创建出来的值都是独一无二的

    1. 基本用法

    1. // ES6之后可以使用Symbol生成一个独一无二的值
    2. const s1 = Symbol();
    3. // ------加入对象中,作为对象的一个key------
    4. // 加入方式一
    5. const obj = {
    6. [s1]: 'aaa'
    7. };
    8. // 加入方式二
    9. const s2 = Symbol();
    10. obj[s2] = 'bbb';
    11. // 加入方式三
    12. const s3 = Symbol();
    13. Object.defineProperty(obj, s3, {
    14. value: "ccc"
    15. })

    2. 获取symbol对应的key

    注 : Object.keys( ) 获取不到 symbol 作为key的

    1. const s1 = Symbol(); // aaa
    2. const s2 = Symbol(); // bbb
    3. const obj = {
    4. name: 'why',
    5. age: 18,
    6. [s1]: 'aaa',
    7. [s2]: 'bbb'
    8. };
    9. // 只能获取到普通的作为字符串的key
    10. console.log(Object.keys(obj)); // ['name', 'age']
    11. // 可以获取到Symbol的key
    12. console.log(Object.getOwnPropertySymbols(obj)); //  [Symbol(), Symbol()]
    13. const symbolKeys = Object.getOwnPropertySymbols(obj);
    14. // 1. 通过便利,通过Symbol的key获取值
    15. for (const key of symbolKeys) {
    16. console.log(obj[key]); // aaa bbb
    17. }
    18. // 2. 直接获取值
    19. console.log(obj[s1]); // aaa
    20. console.log(obj[s2]); // bbb

    3. description

    description : 可以在创建Symbol的同时,写入一个描述

    1. // 写入description
    2. const s3 = Symbol("ccc")
    3. // 获取description
    4. console.log(s3.description)
    5. // 就算写入相同的description,新生成的Symbol也是独一无二的
    6. const s4 = Symbol(s3.description)
    7. console.log(s3 === s4) // false

    4. Symbol.for 

    如果非要生成相同的Symbol : 如果相同的key, 通过 Symbol.for 可以生成相同的Symbol值

    1. // 这样创建,才可以
    2. const s5 = Symbol.for("ddd")
    3. const s6 = Symbol.for("ddd")
    4. console.log(s5 === s6) // true
    5. // 获取传入的key
    6. console.log(Symbol.keyFor(s5)) // ddd

    六、Set 数据结构 

    在ES6之前,存储数据的结构主要有两种:数组、对象

    在ES6中新增了另外两种数据结构:Set、Map,以及它们的另外形式WeakSet、WeakMap

    Set是一个新增的数据结构,可以用来保存数据,类似于数组,但是和数组的区别是元素不能重复 

    Set有一个非常常用的功能就是给数组去重

    Set常见的属性: size:返回Set中元素的个数;

    Set常用的方法

    • add(value):添加某个元素,返回Set对象本身;
    • delete(value):从set中删除和这个值相等的元素,返回boolean类型;
    • has(value):判断set中是否存在某个元素,返回boolean类型;
    • clear():清空set中所有的元素,没有返回值;
    • forEach(callback, [, thisArg]):通过forEach遍历set
    • Set是支持for of的遍历的

    1. 基本使用

    1. // 1. 创建Set
    2. const set = new Set();
    3. console.log(set);
    4. // 2. 添加元素
    5. set.add(10);
    6. set.add(22);
    7. set.add(35);
    8. // 相同元素,添加失败
    9. set.add(22);
    10. console.log(set); // {10, 22, 35}
    11. // 3. 删除元素
    12. set.delete(10);
    13. console.log(set); // {22, 35}
    14. // 4. 是否拥有
    15. console.log(set.has(10), set.has(35)); // false true
    16. // 5. 遍历set
    17. set.forEach((item) => console.log(item)); // 22 35
    18. // 6. set支持for...of
    19. for (const item of set) {
    20. console.log(item); // 22 35
    21. }

    2. 数组去重

    01 - 循环

    1. const names = ['abc', 'cba', 'nba', 'cba', 'nba'];
    2. const newNames = [];
    3. for (const item of names) {
    4. if (!newNames.includes(item)) {
    5. newNames.push(item);
    6. }
    7. }
    8. console.log(newNames); // ['abc', 'cba', 'nba']

    02 - 使用Set去重

    1. const names = ['abc', 'cba', 'nba', 'cba', 'nba'];
    2. // 先转成set,然后再解构成数组 或者 Array.from(new Set(names))
    3. const newNames = [...new Set(names)];
    4. console.log(newNames); // ['abc', 'cba', 'nba']

    七、weakSet 数据结构 

    Set类似的另外一个数据结构称之为 WeakSet ,也是内部元素不能重复的数据结构

    • 区别一:WeakSet中只能存放对象类型,不能存放基本数据类型
    • 区别二:WeakSet对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么GC可以对该对象进行回收

    WeakSet常见的方法:

    • add(value):添加某个元素,返回WeakSet对象本身
    • delete(value):从WeakSet中删除和这个值相等的元素,返回boolean类型
    • has(value):判断WeakSet中是否存在某个元素,返回boolean类型

    注意 : 

    • 因为WeakSet只是对对象的弱引用,如果遍历获取到其中的元素,那么有可能造成对象不能正常的销毁。
    • 所以存储到WeakSet中的对象是没办法获取的
    • 也就是,只能存储进去,不能拿出来用

    1. 解析 

    2. 应用场景

    1. // 这里使用弱引用,这样当实例对象置为空的时候,数据会自动被回收
    2. // 如果使用了强引用,那么这里还在引用着实例对象,那么实例对象就不会被回收
    3. const pWeakSet = new WeakSet();
    4. class Person {
    5. constructor() {
    6. // 每次创建对象时,把对象存储到WeakSet中
    7. pWeakSet.add(this);
    8. }
    9. running() {
    10. // 判断调用该方法的是不是实例对象
    11. if (!pWeakSet.has(this)) {
    12. console.log('Type error: 调用的方式不对');
    13. return;
    14. }
    15. console.log('running~');
    16. }
    17. }
    18. let p = new Person();
    19. p.running(); // 可正常调用 running~
    20. const runFn = p.running;
    21. runFn(); // 报错 Type error: 调用的方式不对
    22. const obj = { run: runFn };
    23. obj.run(); // 报错 Type error: 调用的方式不对

    八、Map 数据结构

    新增的数据结构是Map,用于存储映射关系

    • 对象存储映射关系只能用字符串 || Symbol 作为属性名(key)
    • 希望通过其他类型作为key,比如对象,这个时候会自动将对象转成字符串来作为key

    Map常见的属性: size:返回Map中元素的个数;

    Map常见的方法:

    • set(key, value):在Map中添加key、value,并且返回整个Map对象;
    • get(key):根据key获取Map中的value;
    • has(key):判断是否包括某一个key,返回Boolean类型;
    • delete(key):根据key删除一个键值对,返回Boolean类型;
    • clear():清空所有的元素;
    • forEach(callback, [, thisArg]):通过forEach遍历Map
    • Map是支持for of的遍历的

    基本使用

    1. const info = { name: 'star', age: 18 };
    2. const obj = { id: 999 };
    3. // 1. 创建map对象
    4. const map = new Map();
    5. // 2. 增加元素
    6. map.set(info, 'aaaa');
    7. map.set(obj, 'bbbbb');
    8. console.log(map); // Map(2) {{…} => 'aaaa', {…} => 'bbbbb'}
    9. // 3. 更改内容
    10. map.set(info, 'cccc');
    11. console.log(map); // Map(2) {{…} => 'cccc', {…} => 'bbbbb'}
    12. // 4. 获取内容
    13. console.log(map.get(info)); // cccc
    14. // 5. 删除内容
    15. map.delete(info);
    16. console.log(map); // Map(1) {{…} => 'bbbbb'}
    17. // 6. 判断是否存在
    18. console.log(map.has(info), map.has(obj)); // false true
    19. // 7. forEach方法
    20. map.forEach((item) => console.log(item)); // bbbbb
    21. // 8. for...of遍历
    22. for (const item of map) {
    23. const [key, value] = item;
    24. console.log(key, value); // {id: 999} 'bbbbb'
    25. }
    26. // 9. 清空内容
    27. map.clear();
    28. console.log(map); // Map(0) {size: 0}

    九、weakMap 数据结构  

    和Map类型的另外一个数据结构称之为WeakMap,也是以键值对的形式存在的

    和Map的区别 : 

    • 区别一:WeakMap的key只能使用对象,不接受其他的类型作为key
    • 区别二:WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象

    WeakMap常见的方法有四个:

    • set(key, value):在Map中添加key、value,并且返回整个Map对象;
    • get(key):根据key获取Map中的value;
    • has(key):判断是否包括某一个key,返回Boolean类型;
    • delete(key):根据key删除一个键值对,返回Boolean类型

    注意 : 

    • WeakMap也是不能遍历的
    • 没有forEach方法,也不支持通过for of的方式进行遍历

    十、可选链 ?. 可选链操作符 - JavaScript | MDN

    1. 直接调用 : 很危险

    1. const obj = {
    2. name: "star",
    3. friend: {
    4. name: "coder",
    5. running: function() {
    6. console.log("running~")
    7. }
    8. }
    9. }
    10. // 直接调用: 非常危险
    11. // 万一没有这个方法呢,就直接报错
    12. obj.friend.running()

    2. 判断调用 : 很麻烦

    1. const obj = {
    2. name: 'star',
    3. friend: {
    4. name: 'coder',
    5. running: function () {
    6. console.log('running~');
    7. }
    8. }
    9. };
    10. // if判断: 麻烦/不够简洁
    11. if (obj.friend && obj.friend.running) {
    12. obj.friend.running();
    13. }

    3. 链式调用 : 很可以

    1. const obj = {
    2. name: 'star',
    3. friend: {
    4. name: 'coder',
    5. running: function () {
    6. console.log('running~');
    7. }
    8. }
    9. };
    10. // 3.可选链的用法: ?.
    11. /**
    12. * obj?. => 是否有obj对象
    13. * obj?.friend?. => obj对象中是否有friend属性
    14. * obj?.friend?.running?. => obj的friend属性中是否有running这个方法
    15. */
    16. obj?.friend?.running?.();

    十一、空值合并运算符 ?? 

    1. let info = undefined; // 默认值
    2. info = null; // 默认值
    3. info = ''; // ''
    4. info = 0; // 0
    5. info = false; // false
    6. // ??: 空值合并运算符 当是null || undefined时,使用后面的
    7. info = info ?? '默认值';
    8. console.log(info);

    十二、逻辑赋值运算符

    1 -  | |  逻辑赋值运算符

    1. // 逻辑赋值运算符
    2. function foo(message) {
    3. // || 逻辑赋值运算符
    4. message = message || "默认值"
    5. // 转变
    6. message ||= "默认值"
    7. console.log(message)
    8. }

    2 - ?? 逻辑赋值运算符

    1. // 逻辑赋值运算符
    2. function foo(message) {
    3. // ?? 逻辑赋值运算符
    4. message = message ?? "默认值"
    5. // 转变
    6. message ??= "默认值"
    7. console.log(message)
    8. }

    3 - && 逻辑赋值运算符 不常用~

    1. // &&逻辑赋值运算符
    2. let obj = {
    3. name: 'star',
    4. running: function () {
    5. console.log('running~');
    6. }
    7. };
    8. obj && obj.running && obj.running();
    9. let name1 = obj && obj.name;
    10. console.log(name); // star
    11. // 强行使用
    12. obj = obj && obj.name;
    13. console.log(obj); // star
    14. // 转变
    15. obj &&= obj.name;
    16. console.log(obj); // star

    十三、.at ( )

    1. 数组

    1. const arr = ['abc', 'cab', 'ddd'];
    2. // 取到最后一位
    3. console.log(arr[arr.length - 1]); // ddd
    4. console.log(arr.at(-1)); // ddd
    5. // 取到倒数第二位
    6. console.log(arr.at(-2)); // cab

    2. 字符串

    1. const str = 'hello world';
    2. // 取到最后一位
    3. console.log(str[str.length - 1]); // d
    4. console.log(str.at(-1)); // d
    5. // 取到倒数第二位
    6. console.log(str.at(-2)); // l

    十四、FinalizationRegistry监听GC

    FinalizationRegistry :

    • FinalizationRegistry 对象可以让你在对象被垃圾回收时请求一个回调
    • 当注册的对象被回收的时候,会促发回调函数
    • 可以通过调用register方法,注册任何你想要清理回调的对象,传入该对象和所含的值
    1. let obj = { name: 'why', age: 18 };
    2. let info = { name: 'kobe', age: 30 };
    3. // 2. 如果被回收了,会促发这个回调函数
    4. const finalRegistry = new FinalizationRegistry((value) => {
    5. console.log('某一个对象被回收了:', value);
    6. });
    7. // 1. 监听注册的对象是否被回收
    8. finalRegistry.register(obj, 'why');
    9. finalRegistry.register(info, 'kobe');
    10. // 3. 赋值为null后,不会马上回收,有惰性的GC
    11. // obj = null
    12. info = null;

    十五、WeakRef 弱引用

    如果默认将一个对象赋值给另外一个引用,那么这个引用是一个强引用:

    如果希望是一个弱引用的话,可以使用WeakRef

    1. 建立弱引用 : WeakRef

    1. let info = { name: "why", age: 18 }
    2. // 建立弱引用
    3. let obj = new WeakRef(info)

    2. 还原原始值 : deref

    1. let info = { name: 'why', age: 18 };
    2. // 建立弱引用
    3. let obj = new WeakRef(info);
    4. // 从弱引用中拿到值 这样拿值!!
    5. console.log(obj.deref().name, obj.deref().age);
    6. /**
    7. * 如果这样拿值的话
    8. * const infoRef = obj.deref()
    9. * 相当于这里又是强引用了,那么使用WeakRef就变得无意义了
    10. * console.log(infoRef)
    11. */

  • 相关阅读:
    Vue:基础语法指令
    ipconfig显示的内容分析(一)网卡
    [C++](7)内存管理:new和delete
    【扩散生成模型】Diffusion Generative Models
    Node Sass version 7.0.3 is incompatible with ^4.0.0.
    MS Access 与 Excel区别与各自的优势
    SpringBoot容器化部署_Dockerfile制作镜像
    矩阵运算_矩阵的协方差矩阵/两个矩阵的协方差矩阵_求解详细步骤示例
    【开源】基于Vue+SpringBoot的音乐平台
    .NET周报 【4月第2期 2023-04-08】
  • 原文地址:https://blog.csdn.net/a15297701931/article/details/126010477