专栏首页call_me_R谈谈ES6语法(汇总下篇)

谈谈ES6语法(汇总下篇)

客套话不多说了,直奔下篇的内容~

async函数

ES2017标准引入了async函数,使得异步操作更加方便。async函数是Generator函数的语法糖。不打算写Generator函数,感兴趣的话可以看文档。与Generator返回值(Iterator对象)不同,async返回的是一个Promise对象。

用法

async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

async function getStockPriceByName(name) {
	const symbol = await getStockSymbol(name);
	const stockPrice = await getStockPrice(symbol);
	return stockPrice;
}
getStockPriceByName('goog').then(function(result) {
	console.log(result);
})
复制代码

再来看几种情况加深下印象:

function fun1() {
  console.log('fun1');
  return 'fun1 result';
}
async function test() {
  const result1 = await fun1();
  console.log(result1);
  console.log('end');
}
test();
// 输出 
// 'fun1'
// 'fun1 result'
// 'end'
复制代码
async function fun2() {
  console.log('fun2');
  return 'fun2 result';
}
async function test() {
  const result2 = await fun2();
  console.log(result2);
  console.log('end');
}
test();
// 输出
// 'fun2'
// 'fun2 result'
// 'end'
复制代码

正常情况下,await命令后面是一个Promise对象,返回该对象的结果。如果不是Promise对象,就直接返回对应的值。

async function fun3() {
  console.log('fun3');
  setTimeout(function() {
    console.log('fun3 async');
    return 'fun3 result';
  }, 1000)
}
async function test() {
  const result3 = await fun3();
  console.log(result3);
  console.log('end');
}
test();
// 输出
// 'fun3'
// undefined
// 'end'
// 'fun3 async'
复制代码
async function fun4() {
  console.log('fun4');
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('fun4 async');
      resolve('fun4 result');
    }, 1000);
  })
}
async function test() {
  console.log(result4);
  console.log('fun4 sync');
  console.log('end');
}
test();
// 输出
// 'fun4'
// 'fun4 async'
// 'fun4 result'
// 'fun4 sync'
// 'end'
复制代码

模拟sleep

JavaScript一直没有休眠的语法,但是借助await命令就可以让程序停顿指定的时间。【await要配合async来实现】

function sleep(interval) {
	return new Promise(resolve => {
		setTimeout(resolve, interval);
	})
}
// use
async function one2FiveInAsync() {
	for(let i = 1; i <= 5; i++) {
		console.log(i);
		await sleep(1000);
	}
}
one2FiveInAsync();
// 1, 2, 3, 4, 5 每隔一秒输出数字
复制代码

一道题

需求:使用async await改写下面的代码,使得输出的期望结果是每隔一秒输出0, 1, 2, 3, 4, 5,其中i < 5条件不能变。

for(var i = 0 ; i < 5; i++){
    setTimeout(function(){
        console.log(i);
    },1000)
}
console.log(i);
复制代码

之前我们讲过了用promise的方式实现,这次我们用async await方式来实现:

const sleep = (time) => new Promise((resolve) => {
	setTimeout(resolve, time);
});

(async () => {
	for(var i = 0; i < 5; i++){
		console.log(i);
		await sleep(1000);
	}
	console.log(i);
})();
// 符合条件的输出 0, 1, 2, 3, 4, 5
复制代码

比较promise和async

为什么只比较promiseasync呢?因为这两个用得频繁,实在的才是需要的,而且async语法generator的语法糖,generator的说法直接戳async与其他异步处理方法的比较

两者上,async语法写法上代码量少,错误处理能力佳,而且更有逻辑语义化。

假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。

// promise
function chainAnimationsPromise(elem, animations) {

  // 变量ret用来保存上一个动画的返回值
  let ret = null;

  // 新建一个空的Promise
  let p = Promise.resolve();

  // 使用then方法,添加所有动画
  for(let anim of animations) {
    p = p.then(function(val) {
      ret = val;
      return anim(elem);
    });
  }

  // 返回一个部署了错误捕捉机制的Promise
  return p.catch(function(e) {
    /* 忽略错误,继续执行 */
  }).then(function() {
    return ret;
  });

}
复制代码
// async await
async function chainAnimationsAsync(elem, animations) {
  let ret = null;
  try {
    for(let anim of animations) {
      ret = await anim(elem);
    }
  } catch(e) {
    /* 忽略错误,继续执行 */
  }
  return ret;
}
复制代码

类class

ES6之前,是使用构造函数来模拟类的,现在有了关键字class了,甚是开心?

function Person() {}
Person.prototype.sayHello = function(){
	console.log('Hi');
};
复制代码
class Person{
	sayHello(){
		console.log('Hi!');
	}
}
复制代码

constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法,一个类中必须有construtor方法,如果没有显式定义,一个空的constructor方法会默认添加。

class Person{}
// 等同于
class Person{
	constructor(){}
}
复制代码

construtor方法也就类似构造函数,在执行new的时候,先跑构造函数,再跑到原型对象上。

取值函数(getter)和存值函数(setter)

与ES5一样,在的内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为

class MyClass {
	get prop() {
		return 'getter';
	}
	set prop(value) {
		console.log(`setter: ${ value }`)
	}
}

let inst = new MyClass();

inst.prop = 123;
// 'setter: 123'

