ECMAScript6 学习笔记

安装和使用babel

1
$ sudo npm install -g babel@5


在mac终端使用babel-node可以直接在终端执行es6代码。


babel命令可以直接将es6代码转为es5代码。添加-o参数可以导出一个标准输出文件

1
babel  /Users/trigkit4/Desktop/webStormProject/es6.js -o es5.js

也可以安装babel的浏览器版本:

1
sudo npm install babel-core

就可以在node_modules下找到babel-core文件夹了,找到browser.js通过script标签引入该文件即可。

ES6脚本放在script标签之中,但是要注明type="text/babel"



let声明的变量的作用域在当前的{}块内

for...of

JavaScript原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6提供for...of循环,允许遍历获得键值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const arr = ['red','blue','green'];

for(let a in arr){

console.log(a);//0 1 2

}



for(let a of arr){

console.log(a);//red blue green

}

for...of 不能遍历对象的属性值

for...of循环可以代替数组实例的forEach方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const arr = ['red','blue','green'];

arr.forEach(function (element,index) {

console.log(element);

console.log(index);

});

/*

red

0

blue

1

green

2

*/


对象的扩展

ES6允许在对象之中,只写属性名,不写属性值。这时,属性值等于属性名所代表的变量。

1
2
3
4
5
var foo = 'bar';

var baz = {foo};

console.log(baz);//{foo: 'baz'}

方法也可以简写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var o = {

method(){

return 'hello';

}

}

console.log(o.method());//hello



//等同于

var o = {

method: function(){

return 'hello';

}

};

Object.is()

Object.is用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致。

1
2
3
console.log(Object.is('foo','foo'));//true

console.log(Object.is({},{}));//false

Object.assign()

Object.assign方法用来将源对象(source)的所有可枚举属性,复制到目标对象(target)。它至少需要两个对象作为参数,第一个参数是目标对象,后面的参数都是源对象。

1
2
3
4
5
6
7
8
9
var target = {a:1,b:2};


var source1 = {c:3,d:4};

var source2 = {f:5};


console.log(Object.assign(target,source1,source2));//{ a: 1, b: 2, c: 3, d: 4, f: 5 }

如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

Object.observe()

Object.observe方法用来监听对象(以及数组)的变化。一旦监听对象发生变化,就会触发回调函数。

Object.observe方法目前共支持监听六种变化。

1
2
3
4
5
6
7
8
9
10

- add:添加属性

- update:属性值的变化

- delete:删除属性

- setPrototype:设置原型

- reconfigure:属性的attributes对象发生变化

module

模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。

1
2
3
4
5
6
7
8
9
10
11
12

// profile.js

var firstName = 'Michael';

var lastName = 'Jackson';

var year = 1958;



export {firstName, lastName, year};


输出函数或类 param => expression

1
2
3
4
5
6
7


export function multiply (x, y) {

return x * y;

};

export输出的变量就是本来的名字,但是可以使用as关键字重命名。

1
2
3
4
5
6
7
8
9
10
11

import multiply from 'math';

// 对应的输出

export default function multiply(){}
import { multiply } from 'math';

// 对应的输出

export function multiply(){};

第一组是使用export default时,对应的import语句不需要使用大括号;第二组是不使用export default时,对应的import语句需要使用大括号。

export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export deault命令只能使用一次。

关键字export default后可跟随任何值:一个函数、一个类、一个对象字面量,只要你能想到的都可以。


import

import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

1
2

import { lastName as surname } from './profile';

有时候,导出的名称会与你需要使用的其它名称产生冲突,ES6为你提供了重命名的方法解决这个问题,当你在导入名称时可以这样做:

1
2
3
4
5
6
7
8
9
10

// suburbia.js

// 这两个模块都会导出以`flip`命名的东西。

// 要同时导入两者,我们至少要将其中一个的名称改掉。

import {flip as flipOmelet} from "eggs.js";

import {flip as flipHouse} from "real-estate.js";

同样,当你在导出的时候也可以重命名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function v1() { ... }

function v2() { ... }



export {

v1 as streamV1,

v2 as streamV2,

v2 as streamLatestVersion

};

module命令

module命令可以取代import语句,达到整体输入模块的作用。module命令后面跟一个变量,表示输入的模块定义在该变量上。


模块的继承

字符串的扩展

