前端 ES6 十个新特性总结

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

往期文章

前言

一、前面我们已经对JavaScript中的基础知识做了有关总结,不知大家还记得多少呢

二、接下来的内容我将对ES6中的部分新特性做出相关梳理和总结,为大家的前端知识体系进一步地巩固

总结

1.var let const

一、var

1、在ES5中,顶层对象的属性和全局变量是等价的,用var声明的变量既是全局变量,也是顶层变量:

  • 顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象

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

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

  • 在函数中使用使用var声明变量时候,该变量是局部的
  • 而如果在函数内不使用var,该变量是全局的

二、let

1、letES6新增的命令,用来声明变量

2、用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效,不存在变量提:

  • 只要块级作用域内存在let命令,这个区域就不再受外部影响

3、使用let声明变量前,该变量都不可用,也就是大家常说的暂时性死区:

  • let不允许在相同作用域中重复声明
  • 注意的是相同作用域,下面这种情况是不会报错的,因此,我们不能在函数内部重新声明参数

三、const

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

  • const一旦声明变量,就必须立即初始化,不能留到以后赋值
  • 如果之前用varlet声明过变量,再用const声明同样会报错

2、const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动:

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

四、区别

1、变量提升:

  • var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined
  • letconst不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错

2、暂时性死区:

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

3、块级作用域:

  • var不存在块级作用域
  • letconst存在块级作用域

4、重复声明:

  • var允许重复声明变量
  • letconst在同一作用域不允许重复声明变量

5、修改声明的变量:

  • varlet可以
  • const声明一个只读的常量。一旦声明,常量的值就不能改变

五、使用

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

数组新增拓展

一、扩展运算符的应用

1、ES6通过扩展元素符...,好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列:

  • 主要用于函数调用的时候,将一个数组变为参数序列
  • 可以将某些数据结构转为数组,能够更简单实现数组复制,数组的合并也更为简洁

2、扩展运算符可以与解构赋值结合起来,用于生成数组。如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错:

  • 可以将字符串转为真正的数组

3、定义了遍历器(Iterator)接口的对象,都可以用扩展运算符转为真正的数组。如果对没有 Iterator 接口的对象,使用扩展运算符,将会报错

二、构造函数新增的方法

1、Array.from():将两类对象转为真正的数组:类似数组的对象和可遍历(iterable)的对象(包括 ES6 新增的数据结构 SetMap

  • 可以接受第二个参数,用来对每个元素进行处理,将处理后的值放入返回的数组

2、Array.of():用于将一组值,转换为数组

  • 没有参数的时候,返回一个空数组
  • 当参数只有一个的时候,实际上是指定数组的长度
  • 参数个数不少于 2 个时,Array()才会返回由参数组成的新数组

三、实例对象新增的方法

1、copyWithin():将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组

  • target(必需):从该位置开始替换数据。如果为负值,表示倒数。
  • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

2、find()、findIndex():find()用于找出第一个符合条件的数组成员

  • 参数是一个回调函数,接受三个参数依次为当前的值、当前的位置和原数组
  • findIndex返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
  • 这两个方法都可以接受第二个参数,用来绑定回调函数的this对象

3、fill():使用给定值,填充一个数组

  • 还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
  • 注意,如果填充的类型为对象,则是浅拷贝

4、entries(),keys(),values():

  • keys()是对键名的遍历
  • values()是对键值的遍历
  • entries()是对键值对的遍历

5、includes():用于判断数组是否包含给定的值

  • 方法的第二个参数表示搜索的起始位置,默认为0
  • 参数为负数则表示倒数的位置

6、flat(),flatMap():将数组扁平化处理,返回一个新数组,对原数据没有影响

  • flat():默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1
  • flatMap()方法对原数组的每个成员执行一个函数相当于执行Array.prototype.map(),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。flatMap()方法还可以有第二个参数,用来绑定遍历函数里面的this

四、数组的空位

1、数组的空位指,数组的某一个位置没有任何值

  • ES6 则是明确将空位转为undefined
  • 包括Array.from、扩展运算符、copyWithin()fill()entries()keys()values()find()findIndex()

3.对象新增拓展

一、属性的简写

1、ES6中,当对象键名与对应值名相等的时候,可以进行简写。方法也能够进行简写。在函数内作为返回值,也会变得方便很多:

  • 注意:简写的对象方法不能用作构造函数,否则会报错

二、属性名表达式

1、ES6 允许字面量定义对象时,将表达式放在括号内。表达式还可以用于定义方法名:

  • 注意,属性名表达式与简洁表示法,不能同时使用,会报错
  • 注意,属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object]

