ES6笔记(完整版2)

129 阅读16分钟

三、数组的扩展 

1、Array.from

用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iteratble)对象
包含ES6新增的数据结构Set和Map

//ES6的写法
let arr2 = Array.from(arrayLike); //['a','b','c']
只要是部署了iterator接口的数据结构,Array.from都能将其转为数组

Array.from('hello'); //将字符串转为数组['h','e','l','l','o']
let nameSet = new Set(['a','b','c'])
Array.from(nameSet); //['a','b']

2、Array.of()

用于将一组值转换为数组,这个的主要目的是弥补数组构造函数Array()的不足
Array.of(3,18,8); //[3,18,8]

3、数组实例的find()和findIndex()

find()方法用于找出第一个符合条件的数组成员
它的参数是一个回调函数,所有数组成员依次执行该回调函数,知道找出第一个返回值为true的成员,然后返回该成员
如果没有符合条件的成员,则返回undefined
find()方法的回调函数可以接受三个参数,依次为当前的值,当前的位置和原数组

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

findIndex()方法与find()方法非常类似,更换第一个符合条件的数组成员的位置
如果所有成员都不符合条件,则返回-1

[1,5,10,15].findIndex(function(value,index,arr){
    return value > 9;
}); //2

4、数组实例的fill()

fill()方法使用给定的值填充一个数组
['a','b','c'].fill(7); //[7,7,7]
new Array(3).fill(7); //[7,7,7]

5、数组实例的entries(),keys()

这两个方法用于遍历数组,它们都返回一个遍历器对象,可以用for...of循环进行遍历
唯一的区别是:keys()是对键名的遍历,entries()是对键值对的遍历

for(let index of ['a','b'].keys()){
    console.log(index)
}
for(let [index,elem] of ['a','b'].entries()){
    console.log(index,elem)
}

6、数组实例的includes()

该方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES6引入了该方法

[1,2,3].includes(2); //true
[1,2,3].includes(4); //false
[1,2,NaN].includes(NaN); //true

第四章 Set和Map数据结构及Promise

一、Set

1、set实例的创建

它类似于数组,但是成员的值都是唯一的,没有重复的值
set本身是一个构造函数,用来生成set数据结构

const s = new Set();
[2,3,4,5,3,2,2].forEach(x => s.add(x));
console.log(s); //2,3,4,5

Set函数可以接收一个数组(具有iterator接口的其他数据结构)作为参数,用来初始化
[...new Set(array)] //去除数组的重复成员

2、set实例的属性和方法

Set结构的实例有以下属性:
    1)Set.prototype.constructor:构造函数,默认就是Set函数
    2)Set.prototype.size:返回Set实例的成员总数

Set结构实例有以下方法:
    1add(value):添加某个值,返回Set结果本身
    2delete(value):删除某个值,返回一个布尔值,表示删除是否成功
    3has(value):返回一个布尔值,表示该值是否为Set成员
    4clear():清楚所有成员,没有返回值
    5keys():返回键名的遍历器
    6values():返回键值的遍历器
    7entries():返回键值对的遍历器
    8forEach():使用回调函数遍历每个成员

二、Map

1、Map实例的属性和方法

Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各类型的值(包括对象)都可以当做键
也就是说:Object结构提供了“字符串--值”的对应,Map结构提供了“值--值”的对应,是一种更完善的Hash结构的实现
如果你需要“键值对”的数据结构,MapObject更合适

Map可以接受一个数组作为参数,该数组的成员是一个个表示键值对的数组

const map = new Map([[name,'张三'],['title','Author']]);

Map结构的实例有以下属性:
    Set.prototype.size:返回Map实例的成员总数

Map结构的实例有以下方法:
    1set(key,value):set方法设置键名可以对应的键值为value,然后返回整个Map结构
        如果key已经有值,则键值会被更新,否则就新生成该键
    2get(key):get方法读取对应的键值,如果找不到key,返回false
    3)has(key):has方法返回一个布尔值,表示某个键是否在当前Map对象之中
    4)delete(key):delete方法删除某个键,返回true。如果删除失败,返回false
    5)clear():清除所有成员,没有返回值
    6)keys():返回键名的遍历器
    7)values():返回键值的遍历器
    8)entries():返回键值对的遍历器
    9)forEach():使用回调函数遍历每个成员

三、Iterator

1、Iterator(遍历器)的概念

遍历器(iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制
任何数据结构只要部署iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)

iterator的作用有三个
    1)为各种数据结构提供一个统一的、简便的访问接口
    2)使得数据结构的成员能够按某种次序排列
    3)ES6创造了一种新的遍历命令for...of循环,iterator接口主要提供for...of消费

iterator的遍历过程是这样的:
    1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上是一个指针对象
    2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员
    3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员
    4)不断调用指针对象的next方法,直到它指向数据结构的结束位置

2、默认iterator接口

