这是我参与「第四届青训营 」笔记创作活动的的第17天
前言:昨天我们介绍了ES5的新特性,今天我们将介绍ES6的新特性。
ES6
ES2015是改动最大的一个版本,基本上对ES2015之前的所有的内容都做了扩展,大体如下图所示:
let、const关键字
在ES6之前只有一种声明变量的方式,就是使用var关键字,在ES2015中新增了let和const关键字来声明变量与常量。推荐所有变量声明都使用let或const。
使用let和const关键字声明的变量或者常量是具有块级作用域的,同时let或者const关键字声明的变量不具有变量提升的特性,且存在暂时性死区的特性。
块级作用域:
{
val a = 1;
} {
let b = 2;
}
console.log(a);
console.log(b); // b not defined
箭头函数
function a() {
// do something
}
等价于
const a = () => {
// do something
}
注意
- 箭头函数的
this是根据执行上下文决定(指向上级作用域的this),内部并不会绑定this。 - 箭头函数中不存在
arguments,所以箭头函数不可以使用arguments。 - 箭头函数不存在预解析,所以我们使用箭头函数必须先定义、再使用。
// 1. this指针
let obj = {
uname : '张三',
fei : function () {
// console.log(this);
// setInterval( function () {
// console.log( this );
// }, 1000 );
setInterval( () => {
console.log( this );
}, 1000 );
}
};
obj.fei();
// 2. arguments参数
const foo = (...args) => {
console.log(arguments) // ReferenceError: arguments is not defined`
console.log(args) // args 是一个数组
};
foo(1, 2, 3, 4) // [ 1, 2, 3, 4 ]
// 3. 函数提升
c(); function c () {console.log('c')}; // OK
d(); const d = () => {console.log('d')}; // d is not defined
数值
在ES2015中对数值的扩展主要时为Math和Number两个对象增加一些方法,以及二进制和八进制的表示方法。在ES2015中使用0b或者0B表示二进制,使用0o或者0O表示八进制。
Number扩展
| 属性/方法名 | 描述 |
|---|---|
| Number.EPSILON | 数值最小精度:2.220446049250313e-16 |
| Number.MIN_SAFE_INTEGER | 最小安全数(-2^53) |
| Number.MAX_SAFE_INTEGER | 最大安全数(2^53) |
| Number.parseInt() | 把参数解析为整数并返回 |
| Number.parseFloat() | 把参数解析为浮点数并返回 |
| Number.isFinite() | 判断是否为有限数值 |
| Number.isNaN() | 判断是否为NaN |
| Number.isInteger() | 判断是否为整数 |
| Number.isSafeInteger() | 判断数值是否在安全范围内 |
Math扩展
| 方法名 | 描述 |
|---|---|
| Math.trunc() | 返回数值整数部分 |
| Math.sign() | 返回数值类型(正数1、负数-1、零0) |
字符串
模板字符串
const val1 = `a string`; // 但一般开发中固定字符串依旧使用''
// 模板字符串可以保留格式
let val2 = `<html>
string
format
</html>
`;
console.log(`${val2}`) // 使用 ${} 进行包裹
String扩展
| 方法名 | 描述 |
|---|---|
| String.fromCodePoint() | 用于从 Unicode 码点返回对应字符 |
| String.raw() | 返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。 |
| String.prototype.codePointAt() | 返回字符对应码点(String.fromCodePoint()的逆操作) |
| String.prototype.normalize() | 把字符的不同表示方法统一为同样形式,返回新字符串(Unicode正规化) |
| String.prototype.repeat() | 把字符串重复n次,返回处理后的字符串 |
| String.prototype.includes() | 判断是否存在指定字符串 |
| String.prototype.startsWith() | 判断字符串是否存在原始字符串的头部 |
| String.prototype.endsWith() | 判断字符串是否存在原始字符串的尾部 |
Iterator
在介绍数组前,我们先了解ES6中的迭代器(Iterator)
迭代器是一种接口,为各种不同的数据结构提供了统一的访问机制,换句话说,只要有任何数据结构部署了迭代接口,就可以使用统一的方式的来遍历它。
实现可迭代接口的数据结构,一般都自身实现或继承了以Symbol.iterator属性的,就属于可迭代对象。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。
一个包含next()方法的对象,才可以称为一个迭代对象。next()对象的会有返回一个对象,对象中包含两个值:
value:迭代器返回的任何JavaScript值。done为true时可省略。done:一个布尔值,为false时表示迭代未停止,为true时立即停止迭代器,且可以省略value的值。
数组
展开运算符...
const arr = [1, 2, 3, 4, 5, 6]
const newArr = [...arr] // 复制数组
console.log(Math.max.call(null, ...arr)) // 6
Array扩展
Array.from():
将类数组对象或者可迭代对象创建为一个新的数组
function foo() {
return Array.from(arguments) // 将 arguments 转换为数组
}
console.log(foo(1, 2, 3, 4, 5, 6)) // [ 1, 2, 3, 4, 5, 6 ]
Array.prototype.find()
根据给定的回调函数,找到匹配的第一个元素,找不到返回undefined
const arr = [1, 2, 3, 4]
arr.find(item => item === 2) // 2(表示元素)
Array.prototype.findIndex()
根据给定的回调函数,找到匹配的第一个元素的索引,找不到返回-1
const arr = [1, 2, 3, 4]
arr.findIndex(item => item === 2) // 1 (表示索引)
Array.prototype.fill()
将给定值填充数组
const arr = [1, 2, 3, 4]
// 将给定值填充索引1-3
arr.fill('999', 1, 3) // [ 1, '999', '999', 4 ]
Array.prototype.keys()
返回一个可迭代的对象(Array Iterator),其内容为数组的key
const arr = ['aaa', true, 123]
const keys = arr.keys()
for (const i of keys) {
console.log(i) // 遍历结果 0 1 2
}
Array.prototype.values()
返回一个可迭代的对象,其内容为数组的value
const arr = ['aaa', true, 123]
const values = arr.values()
for (const i of values) {
console.log(i) // 遍历结果 'aaa', true, 123
}
Array.prototype.entries()
返回一个可迭代的对象,其内容是一个数组[key, value],索引0为原数组的元素,1为原数组该位置的值
const arr = ['aaa', true, 123]
const entries = arr.entries()
console.log(Array.from(entries)) // [[0, 'aaa'], [1, true], [2, 123]]
for (const i of entries) {console.log(i)} // [0, 'aaa'] [1, true] [2, 123]
对象
对象的属性名和属性值一致时可以只写属性名
// es6前
function a (event) {
// ...
return {
event: event,
// ...
}
}
// es6后
const a = (event) => {
// ...
return {
event
// ...
}
}
[]包裹作为属性名
const myName = 'name'
const age = 20
const person = {
myName,
['a' + 'g' + 'e']: age,
}
console.log(person) // { myName: 'name', age: 20 }
Object.is()
用于比较两个值是否相等,用于解决NaN ≠= NaN,+0 === -0的问题
console.log(NaN === NaN) // false
console.log(+0 === -0) // true
console.log(Object.is(NaN, NaN)) // true
console.log(Object.is(+0, -0)) // false
Object.assign()
常用!将所有可枚举属性的值从一个或多个源对象复制到目标对象,并返回目标对象
const person = Object.assign({}, { name: 'name' }, { age: 20 })
console.log(person) // { name: 'name', age: 20 }
类(Class)
ES6 最大的改进之一就是提出了类的概念,在语法层面上有了类。
即使JavaScript的类的概念和其他OOP语言有很大不同,这些年也有很多人诟病JS的类,因为JS中的类从设计之初就是残疾的。但不可否认的是,ES6提出类的概念是对原型链语法的一次重大改进。
- class是语法糖
- 让其他oop语言学习者更容易理解js
- 良好IDE支持
class Person {
constructor(age) {
// 属性
this.myName = 'name'
this.age = age
}
// 静态方法
static print() {
console.log()
}
// 访问器
get myName() {
console.log('getter')
return 'abc'
}
set myName(v) {
console.log('setter' + v)
}
setName(v) {
this.myName = v
}
}
const person = new Person(18)
person.setName('ywanzhou') // 触发 setter 访问器
console.log(person.myName) // 触发 getter 访问器
解构赋值
允许我们使用按照一定的模式,在数组或者对象中提取指定的值
// swap a with b
let a = 1;
let b = 2;
[a, b] = [b, a]
Promise
Promise是ES6中提供的一个异步解决方案,解决了回调地狱的问题。
通过Promise()构造函数可以创建一个promise对象,每一个Promise对象都具有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- resolved: 意味着操作成功完成。
- rejected: 意味着操作失败。
状态的切换只有两种,分别是:
- pending → resolved
- pending → reject
一旦状态发生改变,就不会再次改变。
Promise实例中存在then方法,允许我们在Promise实例中链式调用。每个then方法还会返回一个Promise实例。
new Promise((resolve, reject) => {
console.log('我是第一个Promise中的log')
const a = 'aaa'
resolve(a)
})
.then((a) => {
console.log('我是第一个then中的log,携带:', a)
resolve(a)
})
.then((a) => {
console.log('我是第二个then中的log,但是我出现了异常,携带:', a)
throw new Error('Error')
})
.then(() => {
console.log('我是第三个then中的第一个回调的log,但是我不会执行,因为我上面出现了异常')
}, (err) => {
console.log('我是第三个then中的第二个回调的log,捕捉到错误:', err)
})
.then(() => {
console.log('我是第四个then中的log,我可以正常执行')
})
/*
我是第一个Promise中的log
我是第一个then中的log,携带: aaa
我是第三个then中的第二个回调的log,捕捉到错误: ReferenceError: resolve is not defined at <anonymous>:8:9
我是第四个then中的log,我可以正常执行
*/
有关Promise的一些方法如下:
Promise.prototype.then():它最多需要有两个参数:Promise的成功和失败情况的回调函数;Promise.prototype.catch():等于then方法的第二个参数;Promise.all():将多个实例包装成一个新实例,返回全部实例状态变更后的结果数组(齐变更再返回)Promise.race():将多个实例包装成一个新实例,返回全部实例状态优先变更后的结果(先变更先返回)Promise.resolve():将对象转为Promise对象(等价于new Promise(resolve => resolve()))Promise.reject():将对象转为状态为rejected的Promise对象(等价于new Promise((resolve, reject) => reject()))
Set、Map、WeakSet、WeakMap
Set和WeakSet与数组类似,准确的它他们是集合,这两者的区别就是Set可以存储任何数据类型,而WeakSet只能存储对象的引用,而且是弱引用;Map和WeakMap与对象类似,存储方式是键值对形式的,这两者的区别Map的键值对都是可以是任意的而WeakMap键必须是对象的引用而值可以是任意类型的。
Set对象在实际开发中最常见的就是实现数组去重
const arr = [1, 2, 2, 3, 4, 3, 5]
const set = new Set(arr)
// set对象可以使用 ... 展开 所有项
console.log([...set]) // [ 1, 2, 3, 4, 5 ]
Generator
Generator是ES6中提供的一种异步编程解决方案,定义Generator函数在function关键字和函数名中间使用*星号,函数内部使用yield关键字定义不同的状态。
function* testGenerator() {
// yield定义一个状态
yield 'a'
yield 'b'
return 'c' // 结束Generator,后面即使有yield关键字也无效
yield 'd'
}
const g = testGenerator() // 返回 Generator 对象,通过next()方法移动状态
g.next()
/* { value: 'a', done: false } */
g.next()
/* { value: 'b', done: false } */
g.next()
/* { value: 'c', done: true } */
g.next()
/* {value: undefined, done: true} */