三、super关键字

  • this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象

四、扩展运算符的应用

1、在解构赋值中,未被读取的可遍历的属性,分配到指定的对象上面:

  • 注意:解构赋值必须是最后一个参数,否则会报错

2、解构赋值是浅拷贝。对象的扩展运算符等同于使用Object.assign()方法

五、属性的遍历

1、ES6 一共有 5 种方法可以遍历对象的属性:

  • for...in:循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
  • Object.keys(obj):返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名
  • Object.getOwnPropertyNames(obj):回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名
  • Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有 Symbol 属性的键名
  • Reflect.ownKeys(obj):返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举

2、上述遍历,都遵守同样的属性遍历的次序规则:

  • 首先遍历所有数值键,按照数值升序排列
  • 其次遍历所有字符串键,按照加入时间升序排列
  • 最后遍历所有 Symbol 键,按照加入时间升序排

六、对象新增的方法

1、Object.is():严格判断两个值是否相等,与严格比较运算符(===)的行为基本一致,不同之处只有两个:一是+0不等于-0,二是NaN等于自身

2、Object.assign():

  • Object.assign()方法用于对象的合并,将源对象source的所有可枚举属性,复制到目标对象target
  • Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象
  • 注意:Object.assign()方法是浅拷贝,遇到同名属性会进行替换

3、Object.getOwnPropertyDescriptors():返回指定对象所有自身属性(非继承属性)的描述对象

4、Object.setPrototypeOf():用来设置一个对象的原型对象

5、Object.getPrototypeOf():用于读取一个对象的原型对象

6、Object.keys():返回自身的(不含继承的)所有可遍历(enumerable)属性的键名的数组

7、Object.values():返回自身的(不含继承的)所有可遍历(enumerable)属性的键对应值的数组

8、Object.entries():返回一个对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对的数组

9、Object.fromEntries():用于将一个键值对数组转为对象

4.函数新增拓展

一、参数

1、ES6允许为函数的参数设置默认值:

  • 函数的形参是默认声明的,不能使用letconst再次声明
  • 参数默认值可以与解构赋值的默认值结合起来使用
  • 当参数为对象的时候才能进行解构,如果没有提供参数的时候,变量xy就不会生成,从而报错,这里设置默认值避免
  • 参数默认值应该是函数的尾参数,如果不是非尾部的参数设置默认值,实际上这个参数是没发省略的

二、属性

1、length:

  • length将返回没有指定默认值的参数个数
  • rest 参数也不会计入length属性
  • 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了

2、name:

  • 返回该函数的函数名
  • 如果将一个具名函数赋值给一个变量,则 name属性都返回这个具名函数原本的名字
  • Function构造函数返回的函数实例,name属性的值为anonymous
  • bind返回的函数,name属性值会加上bound前缀

三、作用域

1、一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域

2、等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的

四、严格模式

  • 只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错

五、箭头函数

1、如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分

2、如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回

3、如果返回对象,需要加括号将对象包裹

4、注意点:

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数

5.Set Map

一、定义

1、Set是一种叫做集合的数据结构,Map是一种叫做字典的数据结构

2、什么是集合和字典:

  • 集合:是由一堆无序的、相关联的,且不重复的内存结构【数学中称为元素】组成的组合
  • 字典:是一些元素的集合。每个元素有一个称作key 的域,不同元素的key 各不相同

3、区别:

  • 共同点:集合、字典都可以存储不重复的值
  • 不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储