iterator接口的目的:是为所有的数据结构提供了一种统一的访问机制,即for...of循环
当使用for...of循环遍历某舟数据结构的时候,该循环会自动去寻找iterator接口
一种数据结构只要部署了iterator接口,我们就称这种数据结构事“可遍历的”

可以通过以下方法访问iterator对象:
    var iterator = iterObj[Symbol,iterator]();

原生具备iterator接口的数据结构如下:
    Array
    Map
    Set
    String
    TypeArray
    函数的arguments对象
    NodeList对象

四、Promise

1、promise介绍

promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理强大
它由社区最早提出和实现,ES6将其写入了语言标准,统一了用法,原生提供了promise对象

所谓promise,简单说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作)的结果
从语法上说,promise是一个对象,它可以获取异步操作的消息
promise提供统一的API,各种异步操作都可以用同样的方法进行处理

有了promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
此外,promise对象提供了统一的接口,使得控制异步操作更加容易

2、基本用法

promise构造函数接收一个函数作为参数,该函数的两个参数分别是resolve和reject
它们是两个函数,有JavaScript引擎提供,不用自己部署

resolve函数的作用是:将promise对象的状态从“未完成”变为“成功”(即从pending变成resolved)
在异步操作成功时调用,并将异步操作的结果作为参数传递出去

reject函数的作用是:将promise对象的状态从“未完成”变为“失败”(即从pending变成rejected)
在异步操作失败时调用,并将异步操作的结果作为参数传递出去

如果调用resolve和reject函数时带有参数,那么它们的参数会被传递给回调函数

promise实例生成以后,可以调用then方法分别指定resolved状态和rejected状态的回调函数

.then(function(){
        //success
    },
    function(){
        //error
    }
)

3、promise.prototype.then()

then方法是定义在原型对象promise.prototype上的
它的作用是为promise实例添加状态改变时的回调函数

then方法的第一个参数时resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
then方法返回的是一个新的promise实例(注意:不是原来的promise实例)

因此可以采用链式方法,即在then方法后面再调用另一个then方法
第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数

4、promise.prototype.catch()

Promise.prototype.catch方法是.then(null,rejection)的别名,用于指定发生错误时的回调函数
一般来说,不要在then方法里面定义reject状态的回调函数(即then的第二个参数),总是使用catch方法

promise对象的错误具有“冒泡”性质,会一直向后传递,知道被捕获为止
也就是说,错误总会被下一个catch语句捕获

5、promise.all()

Promise.all方法用于将读个Promise实例包装秤一个新的promise实例

var p = Promise.all([p1,p2,p3])

上面代码中,Promise.all方法接收一个数组作为参数,p1,p2,p3都是promise实例
p的状态由p1,p2,p3决定,分为2种情况:
1)只有p1,p2,p3的状态都变为fulfilled,p的状态才会变成fulfilled,
    此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数
2)只要p1,p2,p3之中有一个被rejected,p的状态就会变成rejected
    此时第一个被rejected的实例的返回值,会传递给p的回调函数

6、promise.race()

promise.race方法同样是将多个promise实例包装成一个新的promise
下面代码中,只要p1,p2,p3之中有一个实例率先改变状态,p的状态就跟着改变
那个率先改变的promise实例的返回值,就传递给p的回调函数

var p = Promise.race([p1,p2,p3])

7、promise.resolve()

promise.resolve方法将现有对象转为Promise对象,例如:
var jsPromise = promise.resolve($.ajax('whatever.json'))

1)参数时一个promise实例
    promise.resolve将不做任何修改、原封不动的返回这个实例
2)参数时一个thenable对象
    thenable对象是指具有then方法的对象,promise.resolve方法会将这个对象转为promise对象
    然后立即执行thenable对象的then方法
3)参数不是具有then方法的对象,或根本就不是对象
    如果参数是一个原始值,或者是一个不具有then方法的对象
    则promise.resolve方法返回一个新的promise对象,状态为resolved
4)不带有任何参数
    直接返回一个resolve状态的promise对象
    需要注意的是:立即resolve的promise对象,是在本轮“事件循环”(event loop)结束时
    而不是在下一轮的“事件循环”的开始时

8、promise.reject()

promise.reject方法也会返回一个新的promise实例,该实例状态为rejected

var p  = promise.reject("出错了")
=>
var p  = new Promise((resolve,reject)=>reject('出错了'))

9、finally()

finally方法用于指定不敢promise对象最后无论如何都会执行的操作
它接收一个普通的回调函数作为参数,该函数不管怎么样都必须执行

下面是一个例子,服务器使用promise处理请求,然后使用finally方法关掉服务器
server.listen(0).then(function(){
    //run test
}).finally(server.stop)

第五章 ES6模块

一、介绍

历史上,JavaScript一直没有模块(modules)体系,无法将一个大程序拆分成相互依赖的小文件,再用简单的方法拼接装起来

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

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

二、export命令

模块功能主要由两个命令构成:exportimport
export命令用于规定模块的对外接口
import命令用于输入其他模块提供的接口
一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取
如果你希望外部能够读取模块内部的某个变量,就必须适应使用export关键字输出该变量

