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. 基本使用

// let
let message = "你好"
message2 = "世界"
console.log(message2)

// -----------------------------------------------------

// const
const str = "start"
str = "coder" // 报错

// const赋值引用类型
const info = {
  name: "star",
  age: 18
}
// info = {} // 报错
info.name = "kobe" // 可修改

2. 暂时性死区

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

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

3. 块级作用域 

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

01 - 变量

console.log(message); // undefined
console.log(age); // 报错,没有定义
{
  var message = 'Hello World';
  let age = 18;
  const height = 1.88;
}
console.log(message); // Hello World
console.log(age); // 报错,没有定义

02 - 函数 

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

foo(); // 这里是报错的
{
  let age = 18;
  function foo() {
    console.log('foo function');
  }
}
// 这里是可以访问的
foo();

// --------------------------------------

bar(); // 这里是报错的
if (1) {
  function bar() {
    console.log('foo function');
  }
}
// 这里是可以访问的
bar();

// --------------------------------------

baz(); // 这里是报错的
let str = 1;
switch (str) {
  case 1:
    function baz() {
      console.log('foo function');
    }
    break;
}
// 这里是可以访问的
baz();

三、let && const 和 var 的区别 

1. 重复声明

  • let、const不允许重复声明
  • var 可以
// var变量可以重复声明
var message = "Hello World"
var message = "你好, 世界"


// let/const不允许变量的重复声明
let address = "广州市"
// let address = "上海市" // 报错
const info = {}
// const info = {} // 报错

2. 作用域提升

  • let、const没有作用域提升,但是会在解析阶段被创建出来
  • var 有
// 1.var声明的变量会进行作用域的提升
console.log(message) // 可以使用,值为undefined
var message = "Hello World"

// 2.let/const声明的变量: 没有作用域提升

console.log(address) // 在赋值前访问,报错
let address = "广州市"

3. 挂载到window上 

  • let、const不会挂载上去
  • var 会
// 1.var定义的变量是会默认添加到window上的
var a = "Hello World"
var b = "广州市"

console.log(window.a) // Hello World
console.log(window.b) // 广州市

// 2.let/const定义的变量不会添加到window上的
let c = "Hello World" 
let d = "广州市"

console.log(window.c) // undefined
console.log(window.d) // undefined

4. 块级作用域

  • let、const 有
  • var 没有
console.log(message); // undefined
console.log(age); // 报错,没有定义
{
  var message = 'Hello World';
  let age = 18;
  const height = 1.88;
}
console.log(message); // Hello World
console.log(age); // 报错,没有定义

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

四、模板字符串 ` `

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

1. 基本使用

const name = 'star';
const age = 18;
// 1. 进行数据拼接
const info = `my name is ${name}, age is ${age}`;
console.log(info); // my name is star, age is 18

// 2. 简单表达式运算
const bool = `我是成年人吗?${age > 19 ? '是' : '否'}`;
console.log(bool); // 我是成年人吗?否

// 3. 调用函数
function foo() {
  return 'abc';
}
console.log(`my function is ${foo()}`); // my function is abc

2. 标签模版字符串

const name = 'star';
const age = 18;

// 标签模板字符串的用法
function foo(...args) {
  /**
   * args
   * 第一个参数 : 被${}截取出来的字符串的数组
   * 后面的参数 : ${}中传递过来的变量值
   */
  console.log('参数:', args); // [ ['my name is ', ', age is ', ', height is ', ''] , "star", '18', '1.88' ]
}

// 使用 : 在函数名后面跟上模版字符串
foo`my name is ${name}, age is ${age}, height is ${1.88}`;

五、Symbol

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

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

1. 基本用法

// ES6之后可以使用Symbol生成一个独一无二的值
const s1 = Symbol();

// ------加入对象中,作为对象的一个key------

// 加入方式一
const obj = {
  [s1]: 'aaa'
};