二、Set

1、Setes6新增的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的值,我们一般称为集合

  • Set本身是一个构造函数,用来生成 Set 数据结构

2、增删改查:

  • add():添加某个值,返回 Set 结构本身。当添加实例中已经存在的元素,set不会进行处理添加
  • delete():删除某个值,返回一个布尔值,表示删除是否成功
  • has():返回一个布尔值,判断该值是否为Set的成员
  • clear():清除所有成员,没有返回值

2、遍历:

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员

3、Set的遍历顺序就是插入顺序:

  • keys方法、values方法、entries方法返回的都是遍历器对象
  • forEach()用于对每个成员执行某种操作,没有返回值,键值、键名都相等,同样的forEach方法有第二个参数,用于绑定处理函数的this
  • 扩展运算符和Set 结构相结合实现数组或字符串去重
  • 实现并集、交集、和差集

三、Map

1、Map类型是键值对的有序列表,而键和值都可以是任意类型

  • Map本身是一个构造函数,用来生成 Map 数据结构

2、增删改查:

  • size:size属性返回 Map 结构的成员总数。
  • set():设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。同时返回的是当前Map对象,可采用链式写法
  • get():get方法读取key对应的键值,如果找不到key,返回undefined
  • has():has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中
  • delete():delete方法删除某个键,返回true。如果删除失败,返回false
  • clear():clear方法清除所有成员,没有返回值

3、遍历:

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回所有成员的遍历器
  • forEach():遍历 Map 的所有成员

4、遍历顺序就是插入顺序

四、WeakSet

1、WeakSet可以接受一个具有 Iterable接口的对象作为参数

2、在APIWeakSetSet有两个区别:

  • 没有遍历操作的API
  • 没有size属性

3、WeackSet只能成员只能是引用类型,而不能是其他类型的值

4、WeakSet里面的引用只要在外部消失,它在 WeakSet里面的引用就会自动消失

五、WeakMap

1、WeakMap结构与Map结构类似,也是用于生成键值对的集合

2、在APIWeakMapMap有两个区别:

  • 没有遍历操作的API
  • 没有clear清空方法

3、WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名

4、WeakMap的键名所指向的对象,一旦不再需要,里面的键名对象和所对应的键值对会自动消失,不用手动删除引用

6.Promise

一、定义

1、Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大

2、状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)

二、优点

  • 链式操作减低了编码难度
  • 代码可读性明显增强

三、特点

  • 对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态
  • 一旦状态改变(从pending变为fulfilled和从pending变为rejected),就不会再变,任何时候都可以得到这个结果

四、用法

1、Promise对象是一个构造函数,用来生成Promise实例:

const promise = new Promise(function(resolve, reject) {});
复制代码

2、Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject:

  • resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”
  • reject函数的作用是,将Promise对象的状态从“未完成”变为“失败

3、实例方法:Promise构建出来的实例存在以下方法

  • then():then是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数
  • catch():catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数
  • finally():finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

4、构造函数方法:Promise构造函数存在以下方法

  • all():Promise.all()方法用于将多个 Promise实例,包装成一个新的 Promise实例
  • race():Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实
  • allSettled():Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例
  • resolve():将现有对象转为 Promise对象
  • reject():Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

五、使用场景

  • 将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化
  • 通过链式操作,将多个渲染数据分别给个then,让其各司其职。或当下个异步请求依赖上个请求结果的时候,我们也能够通过链式操作友好解决问题
  • 通过all()实现多个请求合并在一起,汇总所有请求结果,只需设置一个loading即可
  • 通过race可以设置图片请求超时

7.Generator

一、定义

1、Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同

2、执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态

二、特征

1、function关键字与函数名之间有一个星号

2、函数体内部使用yield表达式,定义不同的内部状态

三、异步解决方案

1、回调函数:所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数

2、Promise:Promise就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用

3、generator:yield表达式可以暂停函数执行,next方法用于恢复函数执行,这使得Generator函数非常适合将异步任务同步化

