ES6
1. ECMAScript和JavaScript的关系
该标准从一开始就是针对JavaScript语言制定的,但之所以不叫JavaScript,有两个原因。一是商标,Java是Sun公司的商标,根据授权协议,只有Netscape公司可以合法地使用JavaScript这个名字,且JavaScript本身也已经被Netscape公司注册为商标。二是想体现这门语言的制定者是ECMA,不是Netscape,这样有利于保证这门语言的开放性和中立性。因此,ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现(另外的ECMAScript方言还有Jscript和ActionScript)。日常场合,这两个词是可以互换的。
2. Es6与ECMAScript 2015的关系
Es6既是一个历史名词,也是一个泛指,含义是5.1版以后的JavaScript的下一代标准,涵盖了ES2015、Es2016、Es2017等等,而Es2015则是正式名称,特指该年发布的正式语言标准。本书中提到“Es6”的地方,一般是指Es2015标准,但有时也是泛指“下一代JavaScript语言”。
3. Es6声明变量的六种方法
Var、function、let、const、import、class
4. var、let、const的区别
- 作用域规则
Let/const声明的变量属于块级作用域,只能在其块或子块中可用。而var声明的变量的作用域是全局或者局部作用域 整个封闭函数 - 重复声明/重复赋值
var可以重复声明和重复赋值
let仅允许重复赋值,但不能重复声明
const既不可以重复声明,也不能重复赋值 - 变量提升(hosited)
var声明的变量存在变量提升,既可以在变量声明前访问变量,值为undefined
Let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错 ReferenceError - 暂时死区(TDZ)
var不存在暂时性死区
Let和const存在暂时性死区
5. Var、let、const
Var:用var定义全局或局部变量 会在全局或局部作用域中使用
Let:
一、let会形成块级作用域(只要包含花括号的都是块级作用域)
二、不存在变量提升
三、不可以重复声明
① 相同作用域下不可以重复声明
② 不可以和已有的用var声明变量重名
③ 在函数中不可以和形参重名
四、不影响作用域链
Const:
(一)const会形成块级作用域(只要包含花括号的都是块级作用域)
(二)不存在变量提升
(三)不可以重复声明
① 相同作用域下不可以重复声明
② 不可以和已有的用var声明变量重名
③ 在函数中不可以和形参重名
(四)不影响作用域链
(五)const必须有初始值
Const的本质:const实际上保证的,并不是变量的值不得改动,而是变量向的那个内存所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于他指向的数据结构是不是可变的,就完全不能控制了。
6. 暂时性死区
Es6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。总之,在代码块内使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称TDZ)。暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
7. 为什么需要块级作用域?
Es5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
第一种场景,内层变量可能会覆盖外层变量。
第二种场景,用来计数的循环变量泄露为全局变量。
Es6允许块级作用域的任意嵌套。
内层作用域可以定义外层作用域的同名变量。
块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式(匿名 IIFE)不在必要了。
8.顶层对象
顶层对象的属性与全局变量挂钩,被认为是 JavaScript 语言最大的设计败笔之一。这样的设计带来了几个很大的问题,首先是没法在编译时就报出变量未声明的错误,只有运行时才能知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的);其次,程序员很容易不知不觉地就创建了全局变量(比如打字出错);最后,顶层对象的属性是到处可以读写的,这非常不利于模块化编程。另一方面,window对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合适的。
ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
9.数组的解构赋值
解构赋值
Es6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
数组的解构赋值
如果解构不成功,则值为undefined;不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组;如果右边不是数组则会报错,比如,右边是{}、false/true、数字、null、undefined、NaN;添加默认值,如果赋值为undefined,则使用默认值。如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值,
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
10.对象的解构赋值
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
11.对象的简写写法
es6允许直接在花括号中写变量和函数,作为对象的方法和属性
- 如果属性名和属性相同时,可以只写一个
- 对象中的方法,可以不写冒号和function
12.函数的扩展
1. 默认值
2. rest参数 …变量名
Es6引入rest参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。Rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
3.严格模式
- 设定全局性的严格模式,这是合法的
- 把函数包在一个无参数的立即执行函数里面
13.箭头函数:是一种更加简便函数写法
Es6 允许使用“箭头”(=>)定义函数。
let函数名 = (参数)=>函数体
箭头函数适合用在与this无关的回调函数,比如:数组的方法、计时器
箭头函数不适合用在与this有关的回调函数,比如:事件绑定、事件监听、构造函数、对象的方法
箭头函数的简便写法
- 当参数只有一个时,()可以省略
- 当函数体只有一行代码时,{}可以是省略,当这一行代码是返回值时,return可以省略
14.箭头函数的this指向
箭头函数中的this是静态的,指向定义它的地方,而不是调用它的地方,指向定义箭头函数时所在的作用域下的this的值。
4. 全局环境下,通过let、const、var定义的箭头函数指向window
5. 对象的方法写为箭头函数,函数中的this指向对象所在的作用域中this的值
6. 箭头函数中的this,不可以通过call和apply或者bind修改
7. 不可以作为构造函数实例对象,箭头函数不可以使用new关键词
8. 在箭头函数中不可以使用arguments
15.扩展运算符
- 在解构赋值中使用
- rest参数
- 需要把数组转为参数序列
- 复制数组
- 合并数组
- 复制对象
- 合并对象
16.字符串的方法
① includes:返回布尔值,表示是否找到了参数字符串。
② startsWith:返回布尔值,表示参数字符串是否在原字符串的头部。
③ endsWith:返回布尔值,表示参数字符串是否在原字符串的尾部。
④ repeat:返回一个新字符串,表示将原字符串重复n次。
⑤ 补全字符串,如果某个字符串不够指定长度,会在头部或尾部补全,
padStart()用于头部补全,padEnd()用于尾部补全。
⑥ 消除空格:trimStart:头部消除 trimEnd:尾部消除。
17.数组的方法
- Array.from()将类数组转化为数组
- Array.of()将一组值,转换为数组
- copyWithin()在数组内部进行截取替换
- find()找出第一个符合条件的数组成员
- findIndex()返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
- fill()填充数组,第一个参数:填充内容 第二个参数:填充的起始位置 第三个参数:填充的结束位置
- Includes():返回一个布尔值,表示某个数组是否包含给定的值。
- map()映射数组 ,返回新数组 ,参数回到函数
- filter()过滤数组,过滤出满足条件的数据,返回新数组
- reduce()返回值 回调函数最后一次的返回值
第一个参数:回调函数的返回值(除了第一项)
第二个参数:每一项数据(除了第一项)
第三个参数:每一项数据下标(除了第一项)
第四个参数:数组本身 - every()当回调函数的返回值为false时停止循环,遇假则停,every函数的返回值为boolean类型
- some()当回调函数的返回值为true时停止循环,遇真则停,some函数的返回值为boolean类型
18.Symbol
- Es6引入Symbol的原因:
保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。 - Symbol的概念
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。 - 创建Symbol:
- 通过Symbol()函数
- 通过Symbol.for()属性创建
Symbol()写法没有登记机制,所以每次调用都会返回一个不同的值.
Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for(“cat”)30 次,每次都会返回同一个 Symbol 值,但是调用Symbol(“cat”)30 次,会返回 30 个不同的 Symbol 值。
- 获取对象中的Symbol属性对应的值:
通过 Object.getOwnPropertySymbols()例如:obj[Object.getOwnPropertySymbols(obj)[0]] - 内置方法(11种)
hasInstance判断是否为该对象的实例
isConcatSpreadable species match replace search split iterator toPrimitive toStringTag unscopables - Symbol的特点
是唯一的,解决对象属性名冲突
不能与其他类型进行运算 ,但是可以转为String()、toString()字符串
不可以使用for…in或者for…of进行循环,可以通过Object.getOwnPropertySymbols()获取
19.Set
Es6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构。
Set函数可以接受一个数组(或者具有iterable接口的其它数据类型)作为参数,用来初始化。
注意:添加数据 认为NaN跟NaN是相同的,所以添加多个NaN时,只添加一个NaN 添加对象或数组时,多个空对象或空数组是不同的,所以都可以添加上
实例属性 size 个数
实例方法:
add(value):添加某个值,返回Set结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
Clear():清除所有成员,没有返回值。
遍历方法:
Keys()返回键名的遍历器
Values()返回键值的遍历器
entries()返回键值对的遍历器
forEach()使用回调函数遍历每个成员
20.Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
作为构造函数,Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。
实例方法:
set() get() has() clear() values() keys() entries() forEach()
21.Iterator(迭代器)
原因:JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
定义:遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
Iterator的 遍历过程:
① 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
② 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
③ 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
④ 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
22.generator
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍 Generator 函数的语法和 API,它的异步编程应用请看《Generator 函数的异步应用》一章。
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
23.Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
24. Async
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await
async函数对 Generator 函数的改进,体现在以下四点。
(1)内置执行器。
Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
asyncReadFile();
上面的代码调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。
(2)更好的语义。
async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性。
co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
(4)返回值是 Promise。
async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值
25.Module
JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名
export default命令,为模块指定默认输出。