// 加入方式二
const s2 = Symbol();
obj[s2] = 'bbb';

// 加入方式三
const s3 = Symbol();
Object.defineProperty(obj, s3, {
  value: "ccc"
})

2. 获取symbol对应的key

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

const s1 = Symbol(); // aaa
const s2 = Symbol(); // bbb
const obj = {
  name: 'why',
  age: 18,
  [s1]: 'aaa',
  [s2]: 'bbb'
};

// 只能获取到普通的作为字符串的key
console.log(Object.keys(obj)); // ['name', 'age']
// 可以获取到Symbol的key
console.log(Object.getOwnPropertySymbols(obj)); //  [Symbol(), Symbol()]

const symbolKeys = Object.getOwnPropertySymbols(obj);
// 1. 通过便利,通过Symbol的key获取值
for (const key of symbolKeys) {
  console.log(obj[key]); // aaa bbb
}
// 2. 直接获取值
console.log(obj[s1]); // aaa
console.log(obj[s2]); // bbb

3. description

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

// 写入description
const s3 = Symbol("ccc")
// 获取description
console.log(s3.description)

// 就算写入相同的description,新生成的Symbol也是独一无二的
const s4 = Symbol(s3.description)
console.log(s3 === s4) // false

4. Symbol.for 

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

// 这样创建,才可以
const s5 = Symbol.for("ddd")
const s6 = Symbol.for("ddd")
console.log(s5 === s6) // true

// 获取传入的key
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. 创建Set
const set = new Set();
console.log(set);

// 2. 添加元素
set.add(10);
set.add(22);
set.add(35);
// 相同元素,添加失败
set.add(22);
console.log(set); // {10, 22, 35}

// 3. 删除元素
set.delete(10);
console.log(set); // {22, 35}

// 4. 是否拥有
console.log(set.has(10), set.has(35)); // false true

// 5. 遍历set
set.forEach((item) => console.log(item)); // 22 35

// 6. set支持for...of
for (const item of set) {
  console.log(item); // 22 35
}

2. 数组去重

01 - 循环

const names = ['abc', 'cba', 'nba', 'cba', 'nba'];
const newNames = [];
for (const item of names) {
  if (!newNames.includes(item)) {
    newNames.push(item);
  }
}
console.log(newNames); // ['abc', 'cba', 'nba']

02 - 使用Set去重

const names = ['abc', 'cba', 'nba', 'cba', 'nba'];
// 先转成set,然后再解构成数组  或者 Array.from(new Set(names))
const newNames = [...new Set(names)];
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. 应用场景

// 这里使用弱引用,这样当实例对象置为空的时候,数据会自动被回收
// 如果使用了强引用,那么这里还在引用着实例对象,那么实例对象就不会被回收
const pWeakSet = new WeakSet();
class Person {
  constructor() {
    // 每次创建对象时,把对象存储到WeakSet中
    pWeakSet.add(this);
  }

  running() {
    // 判断调用该方法的是不是实例对象
    if (!pWeakSet.has(this)) {
      console.log('Type error: 调用的方式不对');
      return;
    }
    console.log('running~');
  }
}

let p = new Person();
p.running(); // 可正常调用 running~
const runFn = p.running;
runFn(); // 报错 Type error: 调用的方式不对
const obj = { run: runFn };
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的遍历的

基本使用

const info = { name: 'star', age: 18 };
const obj = { id: 999 };

// 1. 创建map对象
const map = new Map();
// 2. 增加元素
map.set(info, 'aaaa');
map.set(obj, 'bbbbb');
console.log(map); // Map(2) {{…} => 'aaaa', {…} => 'bbbbb'}

// 3. 更改内容
map.set(info, 'cccc');
console.log(map); // Map(2) {{…} => 'cccc', {…} => 'bbbbb'}

// 4. 获取内容
console.log(map.get(info)); // cccc

// 5. 删除内容
map.delete(info);
console.log(map); // Map(1) {{…} => 'bbbbb'}