4、async/await:将上面Generator函数改成async/await形式,更为简洁,语义化更强了

5、区别:

  • promiseasync/await是专门用于处理异步操作的
  • Generator并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口...)
  • promise编写代码相比Generatorasync更为复杂化,且可读性也稍差
  • Generatorasync需要与promise对象搭配处理异步情况
  • async实质是Generator的语法糖,相当于会自动执行Generator函数
  • async使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案

8.Proxy

一、定义

  • 用于定义基本操作的自定义行为

二、本质

1、修改的是程序默认形为,就形同于在编程语言层面上做修改,属于元编程(meta programming)

2、Proxy 亦是如此,用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

三、用法

1、Proxy为 构造函数,用来生成 Proxy实例

var proxy = new Proxy(target, handler)
复制代码

2、参数:

  • target表示所要拦截的目标对象(任何类型的对象,包括原生数组,函数,甚至另一个代理))
  • handler通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为

四、使用场景

1、Proxy其功能非常类似于设计模式中的代理模式,常用功能如下:

  • 拦截和监视外部对对象的访问
  • 降低函数或类的复杂度
  • 在复杂操作前对操作进行校验或对所需资源进行管理

2、使用 Proxy 保障数据类型的准确性:

  • 声明了一个私有的 apiKey,便于 api 这个对象内部的方法调用,但不希望从外部也能够访问 api._apiKey

3、观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行:

  • observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数

4、观察者函数都放进Set集合,当修改obj的值,在会set函数中拦截,自动执行Set所有的观察者

9.Module

一、定义

1、模块:

  • 是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)

2、原因:

  • 变量和方法不容易维护,容易污染全局作用域
  • 加载资源的方式通过script标签从上到下。
  • 依赖的环境主观逻辑偏重,代码较多就会比较复杂。
  • 大型项目资源难以维护,特别是多人合作的情况下,资源的引入会让人奔溃

二、方案

1、CommonJs(典型代表:node.js早期):

  • 它通过 require 来引入模块,通过 module.exports 定义模块的输出接口
  • 这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的
  • 因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题
  • 但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适

2、AMD(典型代表:require.js):

  • 这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行
  • 所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范

3、CMD(典型代表:sea.js):

  • 这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范
  • 它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同

4、ES6 Module:

  • ES6 提出的方案,使用 import 和 export 的形式来导入导出模块

10.Decorator

一、定义

1、Decorator,即装饰器,从名字上很容易让我们联想到装饰者模式:

  • 简单来讲,装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论

2、ES6Decorator功能亦如此,其本质也不是什么高大上的结构,就是一个普通的函数,用于扩展类属性和类方法:

  • 代码可读性变强了,装饰器命名相当于一个注释
  • 在不改变原有代码情况下,对原来功能进行扩展

二、用法

1、类的装饰:当对类本身进行装饰的时候,能够接受一个参数,即类本身

2、类属性的装饰:当对类属性进行装饰的时候,能够接受三个参数

  • 类的原型对象
  • 需要装饰的属性名
  • 装饰属性名的描述对象

3、注意:装饰器不能用于修饰函数,因为函数存在变量声明情况

三、使用场景

1、基于Decorator强大的作用,我们能够完成各种场景的需求,下面简单列举几种

2、使用react-redux的时候,如果写成下面这种形式,既不雅观也很麻烦:

class MyReactComponent extends React.Component {}
​
export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);
复制代码

3、通过装饰器就变得简洁多了:

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}
复制代码

4、将mixins,也可以写成装饰器,让使用更为简洁了:

function mixins(...list) {
  return function (target) {
    Object.assign(target.prototype, ...list);
  };
}
​
// 使用
const Foo = {
  foo() { console.log('foo') }
};
​
@mixins(Foo)
class MyClass {}
​
let obj = new MyClass();
obj.foo() // "foo"
复制代码

结尾

  • 好了,本篇的所有内容就到这里了,希望大家能够在未来的日子里学以致用!

猜你喜欢

转载自juejin.im/post/7087403522806775815