前端开发中常见的ES6技术细节分享一

var、let、const之间有什么区别?

var: 在ES5中,顶层对象的属性和全局变量是等价的,用var声明的变量既是全局变量,也是顶层变量​
注意:顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象

var a = 10;​
console.log(window.a) // 10

使用var声明的变量存在变量提升的情况

console.log(a) // undefined​
var a = 20

在编译阶段,编译器会将其变成以下执行

var a​
console.log(a)​
a = 20

使用var,我们能够对一个变量进行多次声明,后面声明的变量会覆盖前面的变量声明

var a = 20 ​
var a = 30​
console.log(a) // 30

在函数中使用使用var声明变量时候,该变量是局部的

var a = 20function change(){​var a = 30}​
change()​
console.log(a) // 20 

而如果在函数内不使用var,该变量是全局的

var a = 20function change(){​a = 30}​
change()​
console.log(a) // 30 

let : let是ES6新增的命令,用来声明变量​
用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效

{let a = 20}​
console.log(a) // ReferenceError: a is not defined.

不存在变量提升

console.log(a) // 报错ReferenceError​
let a = 2

这表示在声明它之前,变量a是不存在的,这时如果用到它,就会抛出一个错误​
只要块级作用域内存在let命令,这个区域就不再受外部影响

var a = 123if (true) {​a = 'abc' // ReferenceError​let a;}

使用let声明变量前,该变量都不可用,也就是大家常说的“暂时性死区”​
最后,let不允许在相同作用域中重复声明

let a = 20let a = 30​
// Uncaught SyntaxError: Identifier 'a' has already been declared

注意的是相同作用域,下面这种情况是不会报错的

let a = 20{let a = 30}

因此,我们不能在函数内部重新声明参数

function func(arg) {let arg;}​
func()​
// Uncaught SyntaxError: Identifier 'arg' has already been declared

const: const声明一个只读的常量,一旦声明,常量的值就不能改变

const a = 1​
a = 3// TypeError: Assignment to constant variable.

这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值

const a;// SyntaxError: Missing initializer in const declaration

如果之前用var或let声明过变量,再用const声明同样会报错

var a = 20let b = 20const a = 30const b = 30// 都会报错

const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动​
对于简单类型的数据,值就保存在变量指向的那个内存地址,因此等同于常量​
对于复杂类型的数据,变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的,并不能确保改变量的结构不变

const foo = {};​
​
// 为 foo 添加一个属性,可以成功​
foo.prop = 123;​
foo.prop // 123​// 将 foo 指向另一个对象,就会报错​
foo = {}; // TypeError: "foo" is read-only

使用区别:
var、let、const三者区别可以围绕下面五点展开:​

  • 变量提升​
  • 暂时性死区​
  • 块级作用域​
  • 重复声明​
  • 修改声明的变量​
  • 使用

变量提升​
var 声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined​
// 2023.4.25 更新​
let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错​
let / const 不存在变量提升是不完全正确的,只能说由于暂时性死区的存在使得我们无法直观感受到变量提升的效果。​
let 和 const 定义的变量都会被提升,但是不会被初始化,不能被引用,不会像var定义的变量那样,初始值为undefined。​
当进入let变量的作用域时,会立即给它创建存储空间,但是不会对它进行初始化。​
变量的赋值可以分为三个阶段:

  • 创建变量,在内存中开辟空间​
  • 初始化变量,将变量初始化为undefined​
  • 真正赋值

关于let、var和function:​

  • let 的「创建」过程被提升了,但是初始化没有提升。​
  • var 的「创建」和「初始化」都被提升了。​
  • function 的「创建」「初始化」和「赋值」都被提升了。
// var​
console.log(a)  // undefined​
var a = 10​
​
// let ​
console.log(b)  // Cannot access 'b' before initialization​
let b = 10​
​
// const​
console.log(c)  // Cannot access 'c' before initialization​
const c = 10

暂时性死区​
var不存在暂时性死区​
let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量

// var​
console.log(a)  // undefined​
var a = 10​
​
// let​
console.log(b)  // Cannot access 'b' before initialization​
let b = 10​
​
// const​
console.log(c)  // Cannot access 'c' before initialization​
const c = 10

块级作用域​
var不存在块级作用域​
let和const存在块级作用域

// var​
{var a = 20}​
console.log(a)  // 20​// let​
{let b = 20}​
console.log(b)  // Uncaught ReferenceError: b is not defined​// const​
{const c = 20}​
console.log(c)  // Uncaught ReferenceError: c is not defined

重复声明
var允许重复声明变量​
let和const在同一作用域不允许重复声明变量

// var​
var a = 10​
var a = 20 // 20​
​
// let​
let b = 10let b = 20 // Identifier 'b' has already been declared​
​
// const​
const c = 10​
const c = 20 // Identifier 'c' has already been declared

修改声明的变量

// var​
var a = 10​
a = 20​
console.log(a)  // 20​
​
//let​
let b = 10​
b = 20​
console.log(b)  // 20​
​
// const​
const c = 10​
c = 20​
console.log(c) // Uncaught TypeError: Assignment to constant variable

使用​
能用const的情况尽量使用const,其他情况下大多数使用let,避免使用var

ES6新特性

关于ES6和JavaScript的关系​
1、ES6是对于ES2015+的俗称,也可以说是通常叫法,那么,ES6是什么呢?​
ES 全称是ECMAScript,它是JavaScript基础构建的一种语言,JavaScript正是建立在ECMAScript语言的基础规范中建立使用的,那么,ECMAScript的使用,对于JavaScript至关重要!​
在我的理解中,ECMAScript是一种语言层面的东西,它只是定义了JavaScript以及在它基础之上建立的其他语言的语法规范,而JavaScript的语言,更关于一种平台性质在其中。​
JavaScript包括 ECMAScript、DOM、BOM三个组成部分,DOM和BOM是web API提供的接口或者是JavaScript和浏览器之间进行交互的部分,实质就是操纵文档元素,进行展示布局,而ECMAScript在JavaScript中其中语法的作用,它不会去跟文档有直接的关系,但是他的数据处理完成后会通过web API展示在文档中。​
ES6新特性的分类​
新特性主要归为四大类:​

  • 解决原有语法上的一些不足​
    比如let 和 const 的块级作用域​
  • 对原有语法进行增强​
    比如解构、展开、参数默认值、模板字符串​
  • 全新的对象、全新的方法、全新的功能​
    比如promise、proxy、object的assign、is​
  • 全新的数据类型和数据结构​
    比如symbol、set、map

1. let、const 块级作用域以及和 var 的区别 可参考上边的描述

2.解构-快速提取数组/对象中的元素

  • 数组解构​
  • 单独解构-根据数组索引,将数组解构成单独的元素
const arr = [1, 2, 3]​
​
const [a, b, c] = arr​
console.log(a, b, c) //1,2,3​
const [, , d] = arr​
console.log(d) //3

默认值,解构时可以给变量设置默认值,数组没有这个元素的话

const arr = [1, 2, 3]​
​
const [, , , defaultVal = '4'] = arr​
console.log('设置默认值', defaultVal)

剩余解构-用 “…+变量名” 解构剩余参数到新数组,只能用一次

const arr = [1, 2, 3]​
​
const [e, ...rest] = arr​
console.log(rest) //[2, 3]

实例应用

// 拆分字符串​
const str = 'xiaobai/18/200'const strArr = str.split('/')const [, age] = strArr​
console.log(age) //18

对象解构​

  • 单个/多个解构-跟数组解构差不多
const obj = { name: 'xiaohui', age: 18, height: undefined }const { name, age } = obj​
console.log(name, age) // 'xiaohui', 18

解构+重命名-给解构出来的变量重命名

const obj = { name: 'xiaohui', age: 18, height: undefined }const { name: objName } = obj​
console.log(objName)

默认值-给解构变量设置默认值

const obj = { name: 'xiaohui', age: 18, height: undefined }​
const { next = 'default' } = obj​
console.log(next)

3.模板字符串
用法:使用``将字符串包裹起来​
功能:可以换行、插值、使用标签函数进行字符串操作​
示例:

  • 换行/插值
//换行​
const str = `fdsjak​fdsa`​
console.log(str)​
​
// 插值​
const strs = `random: ${Math.random()}`​
console.log(strs)

标签函数-可以对模板字符串的字符串和插值进行处理和过滤等操作

/**​* 字符串模板函数​* @param {array} strs 以插值为分隔符组成的字符串数组​* @param {string} name 插值的value,有多少个就会传入多少个​*/const tagFunc = (strs, name, gender) => {const [str1, str2, str3] = strs​const genderParsed = gender == '1' ? '男' : '女'// 可以在此做过滤,字符串处理,多语言等操作​return str1 + name + str2 + str3 + genderParsed​
0-0-0-}​
​
// 带标签的模板字符串,​
const person = {​name: 'xiaohui',​gender: 1,}// 返回值为标签函数的返回值​
const result = tagFunc`my name is ${person.name}.gender is ${person.gender}`​
console.log(result) //my name is xiaohui.gender is 男

4. 字符串扩展方法​

  • includes-是否包含​
  • startsWith-是否以什么开始​
  • endsWith-是否以什么结束
const str = 'abcd'​
​
console.log(str.includes('e')) //false​
console.log(str.startsWith('a')) //true​
console.log(str.endsWith('a')) //false

5.参数默认值&剩余参数
给函数形参设置默认值

// 带默认参数的形参一般放在后面,减少传参导致的错误几率​
const defaultParams = function (name, age = 0) {return [age, name]}​
console.log(defaultParams(1))

使用…rest 形式设置剩余形参,支持无限参数

// 剩余参数,转化成数组​
const restParams = function (...args) {​console.log(args.toString()) //1, 2, 3, 4, 5}​
​
restParams(1, 2, 3, 4, 5)

6.展开数组

const arr = [1, 2, 3]​
​
console.log(...arr)// 等价于es5中以下写法​
console.log.apply(console, arr)

7.箭头函数
特性&优势:​
1、简化了函数的写法​
2、没有 this 机制,this 继承自上一个函数的上下文,如果上一层没有函数,则指向 window​
3、作为异步回调函数时,可解决 this 指向问题

const inc = (n) => n + 1​
console.log(inc(100))​
​
const obj = {​name: 'aa',func() {setTimeout(() => {​console.log(this.name) //aa​}, 0)setTimeout(function () {​console.log(this.name) //undefined​}, 0)},}​
obj.func()

8.对象字面量增强

  • 同名属性可以省略 key:value 形式,直接 key,​
  • 函数可以省略 key:value 形式​
  • 可以直接 func(),​
  • 可以使用计算属性,比如:{[Math.random()]: value}
/**​* 1、增强了对象字面量:​* 1,同名属性可以省略key:value形式,直接key,​* 2,函数可以省略key:value形式​* 3,可以直接func(),​* 4,可以使用计算属性,比如:{[Math.random()]: value}​*/const arr = [1, 2, 3]const obj = {​arr,func() {​console.log(this.arr)},[Math.random()]: arr,}​
​
console.log(obj)

9.Object.assign(target1, target2, targetN)-复制/合并对象

/**​* Object.assign(target1, target2, ...targetn)​* 后面的属性向前面的属性合并​* 如果target1是空对象,可以创建一个全新对象,而不是对象引用​*/const obj1 = {​a: 1,​b: 2,}const obj2 = {​a: 1,​b: 2,}​
​
const obj3 = Object.assign({}, obj1)​
obj3.a = 5​
console.log(obj3, obj2, obj1)

10.Object.is(value1, value2)​
作用:比较两个值是否相等

console.log(NaN === NaN) //false​
console.log(Object.is(NaN, NaN)) //true​
console.log(0 === -0) // true​
console.log(Object.is(0, -0)) //false​
console.log(Object.is(1, 1)) //true

11.Proxy(object, handler)
作用:​

  • 代理一个对象的所有,包括读写操作和各种操作的监听​

用法:

const P = {​n: 'p',​a: 19,}​
​
const proxy = new Proxy(P, {get(target, property) {​console.log(target, property)return property in target ? target[property] : null},defineProperty(target, property, attrs) {​console.log(target, property, attrs)//   throw new Error('不允许修改')​},deleteProperty(target, property) {​console.log(target, property)delete target[property]},set(target, property, value) {​target[property] = value​},})​
​
proxy.c = 100​
console.log('pp', P)

优势:​
拥有很多 defineProperty 没有的属性方法,比如:​

  • handler.getPrototypeOf() —Object.getPrototypeOf 方法的监听器​
  • handler.setPrototypeOf() —Object.setPrototypeOf 方法的监听器。​
  • handler.isExtensible() —Object.isExtensible 方法的监听器。​
  • handler.preventExtensions() —Object.preventExtensions 方法的监听器。​
  • handler.getOwnPropertyDescriptor() —Object.getOwnPropertyDescriptor 方法的监听器。​
  • handler.defineProperty() —Object.defineProperty 方法的监听器。​
  • handler.has() —in 操作符的监听器。​
  • handler.get() —属性读取操作的监听器。​
  • handler.set() —属性设置操作的监听器。​
  • handler.deleteProperty() —delete 操作符的监听器​
  • handler.ownKeys() —Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的监听器。​
  • handler.apply() —函数调用操作的监听器。​
  • handler.construct() —new 操作符的监听器。​
    对数组的监视更方便​
    以非侵入的方式监管对象的读写

12.Reflect
作用:​
集成 Object 操作的所有方法,统一、方便,具体方法如下:​
用于对对象的统一操作,集成 Object 相关的所有方法​
1、apply:类似 Function.prototype.apply​
2、Reflect.construct()​
对构造函数进行 new 操作,相当于执行 new target(…args)。​
3、Reflect.defineProperty()​
和 Object.defineProperty() 类似。​
4、Reflect.deleteProperty()​
作为函数的 delete 操作符,相当于执行 delete target[name]。​
5、Reflect.get()​
获取对象身上某个属性的值,类似于 target[name]。​
6、Reflect.getOwnPropertyDescriptor()​
类似于 Object.getOwnPropertyDescriptor()。​
7、Reflect.getPrototypeOf()​
类似于 Object.getPrototypeOf(), 获取目标对象的原型。​
8、Reflect.has()​
判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。​
9、Reflect.isExtensible()​
类似于 Object.isExtensible().判断对象是否可扩展,可以添加额外属性​
Object.seal(封闭对象), Object.freeze(冻结对象)是不可扩展的​
10、Reflect.ownKeys()​
返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受 enumerable 影响).​
11、Reflect.preventExtensions()​
类似于 Object.preventExtensions()。返回一个 Boolean。​
12、Reflect.set()​
将值分配给属性的函数。返回一个 Boolean,如果更新成功,则返回 true, 反之返回 false。​
13、Reflect.setPrototypeOf()​
类似于 Object.setPrototypeOf()。

const obj = {​name: 'reflect',}​
Reflect.preventExtensions(obj) //禁止扩展​
console.log(Reflect.set(obj, 'age', 'xiaobai')) //false​
console.log(obj) //{ name: 'reflect' }​
console.log(Reflect.isExtensible(obj, 'name')) //false​
console.log(Reflect.ownKeys(obj)) //[ 'name' ]

13.Promise

14.class&静态方法&继承
使用 class 关键字定义类

class Person {constructor(props) {this.props = props​}}

方法​

  • 实例方法,需要实例化之后才能调用,this 指向实例​
  • 静态方法,用 static 修饰符修饰,可以直接通过类名调用,不需要实例化,this 不指向实例,而是指向当前类
class Person {constructor(props) {this.props = props​}// 实例方法​eat() {}// 静态方法​static run() {}}// 调用静态方法​
Person.run()const person = new Person('props')// 调用实例方法​
person.eat()

继承:子类使用 extends 关键字实现继承,可以继承父类所有属性

class Student extends Person {constructor(props) {super(props)}printProps() {​console.log(this.props)}}​
​
const student = new Student('student')​
student.printProps()

15.Set
说明:​
Set 是一种类似于数组的数据结构​
特性:​

  • 元素唯一性,不允许重复元素​
  • 使用 add 增加重复元素,将会被忽略​

用途:​

  • 数组去重​
  • 数据存储
const arr = [1, 3, 1, 1, 1]const set = new Set(arr)​
set.add(1).add(1)​
console.log(set.size) //2​
const newArr = Array.from(set)​
console.log(newArr) //[ 1, 3 ]

16.Map
说明:​
类似 Object,以 key、value 形式存储数据​
区别:​
Map 键不会隐式转换成字符串,而是保持原有类型​
实例:

const map = new Map()​
map.set(1, 1)​
map.set('name', 'map')​
map.set(obj, obj)​
console.log(map.get(1)) //1​
/**​1 1​name map​{ '1': 1, true: true, a: 'a' } { '1': 1, true: true, a: 'a' }​*/​
map.forEach((val, key) => {​console.log(key, val)})

17.Symbol
说明:​
JavaScript 第六种原始数据类型,用来定义一个唯一的变量​
作用:​
创建唯一的变量,解决对象键名重复问题​
为对象、类、函数等创建私有属性​
修改对象的 toString 标签​
为对象添加迭代器属性​
如何获取对象的 symbol 属性?​
Object.getOwnPropertySymbols(object)​
实例

// 对象属性重名问题;​
const objSymbol = {[Symbol()]: 1,[Symbol()]: 2,}​
console.log(objSymbol)​
​
// 2、为对象、类、函数等创建私有属性​
const name = Symbol()const obj2 = {[name]: 'symbol',testPrivate() {​console.log(this[name])},}​
​
obj2.testPrivate()// 定义toString标签;​
console.log(obj2.toString())​
obj2[Symbol.toStringTag] = 'xx'​
console.log(obj2.toString()) //[object xx]

18.for…of…
用途:​
已统一的方式,遍历所有引用数据类型​
特性:​
可以随时使用 break 终止遍历,而 forEach 不行​
实例:

// 基本用法​
// 遍历数组​if (item > 2) {​console.log(item)}}​
​
// 遍历set​
const set = new Set()​
set.add('foo').add('bar')for (const item of set) {​console.log('set for of', item)}// 遍历map​
const map = new Map()​
map.set('foo', 'one').set('bar', 'two')for (const [key, val] of map) {​console.log('for of map', key, val)}//迭代对象​
const obj = {​name: 'xiaohui',​age: '10',​store: [1, 2, 3],// 实现可迭代的接口​[Symbol.iterator]: function () {const params = [this.name, this.age, this.store]let index = 0return {next() {const ret = {​value: params[index],​done: index >= params.length,}​index++return ret​},}},}​
​
for (const item of obj) {​console.log('obj for of', item)}

19.迭代器模式
作用:通过 Symbol.interator 对外提供统一的接口,获取内部的数据​
外部可以通过 for…of…去迭代内部的数据

const tods = {​life: ['eat', 'sleep'],​learn: ['js', 'dart'],// 增加的任务​work: ['sale', 'customer'],[Symbol.iterator]: function () {const all = []​Object.keys(this).forEach((key) => {​all.push(...this[key])})let index = 0return {next() {const ret = {​value: all[index],​done: index >= all.length,}​index++return ret​},}},}​
​
for (const item of tods) {​console.log(item)}

20.Generator 生成器
Generator​

  • 函数前添加 *,生成一个生成器​
  • 一般配合 yield 关键字使用​
  • 最大特点,惰性执行,调 next 才会往下执行​
  • 主要用来解决异步回调过深的问题
// 生成迭代器方法​
//  生成器Generator的应用​function* createIdGenerator() {let id = 1while (id < 3) yield id++}const createId = createIdGenerator()​
console.log(createId.next()) //{ value: 1, done: false }​
console.log(createId.next()) //{ value: 2, done: false }​
console.log(createId.next()) //{ value: undefined, done: true }​const todos = {​life: ['eat', 'sleep', 'baba'],​learn: ['es5', 'es6', 'design pattern'],​work: ['b', 'c', 'framework'],[Symbol.iterator]: function* () {const all = [...this.life, ...this.learn, ...this.work]for (const i of all) {yield i​}},}for (const item of todos) {​console.log(item)}

21.includes 函数-es2016
判断数组是否包含某个元素,包含 NaN,解决 indexOf 无法查找 NaN 问题

//  includes函数​
const arr = ['foo', 'bar', 'baz', NaN]​
console.log(arr.includes(NaN)) //true​
console.log(arr.indexOf(NaN)) //-1

22.运算符-es2016

// 指数运算符 **​
// es5中2十次方​
console.log(Math.pow(2, 10))// es6中2十次方​
console.log(2 ** 10)

23.values 函数-es2017
将对象的值以数组的形式返回

const obj = {​foo: 1,​bar: 2,​baz: 3,}​
​
console.log(Object.values(obj)) //[ 1, 2, 3 ]

24.entries 函数-es2017
将对象以键值对二维数组返回,使之可以使用 for…of…遍历

const obj = {​foo: 1,​bar: 2,​baz: 3,}​
console.log(Object.entries(obj))const entry = Object.entries(obj)for (const [key, value] of entry) {​console.log(key, value)}

25.Object.getOwnPropertyDescriptors(obj)-es2017
获取对象的描述信息​
可以通过获得的描述信息,配合 Object.defineProperties 来完整复制对象,包含 get,set 方法

// getOwnPropertyDescriptors​// 普通get方法​
const objGet = {​foo: 1,​bar: 2,get getCount() {return this.foo + this.bar​},}// assign方法会把getCount当做普通属性复制,从而getCount为3,修改bar不管用​
const objGet1 = Object.assign({}, objGet)​
objGet1.bar = 3​
console.log(objGet1.getCount) //3​
// descriptors​
const descriptors = Object.getOwnPropertyDescriptors(objGet)​
console.log('des', descriptors)// 通过descriptors来复制对象,可以完整复制对象,包含get,set​
const objGet2 = Object.defineProperties({}, descriptors)​
objGet2.bar = 3​
console.log(objGet2.getCount) //4

26.padStart, padEnd 函数-es2017
在字符串前,或者后面追加指定字符串​
参数:​
targetLenght: 填充后的目标长度​
padString:填充的字符串​
规则:​
1、填充的字符串超过目标长度,会在规定长度时被截断​
2、填充字符串太短会以空格填充​
3、padString 未传值,以空格填充​
作用:​
一般用来对齐字符串输出

 /**​*  foo.................|1​barbar..............|2​bazbazbaz...........|3​*/​console.log(`${key.padEnd(20, '.')}${value.toString().padStart(2, '|')}`)

基本数据类型

JavaScript 中的简单数据类型包括以下几种:​

  • 字符串(String):用于表示文本数据,用引号(单引号或双引号)包裹起来,例如:“Hello, World!”。​
  • 数字(Number):用于表示数值数据,包括整数和浮点数(带小数点的数),例如:42、3.14。​
  • 布尔值(Boolean):用于表示逻辑值,只有两个可能的取值:true(真)和false(假)。​
  • undefined:表示未定义的值,通常表示未声明的变量或缺少返回值的函数。​
  • null:表示空值,用于显式地表示变量或对象没有值。​
  • Symbol(符号):表示唯一的标识符,用于对象属性的键。​
  • BigInt:用于表示任意精度的整数。BigInt 是一种简单数据类型,在 ECMAScript 2020 中引入。​

这些简单数据类型在 JavaScript 中是不可变的,也就是说,它们的值在创建后不能被修改。当你对一个简单数据类型的值进行操作时,实际上是创建了一个新的值。

ES6中数组扩展

扩展运算符(Spread operator):使用 … 语法可以将一个数组展开成多个独立的元素,或者将多个元素合并为一个数组。​

  • Array.from():通过类似数组的对象或可迭代对象创建一个新的数组。​
  • Array.of():创建一个由传入参数组成的新数组。​
  • find() 和 findIndex():用于在数组中查找满足指定条件的第一个元素及其索引。​
  • includes():检查数组是否包含指定的元素,并返回布尔值。​
  • fill():使用指定的值填充数组的所有元素。​
  • flat() 和 flatMap():用于将嵌套的数组展平,减少维度。​
  • map()、filter()、reduce()、forEach() 等方法的回调函数支持箭头函数语法。​
  • entries()、keys() 和 values():用于遍历数组的键值对、键和值。​
  • 数组解构赋值:可以通过解构赋值从数组中提取值并赋给变量。​
  • 数组的扩展属性:Array.prototype.length 可以被修改,- Array.prototype[@@toStringTag] 返回 “Array”。

箭头函数

什么是箭头函数?​
ES6中允许使用箭头=>来定义箭头函数,具体语法,我们来看一个简单的例子:

// 箭头函数​
let fun = (name) => {// 函数体​return `Hello ${name} !`;};​
​
// 等同于​
let fun = function (name) {// 函数体​return `Hello ${name} !`;};

可以看出,定义箭头函在数语法上要比普通函数简洁得多。箭头函数省去了function关键字,采用箭头=>来定义函数。函数的参数放在=>前面的括号中,函数体跟在=>后的花括号中。

箭头函数与普通函数的区别​

1、语法更加简洁、清晰​
从上面的基本语法示例中可以看出,箭头函数的定义要比普通函数定义简洁、清晰得多,很快捷。​
2、箭头函数不会创建自己的this(重要!!深入理解!!)​
我们先来看看MDN上对箭头函数this的解释。​

箭头函数不会创建自己的this,所以它没有自己的this,它只会从自己的作用域链的上一层继承this。​
​箭头函数没有自己的this,它会捕获自己在定义时(注意,是定义时,不是调用时)所处的外层执行环境的this,并继承这个this值。所以,箭头函数中this的指向在它被定义的时候就已经确定了,之后永远不会改变。

3、箭头函数继承而来的this指向永远不变(重要)
上面的例子,就完全可以说明箭头函数继承而来的this指向永远不变。对象obj的方法b是使用箭头函数定义的,这个函数中的this就永远指向它定义时所处的全局执行环境中的this,即便这个函数是作为对象obj的方法调用,this依旧指向Window对象。

4、.call()/.apply()/.bind()无法改变箭头函数中this的指向
.call()/.apply()/.bind()方法可以用来动态修改函数执行时this的指向,但由于箭头函数的this定义时就已经确定且永远不会改变。所以使用这些方法永远也改变不了箭头函数this的指向,虽然这么做代码不会报错

5、箭头函数不能作为构造函数使用
我们先了解一下构造函数的new都做了些什么?简单来说,分为四步:​
① JS内部首先会先生成一个对象; ② 再把函数中的this指向该对象; ③ 然后执行构造函数中的语句; ④ 最终返回该对象实例。​
但是!!因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则用new调用时会报错!

6、箭头函数没有自己的arguments
箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值。

7、箭头函数没有原型prototype

let sayHi = () => {​console.log('Hello World !')};​
console.log(sayHi.prototype); // undefined

8、箭头函数不能用作Generator函数,不能使用yeild关键字

symbol 有什么用处?

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。​
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。​
Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

async/await 和 Promise 有什么关系?

Promise​

Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象​

async/await​
es2017的新语法,async/await就是generator + promise的语法糖​
async/await 和 Promise 的关系非常的巧妙,await必须在async内使用,并装饰一个Promise对象,async返回的也是一个Promise对象。​
async/await中的return/throw会代理自己返回的Promise的resolve/reject,而一个Promise的resolve/reject会使得await得到返回值或抛出异常。​

  • 如果方法内无await节点​
    return 一个字面量则会得到一个{PromiseStatus: resolved}的Promise。​
    throw 一个Error则会得到一个{PromiseStatus: rejected}的Promise。​
  • 如果方法内有await节点​
    async会返回一个{PromiseStatus: pending}的Promise(发生切换,异步等待Promise的执行结果)。​
    Promise的resolve会使得await的代码节点获得相应的返回结果,并继续向下执行。​
    Promise的reject 会使得await的代码节点自动抛出相应的异常,终止向下继续执行。

如何中断Promise

Promise 有个缺点就是一旦创建就无法取消,所以本质上 Promise 是无法被终止的,但我们在开发过程中可能会遇到下面两个需求

  • 中断调用链
    就是在某个 then/catch 执行之后,不想让后续的链式调用继续执行了
somePromise​.then(() => {}).then(() => {// 终止 Promise 链,让下面的 then、catch 和 finally 都不执行​}).then(() => console.log('then')).catch(() => console.log('catch')).finally(() => console.log('finally'))

​一种方法是在then中直接抛错, 这样就不会执行后面的then, 直接跳到catch方法打印err(但此方法并没有实际中断)。但如果链路中对错误进行了捕获,后面的then函数还是会继续执行。​
Promise的then方法接收两个参数:

Promise.prototype.then(onFulfilled, onRejected)

若onFulfilled或onRejected是一个函数,当函数返回一个新Promise对象时,原Promise对象的状态将跟新对象保持一致,详见Promises/A+标准。​
因此,当新对象保持“pending”状态时,原Promise链将会中止执行。

Promise.resolve().then(() => {​console.log('then 1')return new Promise(() => {})}).then(() => {​console.log('then 2')}).then(() => {​console.log('then 3')}).catch((err) => {​console.log(err)})
  • 中断Promise
    注意这里是中断而不是终止,因为 Promise 无法终止,这个中断的意思是:在合适的时候,把 pending 状态的 promise 给 reject 掉。例如一个常见的应用场景就是希望给网络请求设置超时时间,一旦超时就就中断,我们这里用定时器模拟一个网络请求,随机 3 秒之内返回。
function timeoutWrapper(p, timeout = 2000) {const wait = new Promise((resolve, reject) => {setTimeout(() => {reject('请求超时')}, timeout)})return Promise.race([p, wait])}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/6632.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

DAY21|二叉树Part08|LeetCode: 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

目录 LeetCode: 669. 修剪二叉搜索树 基本思路 C代码 LeetCode: 108.将有序数组转换为二叉搜索树 基本思路 C代码 LeetCode: 538.把二叉搜索树转换为累加树 基本思路 C代码 LeetCode: 669. 修剪二叉搜索树 力扣代码链接 文字讲解&#xff1a;LeetCode: 669. 修剪二叉搜…

HarmonyOS基础:鸿蒙系统组件导航Navigation

大家好&#xff01;我是黑臂麒麟&#xff08;起名原因&#xff1a;一个出生全右臂自带纹身的高质量程序员&#x1f60f;&#xff09;&#xff0c;也是一位6&#xff08;约2个半坤年&#xff09;的前端&#xff1b; 学习如像练武功一样&#xff0c;理论和实践要相结合&#xff0…

​Houdini云渲染如何使用?如何让一个镜头使用成百上千台机器渲染,提高渲染效率

​Houdini云渲染如何使用&#xff1f;如何让一个镜头使用成百上千台机器渲染&#xff0c;提高渲染效率呢&#xff0c;最简单的教程来了&#xff01; 第一步&#xff1a;云渲码6666注册成都渲染101&#xff0c;并且下载渲染101客户端 客户端是上传下载的工具&#xff0c;将文件…

如何使用Varjo直接观看Blender内容

最近&#xff0c;开源的3D建模程序Blender为Varjo提供了出色的OpenXR支持&#xff0c;包括四视图和凹进渲染扩展。但是在Blender中&#xff0c;默认不启用VR场景检查。要开始使用VR场景检查&#xff0c;只需遵循以下步骤&#xff1a; 1. 下载并安装Blender 2.启用Blender VR场景…

linux 安装anaconda3

1.下载 使用repo镜像网址下载对应安装包 右击获取下载地址&#xff0c;使用终端下载 wget https://repo.anaconda.com/archive/Anaconda3-2024.02-1-Linux-x86_64.sh2.安装 使用以下命令可直接指定位置 bash Anaconda3-2024.02-1-Linux-x86_64.sh -b -p /home/anaconda3也…

JavaScript。—关于语法基础的理解—

一、程序控制语句 JavaScript 提供了 if 、if else 和 switch 3种条件语句&#xff0c;条件语句也可以嵌套。 &#xff08;一&#xff09;、条件语句 1、单向判断 &#xff1a; if... &#xff08;1&#xff09;概述 < if >元素用于在判断该语句是否满足特定条…

DDD学习笔记

DDD学习笔记 1. 什么是 DDD&#xff1f; 领域驱动设计&#xff08;Domain-Driven Design, DDD&#xff09;是一种复杂软件系统设计的方法&#xff0c;强调以业务领域为核心进行设计与开发。它通过将业务逻辑与代码组织紧密结合&#xff0c;帮助开发团队更好地理解和实现业务需…

c语言简单编程练习8

1、递归函数&#xff1a; 通过调用自身来解决问题的函数&#xff0c;递归也就是传递和回归&#xff1b; 递归函数的两个条件&#xff1a; 1&#xff09;函数调用函数本身 2&#xff09;一定要有结束条件 循环与递归的区别&#xff1a; 每调用一次递归函数&#xff0c;都会…

如何将MySQL彻底卸载干净

目录 背景&#xff1a; MySQL的卸载 步骤1&#xff1a;停止MySQL服务 步骤2&#xff1a;软件的卸载 步骤3&#xff1a;残余文件的清理 步骤4&#xff1a;清理注册表 步骤五:删除环境变量配置 总结&#xff1a; 背景&#xff1a; MySQL卸载不彻底往往会导致重新安装失败…

linux-环境变量

环境变量是系统提供的一组 name value 的变量&#xff0c;不同的变量有不同的用途&#xff0c;通常都具有全局属性 env 查看环境变量 PATH PATH是一个保存着系统指令路径的一个环境变量&#xff0c;系统提供的指令不需要路径&#xff0c;直接就可以使用就是因为指令的路径…

IDEA修改生成jar包名字的两种方法实现

IDEA修改生成jar包名字的两种方法实现 更新时间&#xff1a;2023年08月18日 11:45:36 作者&#xff1a;白白白鲤鱼 本文主要介绍了IDEA修改生成jar包名字的两种方法实现,通过简单的步骤,您可以修改项目名称并在打包时使用新的名称,具有一定的参考价值,感兴趣的可以了解下 …

【Java Web】JSP实现数据传递和保存(中)中文乱码 转发与重定向

文章目录 中文乱码转发与重定向转发重定向区别 升级示例1 中文乱码 JSP 中默认使用的字符编码方式&#xff1a;iso-8859-1&#xff0c;不支持中文。常见的支持中文的编码方式及其收录的字符&#xff1a; gb2312&#xff1a;常用简体汉字gbk&#xff1a;简体和繁体汉字utf-8&a…

ROS话题通信机制理论模型的学习

话题通信是ROS&#xff08;Robot Operating System&#xff0c;机器人操作系统&#xff09;中使用频率最高的一种通信模式&#xff0c;其实现模型主要基于发布/订阅模式。 一、基本概念 话题通信模型中涉及三个主要角色&#xff1a; ROS Master&#xff08;管理者&#xff0…

【Ai教程】Ollma安装 | 0代码本地运行Qwen大模型,保姆级教程来了!

我们平时使用的ChatGPT、kimi、豆包等Ai对话工具&#xff0c;其服务器都是部署在各家公司的机房里&#xff0c;如果我们有一些隐私数据发到对话中&#xff0c;很难保证信息是否安全等问题&#xff0c;如何在保证数据安全的情况下&#xff0c;又可以使用大预言模型&#xff0c;O…

从工作原理上解释为什么MPLS比传统IP方式高效?

多协议标签交换&#xff08;Multiprotocol Label Switching, MPLS&#xff09;是一种用于高速数据包转发的技术。它通过在网络的入口点对数据包进行标签操作&#xff0c;然后在核心网络内部基于这些标签来快速转发数据包&#xff0c;从而提高了数据传输效率。以下是几个方面解释…

以命令行形式执行Postman脚本(使用Newman)

一、背景 ​ Postman的操作离不开客户端。但是在一些情况下可能无法使用客户端去进行脚本执行。比如在服务端进行接口测试。由此我们引入了Newman。Newman基于Node.js开发&#xff0c;它使您可以直接从命令行轻松运行和测试Postman测试集。它在构建时考虑了可扩展性&#xff0c…

国内手机号Google账号(gmail)注册教程

注意&#xff01;&#xff01;本篇只适用于未注册过或未修改过的萌新用户&#xff01;&#xff01;&#xff01;&#xff01;&#xff08;我注册第二个账号时就通过不了了&#xff09; 国内手机号码如何创建Google&#xff08;谷歌&#xff09;账号&#xff0c;我们会发现&…

性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台

前言 在当前激烈的市场竞争中&#xff0c;创新和效率成为企业发展的核心要素之一。在这种背景下&#xff0c;如何保证产品和服务的稳定性、可靠性以及高效性就显得尤为重要。 而在软件开发过程中&#xff0c;性能测试是一项不可或缺的环节&#xff0c;它可以有效的评估一个系…

大语言模型训练的全过程:预训练、微调、RLHF

一、 大语言模型的训练过程 预训练阶段&#xff1a;PT&#xff08;Pre training&#xff09;。使用公开数据经过预训练得到预训练模型&#xff0c;预训练模型具备语言的初步理解&#xff1b;训练周期比较长&#xff1b;微调阶段1&#xff1a;SFT&#xff08;指令微调/有监督微调…

【LeetCode】【算法】142. 环形链表II

142环形链表II 题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#x…