ES6常用语法汇总

ECMAScript 6(简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。

虽然现在不是所有浏览器都能兼容ES6全部特性,但是越来越多的程序员在实际项目当中已经开始使用ES6。同时配合Babel等工具,ES6的普及也是越来越广泛。下面总结一下开发当中使用频率比较高的ES6新语法。

let, const

这两个的用途和var类似,都是用来声明变量,下面例子

1
2
3
4
5
6
7
8
9
var name = 'zach'

while (true) {
var name = 'obama'
console.log(name) // obama
break
}

console.log(name) // obama

使用var两次输出都是obama,因为ES5只有全局作用域和函数作用域,没有块级作用域。而let实际上为JavaScript新增了块级作用域。用它所声明的变量,只在let命令所在的代码块内有效。

1
2
3
4
5
6
7
8
9
let name = 'zach'

while (true) {
let name = 'obama'
console.log(name) // obama
break
}

console.log(name) // zach

另一个var带来的不合理场景就是用来计数循环变量泄露为全局变量

1
2
3
4
5
6
7
var a = []
for (var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i)
}
}
a[6]() // 10

代码当中变量i是var声明的,在全局范围内都有效。所以每一次循环,新的i值都会覆盖旧值,导致输出的是最后一轮的i的值。而使用let则不会出现这个问题。

1
2
3
4
5
6
7
8
var a = []

for (let i = 0; i < 10; i++) {
a[i] = function() {
console.log(i)
}
}
a[6]()

const也用来声明变量,但是声明的是常量。一旦声明,常量的值就不能改变

1
2
3
const PI = Math.PI

PI = 33 // Module build failed: SyntaxError: /es6/app.js: "PI" is read-only

当我们尝试改变const声明的常量时,浏览器就会报错。
const有一个很好的应用场景,就是当我们引入第三方库时声明的变量,用const来声明可以避免未来不小心重命名而导致出现bug

1
const monent = require('moment')

class,extends,super

当初最早是先接触了python的面向对象的语法,后来接触到js的原型链,继承时觉得特别繁杂奇怪,直到再后来看到ES6中新添加的语法特性,顿时就觉得清晰了不少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Animal {
constructor(){
this.type = 'animal'
}
say(say){
console.log(this.type + ' says ' + say)
}
}

let animal = new Animal()
animal.says('hello')

class Cat extends Animal {
constructor(){
super()
this.type = 'cat'
}
}

let cat = new Cat()
cat.says('hello') //cat says hello

super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

ES6的继承机制,实质就是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。

arrow function

1
2
function(i){ return i + 1; } //ES5
(i) => i + 1 //ES6

如果函数体内较复杂,则需用{}把代码包起来

1
2
3
4
5
6
7
function(x, y) {
x++;
y--;
return x + y;
}

(x, y) => {x++; y--; return x + y}

除了语法简洁之外,箭头函数还有一个非常好的优点,当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象,它的this是继承外面的,因此内部this就是外层代码块的this。

代码例子如下

1
2
3
4
5
6
7
8
9
10
11
12
13
class Animal {
constructor(){
this.type = 'animal'
}
says(say){
setTimeout(function(){
console.log(this.type + ' says' + say)
}, 1000)
}
}

var animal = new Animal()
animal.says('hi') //undefined says hi

运行上面的代码会报错,因为setTimeout 中的this是指向全局对象。传统解决方法有两种

第一种是将this传给self,再用self来指代this

1
2
3
4
5
6
says(say){
var self = this;
setTimeout(function(){
console.log(self.type + ' says' + say)
}, 1000)
}

第二种方法是用bind(this)

1
2
3
4
5
says(say){
setTimeout(function(){
console.log(this.type + ' says' + say)
}.bind(this), 1000)
}

现在有了箭头函数,就不需要这么麻烦

1
2
3
4
5
6
7
8
9
10
11
12
13
class Animal {
constructor(){
this.type = 'animal'
}
says(say){
setTimeout( () => {
console.log(this.type + ' says' + say)
}, 1000)
}
}

var animal = new Animal()
animal.say('hi') //animal says hi

template string

当要插入大段的html内容到文档当中去时,传统写法非常麻烦

1
2
3
4
5
6
$("#result").append(
"There are <b>" + basket.count + "</b>" +
"items in your basket, " +
"<em>" + basket.onSale +
"</em> are on sale!"
)

如果用ES6的新特性模板字符串可以直接这么写

1
2
3
4
5
$("#result").append(`
There are <b>${basket.count}<b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`)

destructuring

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,叫解构赋值

1
2
3
4
let cat = 'ken'
let dog = 'lili'
let zoo = {cat: cat, dog: dog}
console.log(zoo) // object {cat: "ken", dog: "lili"}

用ES6完全可以这么写

1
2
3
4
let cat = "ken"
let dog = "lili"
let zoo = {cat, dog}
console.log(zoo) // object {cat: "ken", dog: "lili"}

反过来能这样赋值

1
2
3
let dog = {type: 'animal', many: 2}
let {type, many} = dog
console.log(type, many) // animal 2

default, rest

default 是指可以给函数参数添加默认值

传统方法是这样

1
2
3
4
5
function animal(type){
type = type || 'cat'
console.log(type)
}
animal()

如果用ES6可以直接这样写

1
2
3
4
function animal(type="cat"){
// console.log(type)
}
animal()

rest语法也挺简单

1
2
3
4
function animals(...types){
console.log(types)
}
animals('cat', 'dog', 'fish') //["cat", "dog", "fish"]

import export

传统组织js文件的方式缺乏模块化体系,无法将一个庞大js工程拆分成一个个功能相对独立但相互依赖的小工程,再用简单方法把它们连接起来。这可能导致两个问题。

  1. js代码变得臃肿难以维护
  2. 我们需要注意每个script标签在html的位置,因为它们通常有依赖关系

在ES6之前,我们需要利用第三方方案如CommonJS(服务器端)和AMD(浏览器端,如require.js)

传统的写法

首先是require.js的写法。假设有两个js文件: index.js 和 content.js 现在需要在 index.js 中使用 content.js 返回的结果

1
2
3
4
// content.js
define('content.js', function(){
return 'A cat';
})

然后require

1
2
3
4
// index.js
require(['./content.js'], function(animal){
console.log(animal);
})

CommonJS

1
2
3
4
5
//index.js
var animal = require('./content.js')

//content.js
module.exports = 'A cat'

ES6的写法

1
2
3
4
5
//index.js
import animal from './content'

//content.js
export default 'A cat'