那些必会用到的 ES6 精粹(下)
Promise 是异步编程的一种解决方案。
Promise 对象代表一个异步操作 有三种状态 pending 进行中 、fulfilled 已成功 和rejected 已失败 。
Promise 对象的状态改变 只有两种可能 从 pending 变为 fulfilled 和从 pending 变为
rejected。只要这两种情况发生 状态就凝固了 不会再变了 会一直保持这个结果 这时就称为 resolved 已定型
const someAsyncThing function(flag) { return new Promise(function(resolve, reject) { if(flag){ resolve( ok }else{ reject( error ) someAsyncThing(true).then((data) { console.log( data: ,data); // 输出 ok }).catch((error) { console.log( error: , error); // 不执行 someAsyncThing(false).then((data) { console.log( data: ,data); // 不执行 }).catch((error) { console.log( error: , error); // 输出 error })
上面代码中 someAsyncThing 函数成功返回 ‘OK’, 失败返回 ‘error’, 只有失败时才会被 catch 捕捉到。
最简单实现
// 发起异步请求 fetch( /api/todos ) .then(res res.json()) .then(data ({ data })) .catch(err ({ err }));
来看一道有意思的面试题
setTimeout(function() { console.log(1) }, 0); new Promise(function executor(resolve) { console.log(2); for( var i i 10000 ; i ) { i 9999 resolve(); console.log(3); }).then(function() { console.log(4); console.log(5);
这道题应该考察 JavaScript 的运行机制的。
首先先碰到一个 setTimeout 于是会先设置一个定时 在定时结束后将传递这个函数放到任务队列里面 因此开始肯定不会输出 1 。
然后是一个 Promise 里面的函数是直接执行的 因此应该直接输出 2 3 。
然后 Promise 的 then 应当会放到当前 tick 的最后 但是还是在当前 tick 中。
因此 应当先输出 5 然后再输出 4 。
最后在到下一个 tick 就是 1 。
答案 “2 3 5 4 1”
ES2017 标准引入了 async 函数 使得异步操作变得更加方便。
async 函数的使用方式 直接在普通函数前面加上 async 表示这是一个异步函数 在要异步执行的语句前面加上 await 表示后面的表达式需要等待。async 是 Generator 的语法糖
async 函数内部 return 语句返回的值 会成为 then 方法回调函数的参数。async function f() { return hello world f().then(v console.log(v)) // hello world
上面代码中 函数 f 内部 return 命令返回的值 会被 then 方法回调函数接收到。
async 函数内部抛出错误 会导致返回的 Promise 对象变为 reject 状态。抛出的错误对象会被 catch 方法回调函数接收到。async function f() { throw new Error( 出错了 f().then( result console.log(result), error console.log(error) // Error: 出错了
下面是一个例子:
async function getTitle(url) { let response await fetch(url); let html await response.text(); return html.match(/ title ([\s\S] ) \/title /i)[1]; getTitle( https://tc39.github.io/ecma262/ ).then(console.log( 完成 )) // ECMAScript 2017 Language Specification
上面代码中 函数 getTitle 内部有三个操作 抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成 才会执行 then 方法里面的 console.log。
methods:{ getToken() { return new Promise((resolve, reject) { this.$http.post( /token ) .then(res { if (res.data.code 200) { resolve(res.data.data) } else { reject() .catch(error { console.error(error); getUserInfo(token) { return new Promise((resolve, reject) { this.$http.post( /userInfo ,{ token: token .then(res { if (res.data.code 200) { resolve(res.data.data) } else { reject() .catch(error { console.error(error); async initData() { let token await this.getToken() this.userInfo this.getUserInfo(token) }
import 导入模块、export 导出模块 // example2.js // 导出默认, 有且只有一个默认 export default const example2 { name : my name , age : my age , getName function(){ return my name } //全部导入 // 名字可以修改 import people from ./example2.js -------------------我是一条华丽的分界线--------------------------- // example1.js // 部分导出 export let name my name export let age my age export let getName function(){ return my name } // 导入部分 // 名字必须和 定义的名字一样。 import {name, age} from ./example1.js //有一种特殊情况 即允许你将整个模块当作单一对象进行导入 //该模块的所有导出都会作为对象的属性存在 import * as example from ./example1.js console.log(example.name) console.log(example.age) console.log(example.getName()) -------------------我是一条华丽的分界线--------------------------- // example3.js // 有导出默认, 有且只有一个默认 // 又有部分导出 export default const example3 { birthday : 2018 09 20 export let name my name export let age my age export let getName function(){ return my name } // 导入默认与部分 import example3, {name, age} from ./example1.js
总结
1.当用 export default people 导出时 就用 import people 导入 不带大括号 2.一个文件里 有且只能有一个 export default。但可以有多个 export。 3.当用 export name 时 就用 import { name }导入 记得带上大括号 4.当一个文件里 既有一个 export default people, 又有多个 export name 或者 export age 时 导入就用 import people, { name, age } 5.当一个文件里出现 n 多个 export 导出很多模块 导入时除了一个一个导入 也可以用 import * as example
对于 Class 小汪用在 react 中较多。
//定义类 class FunSum { constructor(x, y) { this.x this.y sum() { console.log( this.x this.y ) // 使用的时候 也是直接对类使用new命令 跟构造函数的用法完全一致。 let f new FunSum(10, 20); f.sum() // 30
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color color; toString() { return this.color super.toString(); // 调用父类的toString() }
上面代码中 constructor 方法和 toString 方法之中 都出现了super关键字 它在这里表示父类的构造函数 用来新建父类的 this 对象。
子类必须在 constructor 方法中调用 super 方法 否则新建实例时会报错。这是因为子类自己的 this 对象 必须先通过父类的构造函数完成塑造 得到与父类同样的实例属性和方法 然后再对其进行加工 加上子类自己的实例属性和方法。如果不调用 super 方法 子类就得不到 this 对象。
class Point { /* ... */ } class ColorPoint extends Point { constructor() { let cp new ColorPoint(); // ReferenceError
上面代码中 ColorPoint 继承了父类 Point 但是它的构造函数没有调用 super 方法 导致新建实例时报错。
一篇文章带你学会整个ES6 ES 全称 EcmaScript,是脚本语言的 规范 ,而平时经常编写的 JavaScript ,是 EcmsScript 的 一种实现 ,所以 ES 新特性其实是指 JavaScript 的新特性 。
浅谈一下ES6的提升 es6的提升 在es6之前,我们定义定义变量的时候,只能使用var关键字来定变量,这样有一个问题,var定义的变量会成为全局变量。