ES6简介
ECMAScript6(简称ES6)是 JavaScript 语言的下一代标准。在2015年6月正式发布,所以又称ES2015。
由于目前并不是所有浏览器都能兼容 ES6 全部特性,但是 ES6 在实际项目中的广泛使用已成为一种趋势。所以作为一个前端开发者,ES6 的语法是我们必须掌握的。
在讲解 ES6 语法之前,我们得先了解下Babel。
Babel
Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。可在Babel官网 (http://babeljs.io/) 查看:
使用Gulp 和 Babel 将 ES6 代码转换成 ES5 代码具体过程如下:
安装依赖
安装全局 Gulp
1npm install -g gulp安装项目中使用的 Gulp
1npm install --save-dev gulp安装 Gulp 上 Babel 的插件
1npm install --save-dev gulp-babel安装 Babel 上将 ES6 转换成 ES5 的插件
1npm install --save-dev babel-preset-es2015
Gulp 配置
gulpfile.js 的内容
12345678var gulp = require("gulp");var babel = require("gulp-babel");gulp.task("default", function () {return gulp.src("src/**/*.js") // ES6 源码存放的路径.pipe(babel()).pipe(gulp.dest("dist")); //转换成 ES5 存放的路径});如果要生成 Soucemap, 则用 gulp-sourcemaps
12345678910111213var gulp = require("gulp");var sourcemaps = require("gulp-sourcemaps");var babel = require("gulp-babel");var concat = require("gulp-concat");gulp.task("default", function () {return gulp.src("src/**/*.js").pipe(sourcemaps.init()).pipe(babel()).pipe(concat("all.js")).pipe(sourcemaps.write(".")).pipe(gulp.dest("dist"));});
Babel 配置
在项目根路径创建文件 .babelrc。内容为:
执行转换
命令行中执行
ES6常用特性
let
, const
, class
, extends
, super
, arrow functions
, template string
, destructuring
, default
, rest arguments
这些是ES6最常用的几个语法,基本上学会它们,就可以满足我们日常的使用!下面就用用最通俗易懂的语言和例子来讲解它们。
let 与 const
这两个的用途与var类似,都是用来声明变量的,但在实际运用中都有各自的特殊用途。
ES5只有全局作用域、函数作用域和eval作用域,没有块级作用域,这带来很多不合理的场景。
第一种,变量提升:就是var关键字声明变量。无论声明在何处,都会被视为声明在函数的最顶部;不在函数内即在全局作用域的最顶部。这样就会引起一些误解。
而 let 就不会被变量提升
第二种场景就是内层变量覆盖外层变量。例如:
而let则实际上为JavaScript新增了块级作用域。用它所声明的变量,只在let命令所在的代码块内有效。
第三种场景就是用来计数的循环变量泄露为全局变量,例如:
上面代码中,变量 i 是 var 声明的,在全局范围内都有效。所以每一次循环,新的 i 值都会覆盖旧值,导致最后输出的是最后一轮的i的值。而使用 let 则不会出现这个问题。
const也用来声明变量,但是声明的是常量。一旦声明,常量的值就不能改变。
当我们尝试去改变用const声明的常量时,浏览器就会报错。const有一个很好的应用场景,就是当我们引用第三方库的时声明的变量,用const来声明可以避免未来不小心重命名而导致出现bug:
Set 和 Map 数据结构
与 Array 增、删、改、查对比
class、extends、super
ES5中原型、构造函数,继承问题一直困扰我们。ES6引入了Class(类)的概念。新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
上面代码首先用class定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。简单地说,constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。
Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个Boy类,该类通过extends关键字,继承了Human类的所有属性和方法。
super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
arrow function 箭头函数
ES6中新增的箭头操作符 =>
简化了函数的书写。操作符左边为输入的参数,而右边则是进行的操作以及返回的值,可以说是 ES6 最最常用的一个新特性了,看下面的实例:
JavaScript语言的this对象指向是一个头疼的问题,在对象方法中使用this,必须非常小心。
上面代码之所以不能输出 Davis,这是因为setTimeout中的this指向的是全局对象。为了让它能够正确的运行,传统的解决方法有两种:
使用箭头函数,非常方便:
当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,它的this是继承外面的,因此内部的this就是外层代码块的this。
template string 模板字符串
ES6中允许使用反引号 ` 来创建字符串,此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraible}。
不再通过 \ 来做多行字符串拼接,模板字符串可以多行书写:
模板字符串中所有的空格、新行、缩进,都会原样输出在生成的字符串中。
destructuring 解构
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
用途一,交换变量的值,不再需要中间变量
用途二,提取JSON数据
用途三,函数参数的定义
default默认参数
default很简单,就是默认值。现在可以在定义函数的时候指定参数的默认值,而不用像以前那样通过逻辑或操作符来达到目的了。
注意: say2(name=’tom’)这里的等号,指的是没有传这个参数,则设置默认值Davis,而不是给参数赋值。
rest参数
rest参数只包括那些没有给出名称的参数;
rest参数是Array的实例,可以直接应用sort, map, forEach, pop等方法;
rest参数之后不能再有其它参数(即,只能是最后一个参数);
函数的length属性,不包括rest参数;
Proxy 代理
Proxy可以监听对象身上发生了什么事情,并在这些事情发生后执行一些相应的操作。让我们对一个对象有了很强的追踪能力,同时在数据绑定方面也很有用处。
Promise
Promise对象状态
Promise/A+规范, 规定Promise对象是一个有限状态机。它三个状态:
- pending(执行中)
- Resolved(已完成)
- Rejected(已失败)
其中pending为初始状态,Resolved和rejected为结束状态(表示promise的生命周期已结束)。
常用关键点:
在Promise定义时,函数已经执行了;Promise构造函数只接受一个参数,即带有异步逻辑的函数。这个函数在 new Promise 时已经执行了。只不过在没有调用 then 之前不会 resolve 或 reject。
在then方法中通常传递两个参数,一个 resolve 函数,一个 reject 函数。reject就是出错的时候运行的函数。resolve 函数必须返回一个值才能把链式调用进行下去。
- resolve 返回一个新 Promise
返回一个新Promise之后再调用的then就是新Promise中的逻辑了。 - resolve 返回一个值
返回一个值会传递到下一个then的resolve方法参数中。
Generator
Generator函数跟普通函数的写法有非常大的区别:
- function关键字与函数名之间有一个 *;
- 函数体内部使用yield语句,定义不同的内部状态;
|
|
第一次输出fn.next()返回一个简单的对象{value: “a”, done: false},’a’就是f函数执行到第一个yield语句之后得到的值,false表示f函数还没有执行完,只是在这暂停。
第二次,返回的就是{value: “b”, done: false},说明f函数运行到了第二个yield语句,返回的是该yield语句的返回值’b’。返回之后依然是暂停。
第三次,第四次同理,这样整个f函数就运行完毕了。
异步操作的同步化写法
Generator函数的暂停执行的效果,意味着可以把异步操作写在yield语句里面,等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield语句下面,反正要等到调用next方法时再执行。所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
例子:假设我在测试服务器的某目录建了4个文件,分别是’test.html’、’a.html’、’b.html’、’c.html’,后三个文件的文件内容跟文件名相同,现在我编辑’test.html’的代码,想要先ajax-get相对网址’a.html’,然后再回调里ajax-get相对网址’b.html’,然后在回调里ajax-get相对网址’c.html’。
|
|
强调:只有当yield后面跟的函数先执行完,无论执行体里面有多少异步回调,都要等所有回调先执行完,才会执行等号赋值,以及再后面的操作。这也是yield最大的特性。
export、import
export用于对外输出本模块(一个文件可以理解为一个模块)变量的接口;
import用于在一个模块中加载另一个含有export接口的模块。
导出一组对象
导出模块文件app.js:
模块导出了两个对象:Human类和walk函数,能被其他文件使用。而play函数没有导出,为此模块私有,不能被其他文件使用。
main.js导入app.js模块
Default导出
使用关键字default,可将对象标注为default对象导出。default关键字在每一个模块中只能使用一次。
main.js导入app.js模块
结语
以上这些是ES6最常用的几个语法,基本上学会它们,就可以满足我们日常的使用!ES6的新语法特性让前端和后端的差异越来越小了,我们必须要了解这些新的前沿知识(虽然ES8已经到来,但我相信目前还是ES6的时代),才能更好运用于项目中。