// 6. 判断是否存在
console.log(map.has(info), map.has(obj)); // false true

// 7. forEach方法
map.forEach((item) => console.log(item)); // bbbbb

// 8. for...of遍历
for (const item of map) {
  const [key, value] = item;
  console.log(key, value); // {id: 999} 'bbbbb'
}

// 9. 清空内容
map.clear();
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. 直接调用 : 很危险

const obj = {
  name: "star",
  friend: {
    name: "coder",
    running: function() {
      console.log("running~")
    }
  }
}

// 直接调用: 非常危险
// 万一没有这个方法呢,就直接报错
obj.friend.running()

2. 判断调用 : 很麻烦

const obj = {
  name: 'star',
  friend: {
    name: 'coder',
    running: function () {
      console.log('running~');
    }
  }
};

// if判断: 麻烦/不够简洁
if (obj.friend && obj.friend.running) {
  obj.friend.running();
}

3. 链式调用 : 很可以

const obj = {
  name: 'star',
  friend: {
    name: 'coder',
    running: function () {
      console.log('running~');
    }
  }
};

// 3.可选链的用法: ?.
/**
 * obj?.    =>    是否有obj对象
 * obj?.friend?.    =>    obj对象中是否有friend属性
 * obj?.friend?.running?.    =>    obj的friend属性中是否有running这个方法
 */
obj?.friend?.running?.();

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

let info = undefined; // 默认值
info = null; // 默认值

info = ''; // ''
info = 0; // 0
info = false; // false

// ??: 空值合并运算符  当是null || undefined时,使用后面的

info = info ?? '默认值';
console.log(info);

十二、逻辑赋值运算符

1 -  | |  逻辑赋值运算符

// 逻辑赋值运算符
function foo(message) {
  // || 逻辑赋值运算符
  message = message || "默认值"
  // 转变
  message ||= "默认值"

  console.log(message)
}

2 - ?? 逻辑赋值运算符

// 逻辑赋值运算符
function foo(message) {
  // ?? 逻辑赋值运算符
  message = message ?? "默认值"
  // 转变
  message ??= "默认值"

  console.log(message)
}

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

// &&逻辑赋值运算符
let obj = {
  name: 'star',
  running: function () {
    console.log('running~');
  }
};
obj && obj.running && obj.running();
let name1 = obj && obj.name;
console.log(name); // star

// 强行使用
obj = obj && obj.name;
console.log(obj); // star
// 转变
obj &&= obj.name;
console.log(obj); // star

十三、.at ( )

1. 数组

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

2. 字符串

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

十四、FinalizationRegistry监听GC

FinalizationRegistry :

  • FinalizationRegistry 对象可以让你在对象被垃圾回收时请求一个回调
  • 当注册的对象被回收的时候,会促发回调函数
  • 可以通过调用register方法,注册任何你想要清理回调的对象,传入该对象和所含的值
let obj = { name: 'why', age: 18 };
let info = { name: 'kobe', age: 30 };

// 2. 如果被回收了,会促发这个回调函数
const finalRegistry = new FinalizationRegistry((value) => {
  console.log('某一个对象被回收了:', value);
});

// 1. 监听注册的对象是否被回收
finalRegistry.register(obj, 'why');
finalRegistry.register(info, 'kobe');

// 3. 赋值为null后,不会马上回收,有惰性的GC
// obj = null
info = null;

十五、WeakRef 弱引用

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

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

1. 建立弱引用 : WeakRef

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

2. 还原原始值 : deref

let info = { name: 'why', age: 18 };
// 建立弱引用
let obj = new WeakRef(info);

// 从弱引用中拿到值   这样拿值!!
console.log(obj.deref().name, obj.deref().age);

/**
 * 如果这样拿值的话
 * const infoRef = obj.deref()
 * 相当于这里又是强引用了,那么使用WeakRef就变得无意义了
 * console.log(infoRef)
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值