console.log(inst.prop);
// 'getter'
复制代码

this的指向

类的方法内部如果含有this,它默认是指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。

class Person{
	constructor(job) {
		this.job = job;
	}
	printJob() {
		console.log(`My job is ${ this.job }`);
	}
  sayHi() {
    console.log(`I love my job -- ${ this.job }.`)
  }
}
const person = new Person('teacher');
person.printJob(); // 'My job is teacher'
const { sayHi } = person;
sayHi(); // 报错: Uncaught TypeError: Cannot read property 'job' of undefined
复制代码

上面的代码中,sayHi方法单独使用,this会指向该方法运行时所在的环境(由于class内部是严格模式,所以this实际上指向undefined)。

修正上面的错误也很简单,也是我们在react开发中经常使用的一种手段:在调用构造函数实例化的时候直接绑定实例(this),修改如下:

class Person{
	constructor(job) {
		this.job = job;
		this.sayHi = this.sayHi.bind(this);
	}
}
复制代码

继承

ES5中继承的方式我之前有整理过--JavaScript 中的六种继承方式

ES6中的继承通过extends关键字实现,比ES5的实现继承更加清晰和方便了。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    this.color = color;
  }
}

let cp = new ColorPoint(25, 8, 'green'); // 报错: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
复制代码

上面这样写,不能继承构造函数里面的属性值和方法。需要在子类的构造函数中加上super关键字。改成下面这样即可:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的construtor(x, y),相当于ES5中的call。注意的是,super要放在子类构造函数的第一行
    this.color = color;
  }
}

let cp = new ColorPoint(25, 8, 'green');
复制代码

module模块

在ES6之前,社区制定了一些模块的加载的方案,最主要的有CommonJSAMD两种。前者用于服务器,后者用于浏览器。

// CommonJS
let { stat, exists, readFile } = require('fs');

ES6在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJSAMD规范,成为浏览器和服务器通用的模块解决方案。

// ES6模块
import { stat, exists, readFile } from 'fs';

各种好处详细见文档

export命令

export命令用于规定模块的对外接口 。

**一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。**你可以理解为一个命名空间~

想要获取模块里面的变量,你就需要导出export

// profile.js
const name = 'jia ming';
const sayHi = function() {
	console.log('Hi!');
}

export { name, sayHi };
复制代码

还有一个export default命令,方便用户(开发者啦)不用阅读文档就能加载模块(实际上就是输出一个default变量,而这个变量在import的时候是可以更改的):

// export-default.js
export default function () {
  console.log('foo');
}
复制代码

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字

// import-default.js
import customName from './export-default';
customName(); // 'foo'

import命令

import命令用于输入其他模块提供的功能。使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块。

// main.js
import { name, sayHi } from './profile.js';

function printName() {
	console.log('My name is ' + name);
}
复制代码

至此,本系列文章谈谈ES6语法已经写完,希望文章对读者有点点帮助。本系列的内容是个人觉得在开发中比较重要的知识点,如果要详细内容的话,请上相关的文档查看~?

参考和后话

本次的ES6语法的汇总总共分为上、中、下三篇,本篇文章为下篇。

系列文章至此已经完结!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 谈谈ES6语法(汇总下篇)

    ES2017标准引入了async函数,使得异步操作更加方便。async函数是Generator函数的语法糖。不打算写Generator函数,感兴趣的话可以看文档...

    嘉明
  • 【译】使用 JavaScript 创建图

    比如,你可能有一个(关于)人物和电影的图表,其中每个人可以有多个喜欢的电影,但是电影没有喜欢的人。

    嘉明
  • 【译】理解JavaScript中的This,Bind,Call和Apply

    this关键词在JavaScript中是个很重要的概念,也是一个对初学者和学习其他语言的人来说晦涩难懂。在JavaScript中,this是一个对象的引用。th...

    嘉明
  • 前端基础-JavaScript函数进阶

    这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression)

    cwl_java
  • Es6浅析

    Es6浅析 Babel 是一个 JavaScript编译器,它可以把我们编写的符合 ECMAScript 6 标准的代码完美地转换为 ECMAScript 5 ...

    IMWeb前端团队
  • Vue生命周期

    这是Vue文档里关于实例生命周期的解释图 那么下面我们来进行测试一下 <section id="app-8"> {{data}} </section> ...

    蓓蕾心晴
  • 北大才女总结:机器学习的概念、历史和未来

    提起机器学习,我们不得不给机器学习下一个准确的定义。在直观的层面,如果说计算机科学是研究关于算法的科学,那么机器学习就是研究关于“学习算法”的科学,或者说,不同...

    double
  • Es6浅析

    Babel 是一个 JavaScript编译器,它可以把我们编写的符合 ECMAScript 6 标准的代码完美地转换为 ECMAScript 5 标准的代码,...

    IMWeb前端团队
  • 10个JavaScript难点

    为了保证可读性,本文采用意译而非直译,并且对示例代码进行了大量修改。另外,本文版权归原作者所有,翻译仅用于学习。

    Fundebug
  • 超全整理前端开发面试题——CSS篇(2016年)

    介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的? (1)有两种,IE 盒子模型、W3C 盒子模型; (2)盒模型:内容(content)、填充...

    前朝楚水

扫码关注云+社区

领取腾讯云代金券