es6中的一些问题

Class

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到.

1
2
3
4
5
6
7
8
9
10
class TestClass {
constructor(x, y) {
this.x = x
this.y = y
}

toString() {
return `${this.x},${this.y}`
}
}

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

和构造函数的关系

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

1
2
typeof TestClass // "function"
TestClass === TestClass.prototype.constructor //true

使用的时候也是直接用 new 来新建实例

类中的方法

类中的方法都定义在类的原型上

1
2
3
4
5
TestClass.prototype = {
toString() {
return `${this.x},${this.y}`
}
} //等同于上面的代码

类的内部所有定义的方法,都是不可枚举的(non-enumerable)。

Class 表达式

1
2
3
4
5
const MyClass = class Me {
getClassName() {
return Me.name
}
}

上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是 MyClass 而不是 Me,Me 只在 Class 的内部代码可用,指代当前类。

私有方法/属性

1
2
3
4
5
6
7
8
9
class MyClass {
foo(baz) {
bar.call(this, baz)
}
}
function bar(baz) {
return (this.snaf = baz)
}
//上面代码中,foo是公有方法,内部调用了bar.call(this, baz)。这使得bar实际上成为了当前模块的私有方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const bar = Symbol('bar')
const snaf = Symbol('snaf')

export default class myClass {
// 公有方法
foo(baz) {
this[bar](baz)
}

// 私有方法
[bar](baz) {
return (this[snaf] = baz)
}

// ...
}

symbol 是 es6 中新增的数据类型,表示独一无二的值,通过 Symbol 函数创建
symbol 作为属性时,不能用点操作符

Class 中的 this

class 中的 this 默认指向类的实例,但是单独使用的时候会指向方法所在的环境,因而会报错

解决方法:

  1. 在 constructor 中绑定 this
1
2
3
4
5
6
7
class Logger {
constructor() {
this.printName = this.printName.bind(this)
}

// ...
}
  1. 使用箭头函数。
1
2
3
4
5
6
7
8
9
class Logger {
constructor() {
this.printName = (name = 'there') => {
this.print(`Hello ${name}`)
}
}

// ...
}

类的继承

class 继承使用 extend 关键字

1
2
3
class parent {}

class child extends parent {}

上面的代码通过 extend 关键字继承父类属性和方法
如果需要添加子类自身的方法和属性需要调用 constructor

1
2
3
4
5
6
class child extends parent {
constructor(x, y, color) {
super(x, y)
this.color = color
}
}

Object.getPrototypeOf 方法可以用来从子类上获取父类。

1
Object.getPrototypeOf(child) === parent

和 es5 继承的区别

ES5 的继承,也就是 prototype,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply(this))。

ES6 的继承,也就是 class,实质是先创造父类的实例对象 this(必须先调用 super),然后再用子类的构造函数修改 this

ansyc await

ansyc 是 Generator 语法糖

一个函数如果加上 async ,那么该函数就会返回一个 Promise,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。以此实现串行的异步操作

async 函数返回的 Promise 对象,必须等到内部所有 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者抛出错误。也就是说,只有 async 函数内部的异步操作执行完,才会执行 then 方法指定的回调函数。

1
2
3
4
5
6
async function getTitle(url) {
let response = await fetch(url)
let html = await response.text()
return html.match(/<title>([\s\S]+)<\/title>/i)[1]
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)

只要一个 await 语句后面的 Promise 变为 reject,那么整个 async 函数都会中断执行。

有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个 await 放在 try…catch 结构里面,这样不管这个异步操作是否成功,第二个 await 都会执行。

1
2
3
4
5
6
7
8
async function f() {
try {
await Promise.reject('出错了')
} catch (e) {}
return await Promise.resolve('hello world')
}

f().then(v => console.log(v))

await 并行

1
2
3
4
5
6
7
async function r(){
const [result1,result2] = promise.all([
readFile('1.text')
readFile('2.text')
])
console.log(result1,result2)
}

每隔 1s 输出 0,1,2,3,4

1
2
3
4
5
6
7
8
9
10
11
12
13
async function timeout(ms) {
await new Promise(reslove => {
setTimeout(reslove, ms)
})
}

async function asyncPrint(ms) {
for (let i = 0; i < 5; i++) {
await timeout(ms)
console.log(i)
}
}
asyncPrint(1000)