类
与大多数面向对象语言不同,js创建之初不支持类,在ES1-ES5很多库创建了一些工具来让js显得好像支持类 ,直到ES6正式引入类。
ES6类的声明是以class关键字开始后面紧跟类名,类声明中有constructor方法可以直接定义一个构造器。
例
class personClass{
constructor(name) {
this.name = name
}
}
类和之前的函数构造器有着很多的区别
1、类声明不会被提升,其行为和let类似,因此在程序执行到达声明处前,类会存在于暂时性死区内。
2、类声明中的所有代码会自动运行在严格模式下,并且无法退出严格模式。
3、类中的所有方法都是不可枚举的,函数构造器中必须使用object.defineProperty()才能将方法变为不可枚举。
4、类的所有方法没有[[constructor]],因此不能使用new来调用他们。
5、调用类构造器时必须使用new。
6、试图在类方法内部重写类名会报错。
类中的生成器方法
类中只需要在方法名称前附加一个"*"号就能定义一个生成器
class MyClass{
*createIterator() {
yield 1;
yield 2;
yield 3;
}
}
let instance = new MyClass();
let iterator = instance.createIterator();
静态成员
//在ES5及更早版本中,是直接在构造器上添加额外方法来模拟静态成员
function PersonType(name) {
this.name = name
}
PersonType.create = function(name) {
return new PersonType(name)
}
//像PersonType.create()会被认定为一个静态方法,它的数据不依赖PersonType任何实例
//ES6中静态方法的表示
class PersonType{
constructor(name) {
this.name = name
}
static create(name) {
return new PersonType
}
}
继承
在出现类之前,继承的实现很繁复,类中提供了extends关键字来指定当前类所需要继承的函数,还能调用super()方法访问基类构造器。
// ES5以及之前构造器间的继承实现
function PersonClass(name){
this.length = length;
}
PersonClass.prototype.getName = function () {
return this.name
}
function Person(name){
PersonClass.call(this,name)
}
Person.prototype = Object.create(PersonClass.prototype, {
constructor {
value: Person,
enumerable: true,
writable: true,
configurable: true
}
})
// ES6中继承的实现方法
class PersonClass {
constructor(name) {
this.name = name
}
getName(){
return this.name
}
}
class Person extends PersonClass{
constructor(name) {
// 与上例 Person.class(this, name)相同
super(name);
}
}
var digua = new Person('digua');
console.log(digua.getName) // digua
迭代器与生成器
迭代器
ES6中加入了迭代器,它是对高效处理集合数据一种补充,所有的迭代器都含有next()方法,它返回两个值,第一个值时集合中的下一项(value),第二项是是否还有更多的值供使用(done)。迭代器中可以选择实现一个throw()方法来抛出错误,ES6中新增了for-of来和迭代器协同工作,扩展运算符(…)也使用了它,同时它对异步操作也有一定意义。
生成器
生成器是能返回一个迭代器的函数,它的表达方法是function后加一个(*),同时使用yield来表明每一步结果。
例子
function* create(){
yield 1;
yield 2;
yield 4;
}
let obj = create()
console.log(obj.next()) // {value: 1, done: false}
console.log(obj.next()) // {value: 2, done: false}
console.log(obj.next()) // {value: 4, done: false}
console.log(obj.next()) // {value: undefined, done: true}
// next 中可以传值,当第一个参数被传递给next方法时,它会成为生成器内部yield语句的值,注意第一次调用时任意参数会被忽略。
function* create2(){
let first = yield 1;
let second = yield first + 2;
yield sencond + 4;
yield 5;
}
let sum = create();
console.log(sum.next()) // {value: 1, done: false}
console.log(sum.next(4)) // {value: 6, dond: false}
console.log(sum.throw(new Error("err"))); // 从生成器中抛出了错,后续done值为true
内置的迭代器
ES6中包含数组、Map、Set三种集合对象类型,这三种类型都拥有三种迭代器。
1、entries():返回一个包含键值对的迭代器
2、values():返回一个包含集合中的值的迭代器
3、keys():返回一个包含集合中键的迭代器
上面提到了for-of和迭代器是协同工作的,在使用for-of时values()方法是数组和Set的默认迭代器,entries()方法是Map的默认迭代器
例子:
let colors = ['red', 'green', 'blue'];
let track = new Set(['123', '224', '412'])
let data = new Map()
data.set("name", "digua")
data.set("age", 12)
for (let value of colors.entries()) {
console.log(value)
}
//[ 0, "red"] [1, "green"] [2, "blue"]
// 默认情况下为values()
for (let value of colors) {
console.log(value)
}
// "red" "green" "blue"
for (let value of track.entries()) {
console.log(value)
}
// ["123", "123"] ["234", "234"] ["412", "412"]
// 默认情况下为values()
for (let value of track) {
console.log(value)
}
// "123" "234" "412"
for (let value of data.entries()) {
console.log(value)
}
//["name": "digua"] ["age", 12]
// 默认情况下为entries()
for (let value of data) {
console.log(value)
}
//["name": "digua"] ["age", 12]
扩展运算符与可迭代对象
之前提到扩展运算符中运用了迭代器,它会使用默认迭代器来判断需要使用哪些值,将所有值从迭代器取出插入数组。
例子
// set
let set = new Set([1, 2, 2, 3, 4])
let arr = [...set]
console.log(arr) // [1, 2, 3, 4]
// map
let map = new Map(["name", "digua"],["age", 20])
let arr1 = [...map]
console.log(arr1) // [["name", "digua"], [["age", 20]]
异步任务
迭代器对异步任务的意义在于,我们可以通过一个看似同步的代码来实现异步操作。下面写一段伪代码来说明迭代器实现的一个异步操作
function* getname() {
let res = yield read('name');
dosomething(res)
}
例子中dosomething的操作是在等read操作执行之后,那么这时就实现一个异步操作。