为什么再次聊起ECMAScript?很多前端er没弄明白语言和平台之间的关系。我们通常把ECMAScript看做JavaScript的标准化规范,实际上JavaScript是ECMAScript的扩展语言。因为在ECMAScript中只提供了最基本的语法,只约定的代码该如何编写。
JavaScript语言本身指的是ECMAScript,并且在基础上实现了扩展,在浏览器环境可以操作BOM和DOM,在node环境可以读写文件之类操作。
浏览器环境
Node环境
ES6新特性参考文档:www.ecma-international.org/ecma-262/6.…
let与块级作用域
作用域-代码中的成员能起作用的范围。在ES6之前只有两种作用域:全局作用域、函数作用域。ES6新增了块级作用域。
支持块级作用域
之前的变量声明var方式是不安全的,在块级内声明后外部也还能访问到。新的let、const声明方式只能在所声明的代码块中访问,外部是访问不到的。
所以Script作用域,是不是就是因为块级作用域而设定的呢?不将变量放到顶级作用域。
不会变量声明提升
var声明变量会提升到代码最顶部位置,
const常量
在let基础上多了一个“只读”的属性,可以使用Object.defineProperty方法去模拟。它禁止的是重新指向一个新的内存地址,并不会禁止修改常量中的属性成员。
解构
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。数组、对象解构可以通过=给默认值,对象解构还可以通过:重新命名变量名。
重命名后同样可以给默认值,const { name: objName = 'jack' } = obj。
模板字符串
可以通过反斜杠\去转义。并支持多行字符串,不需要\n,有些像
标签。${}可以嵌入任何JS语句,返回值会嵌入模板字符串。
带标签的模板字符串
模板字符串前可以带一个函数,函数会接受以${}分割开的字符串数组,以及插值变量,如下代码所示:
function myTagFunc (strings, name, gender) {
// console.log(strings, name, gender)
// return '123'
const sex = gender ? 'man' : 'woman'
return strings[0] + name + strings[1] + sex + strings[2]
}
const result = myTagFunc`hey, ${name} is a ${gender}.`
函数返回值就是模板字符串返回值。其好处是方便对字符串再加工,方便可读性。可实现些小型模板引擎、文字输入检测、条件判断等等功能。
字符串扩展方法
- startsWith
- endsWith
- includes
参数默认值和剩余参数
在函数定义时,参数可通过=传递默认值。
剩余参数就是使用扩展运算符,自动收集剩余的参数。取代arguments对象去获取参数。并且它应该出现在形参最后一位,并只可使用一次。
展开数组
通过...按顺序展开数组的每一项。
箭头函数
语法更简洁。并且它没有this问题,只指向定义其的作用域。了解其源码。
对象新增特性
字面量增强
可以直接写属性名,简化代码。
可以直接以函数方式定义函数属性。
可以通过计算属性名,直接添加不确定变量作为属性名。
Object.assign
可将多个源对象属性复制混入到一个目标对象中。
复制一份新的对象,修改其属性不会对外造成影响。
Object.is
比较两个值是否相等,在这个方法里两个NaN是相等的。平常还是建议使用===。
- Object.keys()
- Object.values()
- Object.entries()...
Proxy
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
相对于Object.defineProperty更加强大:
- defineProperty只能监视到对象属性的读写,而proxy可以监视到更多对象操作,比如delete操作、对象中方法的调用等等。Proxy 支持的拦截操作一览,一共 13 种。
- 更好的支持数组对象的监视,一般监听数组对象的变化是通过重写数组方法的方式,在Vue中就是使用这样的方式,通过重写push、shift之类的方法,去劫持对应的方法调用的过程。
- Proxy是以非侵入的方式监管了对象的读写。我们使用proxy不需要对一个已经定义好的对象本身去做任何的操作,就可以监视到其内部成员的读写。而Object需要单独对需要监听的属性编写读写操作,需要做很多额外操作。
Reflect
Reflect是一个静态类,所以它不能被new。Reflect内部封装了一系列对对象的底层操作。Reflect对象一共有 13 个静态方法。这些静态方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。
Reflect成员方法就是Proxy处理对象的默认实现。他存在的意义是统一提供了一套用于对象操作的API,因为对象存在类似 in、delete这样的操作符,有用静态方法,就会很混乱。Reflect就规范了关于对象操作的使用方式。
class类
在class中直接定义方法是绑定在prototype上,以等式赋值则是绑定在实例上。类的继承中,可以通过super调取父类的原型方法,但是不能通过其获取实例(this)方法。
Set、Map
Set集合可以通过扩展运算符...展开,也可以使用Array.from转化为一个数组。
Map和对象类似,但是Map可以以复杂类型作为key,而对象如果以复杂类型作为key,则会使用toString转为字符串。Map能映射两个任意键值对之间的关系。
Symbol
ES2015中,新增了一个基本数据类型Symbol。对象已支持Symbol作为key。所以我们可以借助Symbol实现对象的私有成员:
const name = Symbol()
const person = {
[name]: 'zce',
say () {
console.log(this[name])
}
}
由于无法创建出一样的 Symbol 值,所以无法直接访问到 person 中的「私有」成员:person[Symbol()]。所以只能通过方法调用获取。
person.say()
目前最主要作用就是为对象添加一个独一无二的属性名。未来还会增加一个BigInt数据类型。
补充
Symbol每次创建的都是一个唯一的值,无论描述文本是否一样,得到的都是一个全新的值。想在全局复用一个相同的Symbol值,可以定义为全局变量或者使用Symbol的静态方法for。
const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
相同的字符串一定会返回相同的值,这方法维护的是字符串和Symbol间的映射关系,所以传入非字符串会转为字符串。
Symbol还提供了很多内置常量,内置的 Symbol 值,可以改写对象内部的一些方法。
可以通过Object.getOwnPropertySymbols(obj)方法获取到一个对象内的所有Symbol属性。
for...of 循环
相较于forEach方法,for of可以使用break关键词去终止循环。一些伪数组例如arguments对象,元素dom节点都可以使用for of循环。
它只能遍历可迭代对象,普通对象是不能被遍历的。
接口和可迭代接口
这里了解了接口的概念,比如每个数据类型都可以使用toString方法,因为它们都实现了toString接口。明白了这一点,我们就可以为普通对象实现接口了,其原理也很简单,就是给对象上增加一个[Symbol.iterator]属性,该属性接收一个方法,调用方法返回一个带next方法的对象。
示例:
const obj = {
store: ['foo', 'bar', 'baz'],
// 可迭代接口
[Symbol.iterator]: function () {
let index = 0
const self = this
return {
// 迭代器接口
next: function () {
const result = {
value: self.store[index],
done: index >= self.store.length,
}
index++
// 迭代结果接口
return result
}
}
}
}
第一层obj对象,实现了可迭代接口“Iterable”,这个接口它约定内部必须要有一个返回迭代器“Iterator”的方法,这就是一个迭代器接口,它约定内部要有一个用于迭代的next方法。
这个next方法返回一个对象,它被称为迭代结果结果“IterationResult”,它必须有value、done两个属性。这里的三种接口不要弄混。整个对象叫 Iterable;带next方法的对象叫 Iterator;next方法返回的对象叫 IterationResult。
迭代器模式
- 方便外部者使用对象内部的数据。
- 对于大量的数据,可以通过迭代器去一个个去获取。
生成器 generator
创建一个generator实例,通过调用其next方法运行,遇到yield就会暂停下来,并将yield后的值返回。next方法传入的值则会被yield前的变量接收。
生成器主要是用于解决JS异步编程的问题,它实现了async、await方法。
ES2016
- 新增数组includes方法,并可用于查找NaN。
- 新增指数运算符,2 ** 10
ES2017
- Object.values
- Object.entries
- Object.getOwnPropertyDescriptors,它可以完全复制对象的描述符,主要用于对象的深层拷贝
- String增加了两个实例方法 —
padStart和padEnd,这两个方法可以在字符串的首/尾添加其他字符串,类似数组的unshift,push方法。用给的的字符去填充字符串的开始和结束位置,填充到我们的指定长度。
- 允许函数参数添加尾逗号。
- 标准化了Async、Await。