ES6又提供了三种新方法:

  • includes():返回布尔值,表示是否找到了参数字符串。

  • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。

  • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。

这三个方法都支持第二个参数,表示开始搜索的位置。

1
2
3
4
5
6
7
8

var s = 'Hello world!';

s.startsWith('world', 6) // true

s.endsWith('Hello', 5) // true

s.includes('Hello', 6) // false


模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

模板字符串中嵌入变量,需要将变量名写在${}之中。

大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。

这其实和jade#{} 变量调用是差不多一个意思

1
2
3
function fn() { return "Hello World"; } 

console.log(`foo ${fn()} bar`);//foo Hello World bar

与普通字符串不同的是,模板字符串可以多行书写。

模板字符串中所有的空格、新行、缩进,都会原样输出在生成的字符串中。

Array.of()

Array.of方法用于将一组值,转换为数组。

1
2
3
console.log(Array.of(3, 11, 8)); // [3,11,8]    
console.log(Array.of(3));// [3]
console.log(Array.of(3).length);//1

class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

class Animal{

constructor(name,color){

this.name = name;

this.color = color;

}

//实例方法

toString(){

console.log('name:'+ this.name + ',color:' + this.color);

}

}

var animal = new Animal('dog','white');

console.log(animal.hasOwnProperty('name'));//true

console.log(animal.hasOwnProperty('toString'));//false

constructor方法,这就是构造方法,构造函数的名字就是class的名字,而this关键字则代表实例对象。也就是说,ES5的构造函数Animal,对应ES6Animal类的构造方法。

ES6的类,完全可以看做构造函数的另一种写法。

class 类名{

    constructor(形参1,形参2);

}

类本身就指向构造函数,类的所有实例共享一个原型对象。

1
2
3
4
5
6
7
8
9
10

class Perople{

constructor(){}

work()

speak()

}

等同于

1
2
3
4
5
6
7
8
9
10

People.prototype = {

constructor(){},

work(){}

speak(){}

}

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

class中的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

class Cat extends Animal{

constructor(action){



//直接调用父类构造器进行初始化
super('cat','white');

this.action = action;

}

toString(){

console.log(super.toString());

}

}

var cat = new Cat('catch');

cat.toString();//name:cat,color:white

console.log(cat instanceof Cat);//true

console.log(cat instanceof Animal);//true


  • 子类的__proto__属性,表示构造函数的继承,总是指向父类

  • 在子类中,super关键字代表父类实例。

1
2
3
4
5
class Cat extends Animal {}

console.log(Cat.__proto__ === Animal); // true

console.log(Cat.prototype.__proto__ === Animal.prototype); // true

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

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

__proto__的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

var human = {

breath(){

console.log('breathing...');

}

}



var worker = {

__proto__:human, //设置此对象的原型为human,相当于继承human

work(){

console.log('working...');

}

}

human.breath();//breathing...

worker.breath();//breathing...

worker.work();//working...

数组的扩展

Array.from()

1
2
3
4
5
6
7
8
9
10
11
12

//将类数组对象转换成数组对象

//Array.from() 方法可以将一个类数组对象或可迭代对象转换成真实的数组。

(function () {

var args = Array.from(arguments);

console.log(args);//[1,2,3]

})(1,2,3);

Array.find()

数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined

1
2

console.log([1, 4, -5, 10].find((n) => n < 0));//-5

ES6提供三个新的方法——entries()keys()values()——用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。


set和map数据结构

ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。


1
2
3
4
5
6
7
8

- add(value):添加某个值,返回Set结构本身。

- delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

- has(value):返回一个布尔值,表示该值是否为Set的成员。

- clear():清除所有成员,没有返回值。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var s = new Set();


//size指的是加入的次数

s.add(1,2,'t').add(3).add(4);

console.log(s.size);//3

console.log(s.has(1));//true



s.delete(2);

console.log(s.has(2));//false

map数据结构

Map数据结构是一种更完善的Hash结构实现。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

PS:Map本意是映射,即键值对对应


set(key, value)

set方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。


get(key)

get方法读取key对应的键值,如果找不到key,返回undefined


has(key)

has方法返回一个布尔值,表示某个键是否在Map数据结构中。


clear()

clear方法清除所有成员,没有返回值。


Map原生提供三个遍历器生成函数和一个遍历方法。

1
2
3
4
5
6
7
8