下面是一个js文件,里面使用export命令输出变量
var firstName = 'Jennie';
var lastName = 'Kim';
var year = 1996'
function multiply(x,y){return x*y;};
export{firstName,lastName,year,multiply};

需要特别注意的是:export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,不能直接导出一个值

export var m = 1;
或 var m = 1;export{m};
或 var n = 1;export{n as m};
在一个模块中,export可以调用多次

三、import命令

使用export命令定义了命令的对外接口以后,其他js文件就可以通过import命令加载这个模块
1)解构导入
    import {firstName,lastName,year} from './profile';

2)重命名变量
    import {lastName as surname} from './profile';

3)重复导入
    import {name} from './module1';
    inport {age} from './module1';
    如果多次重复执行同一import语句,那么只会执行一次模块代码

4)模块的整体加载
    import * as person from './module1'

四、export default命令

使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载
但是,用户肯定希望快速上手,未必愿意阅读文档去了解模块有哪些属性和方法
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令为模块指定默认输出

export default function(){
    console.log('foo')
}

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

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

export default命令用于指定模块的默认输出
显然,一个模块只能有一个默认输出,因此export default命令只能用一次
所以,import命令后面才不用加大括号,因为只可能对应一个方法或对象

五、export与import复合写法

如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起

export {foo,bar} from './my_module';
=>
import {foo,bar} from 'my_module';
export {foo,bar}

第六章 Class

一、简介

JavaScript语言中,生成实例对象的传统方法是通过构造函数,ES6提供了更接近传统语言的写法,引入了class(类)概念,作为对象的模板。通过class关键字,就可以定义类。

基本上,ES6的class可以看做只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰更像面向对象编程的语法而已。所以ES6的类,完全可以看作构造函数的另一种写法。

class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    toString(){
        return '('+this.x+','+this.y+')';
    }
}

二、方法

在类中可以直接定义方法,实际上类的所有方法都定义在类的prototype属性上面
在类的实例上面调用方法,其实就是调用原型上的方法

class Ponit{
    constructor(){//...}
    toString(){//...}
    toValue(){//...}
}

由于类的方法都定义在prototype对象上面,所以类的新方法都可以添加在prototype对象上面。
Object.assign方法可以很方便地向类添加多个方法

class Point{
    constructor(){//...}
}
Object.assign(Point.prototype,{
    toString(){},
    toVlaue(){}
})

三、constructor方法

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

class Point{}
=>
class Point {constructor(){}}

类必须使用new调用,否则会报错
这是它跟普通构造函数的一个主要区别,后者不用new也可以执行

四、静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承
如果在一个方法钱,加上static关键字,就表示该方法不会被实例继承
而是直接通过类来直接调用,这就称为“静态方法”

class Foo{
    static classMethod(){return 'hello';}
}
Foo.classMethod(); //'hello'

如果静态方法包含this关键字,这个this值得是类,而不是实例

五、实例属性

类的实例属性可以定义在构造函数中
 class Person{
    constructor(id,name,age){
        this.id = id;
        this.name = name;
        this.age = age;
    }
}

六、静态属性

 直接在类上定义的属性是静态属性

class Foo{
    //...
}
Foo.prop = 1;
Foo.prop; //1

目前,只有这种写法可行,因为ES6明确规定,class内部只有静态方法,没有静态属性

七、继承

class可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承要清晰和方便很多

class Animal{
    constructor(name){
        this.name = name;
    }
    sayName(){
        console.log("my name is",this.name)
    }
}
class Dog extends Animal{
    //...
}

子类必须在constructor方法中调用super方法,否则新建实例时会报错
这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工
如果不调用super方法,子类就得不到this对象。子类构造函数可以省略
在子类构造函数中,只有调用super之后,才可以使用this关键字,否则会报错

八、super

super这个关键字,既可以当做函数使用,也可以当做对象使用
在这两种情况下,它的用法完全不同

1)函数
    子类B的构造函数中的super(),代表调用父类的构造函数
    super虽然代表了父类A的构造函数,但是返回的是子类B的实例
    即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)
2)对象
    在普通方法中,指向佛雷德原型对象
    在静态方法中,指向父类
    由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的
    ES6规定,通过super调用父类方法时,super会绑定子类的this

    super.print()
    =>
    super.print.call(this)
    不能直接打印super,因为无法得知super到底是函数还是对象

九、类的prototype属性和__proto__属性

class作为构造函数的语法糖,同时又prototype属性和__proto__属性,因此同时存在两条继承链
1)子类的__proto__属性,表示构造函数的继承,总是指向父类
2)子类的prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性

class A{}
class B extends A{}
B.prototype.__proto__ === A.prototype; //true
B.prototype.__proto__ === A.prototypr; //true

类的继承是按照下面的模式实现的
class A{}
class B{}
//B的实例继承A的实例
Object.setPrototypeOf(B.prototype,A.prototype);
//B的实例继承A的静态属性
Object.setPrototypeOf(B,A)
const b = new B();