前言
- 对阮一峰的ES6的内容进行的
提炼
- 对其中的内容进行了
简化
- 对一些我个人认为较复杂且不常用的功能或概念
采取忽略
- 对ES6
不熟悉
的同学 可能会有一点帮助 - 如笔记整理有错误还请留言 指出
- 深度阅读请访问 http://es6.ruanyifeng.com
let 与 const
代码块:外层作用域无法读取内层作用域中的变量
- 共同点
- 只在块级作用域中有效
- 暂时性死区
- 不允许重复声明
- 不存在变量提升
- 不同点
- const 声明时 必须赋值 并且 值不可变
关于解析赋值
数组的
数组的元素是按 次序排列 的,变量的取值要与位置匹配
典型例子
|
设置默认值 :解构赋值允许指定默认值。
|
对象的
对象的属性虽没有次序,但变量必须与属性同名
典型例子
|
设置默认值 :解构赋值允许指定默认值。
|
函数的
经典范例
|
基本用途
交换变量的值
上面代码交换变量x和y的值,这样的写法不仅简洁,而且易读,语义非常清晰。
|
从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。
|
函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
|
提取JSON数据
解构赋值对提取JSON对象中的数据,尤其有用。
|
函数参数的默认值
指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || ‘default foo’;这样的语句。
|
遍历Map结构
ap结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就非常方便。
|
输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。
|
关于类型的扩展
字符串扩展–模板字符串
|
正则的扩展–后行断言
目前,有一个 提案 ,引入后行断言。
|
数值的扩展
方法 | 作用 |
---|---|
Number.isNaN() | 判断值是否为NaN,为NaN返回true |
Number.parseInt()/parseFloat() | ES6将这俩个全局方法移植到了Number对象上 |
Number.isInteger() | 判断值是否为整数 |
Math.trunc() | 返回整数部分 |
Math.sign() | 判断一个数是正数、负数、0,对应返回值 +1、-1、0 |
数组的扩展
Array.from(对象,_对象处理函数,_this指向)
可将 数组的对象
和 可遍历的对象
转换为数组结构
|
Array.of()
将一组值转换为数组
|
find()、findIndex()、includes()
- find 方法,用于找出
第一个
符合条件的数组中的值,否则返回 undefined - findIndex 返回
第一个
符合条件的位置,否则返回-1。 - includes() 和 indexOf() 很像,前者返回布尔值后者返回数值
|
数组的遍历
entries() 、keys()、values()
|
函数的扩展
函数参数的默认值
- 函数内部是不可以再次声明形参
- 函数的执行时形参会形成作用域
- 函数体内>形参内>全局内
|
rest 形参
- rest 只能在形参中的最后一位
- 函数的length属性不包括 rest 函数
|
扩展运算符的应用
小应用
|
实现了 Iterator 接口的对象
任何 Iterator 接口的对象,都可以用扩展运算符转为 真正的数组。
|
Map和Set结构,Generator函数
因此只要 具有Iterator接口的对象
,都可以使用扩展运算符
|
Generator函数运行后,返回一个遍历器对象
,因此也可以使用扩展运算符。
|
上面代码中,变量go是一个 Generator 函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
箭头函数
使用注意
- 箭头函数体内的
this
指向的是 父作用域 - 不可以当作构造函数
- 不可以使用 arguments 对象,可以用 rest 参数代替。
- 不可以使用 yield 命令
基本使用
|
绑定 this
- 绑定 this 是用来取代call、apply、bind调用
- 函数绑定运算符是并排的两个冒号(::)
|
尾调用(Tail Call)
ES6 的尾调用优化只在严格模式下开启,正常模式是无效的
。
尾调用是函数式编程 的一个重要概念,就是指 某个函数 最后一步 以 return
的方式调用另一个函数
|
对于尾调用优化的理解
每函数执行时在内存中形成调用帧(保存调用位置和内部变量等信息),调用帧的释放 取决于该函数是否执行了return
,系统默认会自执行 return, 但前提是需要等待程序执行完毕时,那在这个过程中累积的调用帧无疑加大了内存的开销。
所以结尾处以 return 形式调用另一个函数 可以使 调用帧及时释放以减少无用的内存浪费
对象的扩展
属性的简洁表示法
|
应用场景
场景一 :对象中直接放一个变量
|
场景二 :这种写法用于函数的返回值,将会非常方便。
|
场景三 :CommonJS 模块输出变量
,就非常合适使用简洁写法。
|
属性名表达式
|
属性表达式定义方法名
|
Object.is()
比较俩个值是否相等,和 === 类似 . Object.js(1,2) 等同于 1 === 2
Object.assign()
Object.assign方法用于合并对象,第一个参数是目标对象
,后面的参数都是源对象。
|
应用场景
- 为对象添加属性
|
- 为对象添加方法
|
- 合并多个对象
|
- 为属性指定默认值
|
属性的遍历
方法 | 作用 |
---|---|
for…in | 遍历自身的和继承的可枚举属性(不含 Symbol 属性)。 |
Object.keys(obj) | 返回数组,(不含继承的)所有可枚举属性(不含 Symbol 属性)。 |
Object.getOwnPropertyNames(obj) | 返回自身对象的一个数组,不含 Symbol 属性,但是包括不可枚举属性)。 |
Object.getOwnPropertySymbols(obj) | 返回自身对象的一个数组,含所有 Symbol 属性。 |
Reflect.ownKeys(obj) | 返回自身对象的一个数组,不管属性名是 Symbol 或字符串,也不管是否可枚举。 |
- 以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。
- 首先遍历
为数值的属性
,按照数字排序
。 - 其次遍历
字符串的属性
,按照生成时间排序。 - 最后遍历
Symbol 值的属性
,按照生成时间排序。
- 首先遍历
|
proto 属性
- Object.getprototypeOf() == 读取一个对象的原型对象
- Object.setprototypeOf() == 设置一个对象的原型对象
Object.keys()/values()/entries()
可将对象中所有的 键或值
单独分离进行独立出来
|
对象的扩展运算符
|
扩展运算符花样操作
|
Symbol
- 为了从根本上解决命名冲突
- ES6 引入了一种新的原始数据类型 Symbol,
表示独一无二的值
- 目前总共7种分别是: undefined、null、Nubmber、String、Boolean、Object、 Symbol
Set 和 Map 数据结构
Set
基本用法
- 类似于数组 ,但成员的值 没有重复 的
- 可接受 任何数组 作为参数进行初始化
- Set 本身是一个构造函数,用来生成 Set 数据结构。
|
Set 属性
属性 | 作用 |
---|---|
构造函数 | Set.prototype.constructor |
成员总数 | Set.prototype.size |
Set 方法
操作方法 | 作用 |
---|---|
add(value): | 添加某个值,返回Set结构本身。 |
delete(value): | 删除某个值,返回一个布尔值,表示删除是否成功。 |
has(value): | 返回一个布尔值,表示该值是否为Set的成员。 |
clear(): | 清除所有成员,没有返回值。 |
遍历方法 | 作用 |
---|---|
keys() / values(): | 返回键名的遍历器 |
entries(): | 返回键和值,但键和值都一样 |
forEach(): | 使用回调函数遍历每个成员 |
|
应用
数组去重
|
数组的 map 和 filter 方法结合Set轻松实现并、交、差集
|
Map
解决对象的键只能是字符串的痛点(null、undefined都可以),Map 结构提供了“值和值”的对应
基本用法
|
注意 :Map 结构只会对 有对象引用的 , 才将其视为一个键。
|
属性和方法
属性:size ,返回 Map结构成员总数。
方法:
操作的 | 作用 |
---|---|
set(key,value): | 设置 key 对应的键值,可采用链式写法 |
get(key): | 读取 key 对应的键值,找不到返回 undefined |
has(value): | 返回一个布尔值,表示该值是否为 Map 的成员。 |
delete(value): | 删除某个值,返回一个布尔值,表示删除是否成功。 |
clear(): | 清除所有成员,没有返回值。 |
遍历的 | 作用 |
---|---|
keys() | 返回键名的遍历器 |
values() | 返回键值的遍历器 |
entries(): | (默认)返回键和值的遍历器 |
forEach(): | 遍历map的所有成员 |
forEach方法还可以接受第二个参数,用来绑定 this。
|
与其他数据结构的互相转换
Map 转为数组
直接用扩展运算符就可以很方便的进行转化
|
数组转为 Map
直接将数组传入 Map 构造函数,就可以转化为 Map 。
|
Map 转为对象
如果所有 Map 的键都是字符串,它可以转为对象。
|
对象转为 Map
|
Map 转为 JSON
Map 转为 JSON 要区分两种情况。一种情况是,Map 的键名都是字符串,这时可以选择转为对象 JSON。
|
JSON 转 Map
JSON 转为 Map,正常情况下,所有键名都是字符串。
|
Proxy (代理)
在目标对象之前设置了一个中间人,外界访问该对象时都必须先通过这个中间人,这种机制就可以对外界的访问进行过滤和改写
ES6 原声提供 Proxy 构造函数,用来生成 proxy 实例。
|
- new Proxy(): 表示生成一个
Proxy实例
- target : 参数表示所要
拦截的目标对象
- handler: 参数也是一个对象,用来
定制拦截行为
。
应用场景
Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端。
|
Promise(承诺) 对象
了解 Promise
- Promise 是异步编程的一种解决方案
- Promise 是一个保存着一个未来才会结束的事件
- Promise 的俩个特点
- 只有异步操作的 结果,才能决定当前是哪一种 状态
- Pending(进行中)
- Resolved(已完成,又称 Fulfilled)
- Rejected(已失败)
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
- 状态的改变只有两种可能:从
Pending
变为 Resolved 和 从Pending
变为 Rejected。
- 状态的改变只有两种可能:从
- 只有异步操作的 结果,才能决定当前是哪一种 状态
如果某些事件不断地反复发生,一般来说,使用 Stream
模式是比部署Promise更好的选择。
基本操作
ES6 规定,Promise对象是一个 构造函数
,用来生成Promise实例。
Promise构造函数
接受一个函数作为参数,该函数的两个参数也是函数分别是resolve和reject。- resolve 函数: 在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
- reject 函数:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
|
一个 Promise 对象的简单例子
一个用Promise对象实现的 Ajax 操作的例子
Promise.prototype.then()
- then方法是定义在原型对象Promise.prototype上的
- then(Resolved状态回调函数,Rejected 状态的回调函数)
then方法返回的是一个 新的
Promise实例,因此可以采用链式写法,即then方法后面再调用另一个then方法。
Promise.prototype.catch()
Promise.prototype.catch 方法是 .then(null, rejection)的别名,用于 指定发生错误时的回调函数
。
|
例子
|
俩种捕捉错误的方法
|
其他
Promise.all()
Promise.all用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.resolve()
有时需要将现有对象转为Promise对象,Promise.resolve 方法就起到这个作用。
Promise.reject()
也会返回一个新的 Promise 实例,该实例的状态为 rejected。
Iterator 和 for…of循环
Iterator 概念
- ES6中有四种数据集合分别是:数组、对象(没有Iterator接口)、Map、Set
- 用户如果想组合使用这些数据,就需要一种统一的接口机制,来处理所有不同的数据结构。
- 遍历器(Iterator)就是为各种不同的数据结构 提供统一的访问机制的接口 。
- 任何数据结构只要部署Iterator接口 ,就可以完成遍历操作。
- Iterator的三个作用
- 为各种数据结构,
提供一个统一访问接口
; - 使得数据结构的成员能够按
某种次序排列
; - Iterator接口主要
配合for...of
。
- 为各种数据结构,
默认的 Iterator 接口
- 默认的Iterator接口 部署在数据结构的
Symbol.iterator属性
- 也就是,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”。
- Symbol.iterator就是 默认生成遍历器的函数,执行它就会返回一个遍历器。
- 原生具备 Iterator 接口的数据结构:Array、Map、Set、
String
、TypedArray、函数的 arguments 对象
|
调用 Iterator 接口的场合
|
遍历器对象的 return(),throw()
遍历器对象具有next、_return、_throw方法。
return方法的使用场合是,如果for...of循环提前退出
(通常是因为出错,或者有break语句或continue语句),就会调用return方法。
for…of 循环
for…of循环 内部调用
的是数据结构的Symbol.iterator方法
,所以只要数据结构只要部署了Symbol.iterator属性,就可以用for…of循环遍历它的成员
与其他遍历语法比较
- for循环
- 没有 fo…of 简洁
- forEach
- 无法中途跳出forEach循环,break命令或return命令都不能奏效
- for…in
- 主要是为遍历对象而设计的,不适用于遍历数组
Generator 函数的语法
基本概念
Generator 函数是 ES6 提供的一种 异步编程解决方案
,语法行为与传统函数 完全不同
。
Generator 函数有多种理解角度。
- 从语法上,首先可以把它理解成,
Generator 函数是一个状态机
,封装了多个内部状态。 - 同时执行 Generator 函数会
返回一个遍历器对象
- 返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
- 从语法上,首先可以把它理解成,
Generator 函数形式上的两个特征。
- function 与函数名的中间有一个星号;
- 函数体内部使用yield表达式,定义不同的内部状态( yield 在英语里的意思就是“产出”)。
yield 表达式
由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以 暂停执行的函数
,yield表达式就是暂停标志。
遍历器对象的next方法的运行逻辑如下。
- 遇到yield表达式,暂停并将
紧跟在yield后面
的那个表达式的值
,作为返回的对象的value属性值。 - 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
- 如果没有再遇到新的yield表达式,就一直运行到return语句为止,并以对象行使返回return语句后面的表达式的值
- 如果该函数没有return语句,则返回的对象的value属性值为undefined。
- 遇到yield表达式,暂停并将
yield 于 return 的区别
- yield 函数暂停执行,下一次再从该位置继续向后执行,具备记忆
- 一个函数里面,只能执行一次return语句,但是可以执行多个yield表达式。
- 正常函数只能返回一个值,因为只能执行一次return;
- 函数可以返回一系列的值,因为可以有任意多个yield。
yield表达式如果用在另一个表达式之中,必须放在圆括号里面。
|
next 方法的参数
next方法的参数 表示 上一个yield表达式的返回值
|
Generator.prototype.throw()
Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。
Generator.prototype.return()
Generator函数返回的遍历器对象,还有一个return方法,可以返回给定的值
,并且终结遍历Generator函数。
yield* 表达式
如果在 Generator 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的。
|
作为对象属性的 Generator 函数
|
应用场景
- 异步操作的同步化表达
- 控制流管理
- 部署 Iterator 接口
- 作为数据结构
Generator 函数的异步应用
异步编程对 JavaScript 语言太重要,因为 Javascript 语言的执行环境是 “单线程”的.
async 函数
一句话,它就是 Generator 函数的语法糖
Class 的基本语法
ES6 的class写法只是让对象原型的 写法更加清晰
、更接近主流面向对象编程
的语法而已。
Module 的语法
概述
- 历史上,JavaScript 一直没有模块体系,直接导致了对开发大型的、复杂的项目造成了巨大障碍。
- ES6 实现了
模块功能
,而且实现得相当简单
,完全可以取代其他规范。
ES6 模块的设计思想是尽量的静态化
,使得 编译时
就能 确定
模块的 依赖关系
,以及输入和输出的变量。
CommonJS 与 ES6 模块的区别
严格模式
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上 "use strict"
- 严格模式主要有以下限制
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用
with
语句 - 不能对只读属性赋值,否则报错
- 不能使用前缀0表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量
delete prop
,会报错,只能删除属性delete global[prop]
eval
不会在它的外层作用域引入变量eval
和arguments
不能被重新赋值- arguments不会自动反映函数参数的变化
- 不能使用
arguments.callee
- 不能使用
arguments.caller
- 禁止
this
指向全局对象 - 不能使用
fn.caller
和fn.arguments
获取函数调用的堆栈 - 增加了保留字(比如
protected
、static
和interface
)
export 命令
模块功能主要由两个命令构成:export
命令用于 规定
模块的 对外接口
,import
命令用于 输入其他模块提供的功能
。
输出变量
- 一个模块就是一个独立的文件,其内部所有变量
外部无法获取
。 - 如果希望外部能够读取模块内部的某个变量,就必须使用
export输出该变量
。
|
输出函数或类
|
使用as关键字重命名
|
特别注意
export
规定是: 对外的接口
与 模块内部的变量
建立一一 对应关系
。
import 命令
使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。
|
- 知识点
- from指定模块文件的位置时可以是相对或绝对路径,.js可以省略
- 如果只是模块名,不带有路径,那么必须有配置文件,告诉 JS 引擎该模块的位置。
- import命令具有提升效果,会提升到整个模块的头部,首先执行。
模块的整体加载
整体加载:即用星号(*)指定一个对象
,所有输出值都加载在这个对象上面。
|
export default 命令
为模块指定默认输出
|
比较默认输出和正常输出
|
本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。
|
有了export default命令,输入模块时就非常直观了,以输入 lodash 模块为例。
|
export 与 import 的复合写法
如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。
|
模块的继承
假设有一个circleplus模块,继承了circle模块。
|
跨模块常量
设置跨模块的常量,或者说一个值要被多个模块共享,可以采用下面的写法。
|
如果要使用的常量非常多,可以建一个专门的constants目录,将各种常量写在不同的文件里面,保存在该目录下。
|
import()
- 只是有一个 提案,建议引入import()函数,完成动态加载。
- import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载。
- 适用场合
- 按需加载
- 条件加载
- 动态的模块路径
Module 的加载实现
浏览器加载
传统方法
|
加载规则
浏览器加载 ES6 模块,也使用 <script>
标签,但是要加入 type="module"
属性。
|
编程风格
块级作用域
let
完全取代var- let 和 const 之间,优先使用
const
字符串
静态字符串一律使用 单引号或反引号
,不使用双引号
|
解构赋值
|
对象
- 单行定义的对象,最后一个成员
不以逗号结尾
。 - 多行定义的对象,最后一个成员
以逗号结尾
。
|
数组
使用扩展运算符 (…) 拷贝数组
|
使用Array.from方法,将伪数组转为真数组。
|
函数
|
Map 结构
- 注意区分 Object 和 Map。
- 如果只是需要key: value的数据结构,使用Map结构。
Class
用Class 取代需要 prototype 的操作,因为Class的写法更简洁,更易于理解。
模块
Module 语法是 JS 模块的 标准写法 ,坚持使用这种写法。
- 使用
import
取代 require。 - 使用
export
取代 module.exports。
- 使用
注意事项
- 如果模块只有一个输出值,才使用export default。
- 在模块输入中使用通配符,就无法确保有一个默认输出值
- 模块默认输出一个函数,
函数名首字母小写
。 - 模块默认输出一个对象,
对象名首字母大写
。