- keys():返回键名的遍历器。

- values():返回键值的遍历器。

- entries():返回所有成员的遍历器。

- forEach():遍历Map的所有成员。

解构赋值

解构赋值允许你使用类似数组或对象字面量的语法将数组和对象的属性值赋给一系列变量。

对象的结构

对象的结构可以把它的每个属性与不同的变量绑定


Promise对象

Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算.。

1
2
3
4

new Promise(executor)

new Promise(function(resolve,reject) { ... });

reject函数的参数通常是Error对象的实例,表示抛出的错误;

executor:带有resolve、reject两个参数的函数对象。第一个参数用在处理执行成功的场景

Promise对象有以下几种状态:

  • pending: 初始状态, 非 fulfilledrejected.

  • Resolved: 已成功(又称fulfilled).

  • rejected: 已失败
    Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected

方法

Promise.reject(reason):调用Promiserejectd句柄,并返回这个Promise对象


Promise.resolve(value):用成功值value解决一个Promise对象


Promise.prototype.then(onFulfiller,onRejected):添加肯定和否定回调到当前Promise,返回一个新的Promise

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

当你发起一个异步请求,并绑定了.when(), .done()等事件处理程序时,其实就是在应用promise模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29


var promise = new Promise(function (resolve,reject) {

if(true){

// `resolve`函数的作用是,将`Promise`对象的状态从“未完成”变为“成功”(即从`Pending`变为`Resolved`)在异步操作成功时调用

resolve('stuff worked');

}else{

reject(Error("It brokes"));

}

});

//绑定事件处理程序

promise.then(function (result) {

console.log(result);//Promise对象状态变为Resolved时调用

}, function (err) {//Promise对象的状态变为Reject状态时调用(可选)

console.log(err);

});

Promise实例生成以后,可以用then方法分别指定Resolved状态和Rejected状态的回调函数。

推荐的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14

promise

.then(function(data) { //cb

// success

})

.catch(function(err) {

// error

});

Promise.all()

Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例:

1
2

var p = Promise.all([p1,p2,p3]);//p1,p2,p3都是Promise实例

Promise.resolve() 将现有对象转为Promise对象

1
2

var p = Promise.resolve() 相当于 var p = new Promise();

Promise.prototype.done

为了保证抛出任何可能出现的错误,我们提供done方法

属性访问器

属性访问器,通过使用getset关键字来声明属性(Attribute),在ES5中需要借助Object.defineProperty来声明属性访问器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


(function () {

'use strict';


class Person{

constructor(name){

this.name = name;

}

get Name(){

return this.name;

}

set Name(value){

this.name = value;

}

}

let person = new Person('trigkit4');

console.log('personName: ',person.name);

})();

静态变量和函数static

当定义一个函数后通过点号 “.”为其添加的属性和函数,通过对象本身仍然可以访问得到,但是其实例却访问不到,这样的变量和函数分别被称为静态变量静态函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

<script type="text/javascript">

function Obj(){};



Obj.num = 72;//静态变量

Obj.fn = function() //静态函数

{




}



alert(Obj.num);//72

alert(typeof Obj.fn)//function



var t = new Obj();

alert(t.name);//undefined

alert(typeof t.fn);//undefined

</script>

静态成员,ES5或者之前的代码通过在构造函数中直接定义属性来模拟静态成员;ES6则只需要在方法名前面加上static关键字

如果在一个方法前,加上static关键字,就表示该方法不会被实例继承(无法通过实例对象调用该方法),而是直接通过类来调用,这就称为“静态方法”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85


/**

* Created by trigkit4 on 15/12/16.

*/




//ES5

function PersonType(name) {

this.name = name;

}



// static method

PersonType.create = function(name) {

return new PersonType(name);

};



// instance method

PersonType.prototype.sayName = function() {

console.log(this.name);

};



var person = PersonType.create("Nicholas");



//ES6

//Static Members

(function(){

'use strict';


class PersonClass {

constructor(name) {

this.name = name;

}



sayName() {

console.log(this.name);

}



static create(name) {

return new PersonClass(name);

}

}



let person = PersonClass.create("Nicholas");

console.log(person);

})()

ES6还可以为函数参数指定默认值,参数变量是默认声明的,所以不能用let或const再次声明:

1
2
3
4
5
6

function test(a=1,b=2){

console.log(a+b);

}