class extends function
# class extends function
注意:本文示例代码是 typescript
# klg-logger 的封装
最近在做一个共用的 logger 包 klg-logger (opens new window) ,在调研了一圈后,决定基于 tracer (opens new window) 这个包来做封装,tracer 足够简单,也具有拓展性。因为公司使用的工具包都是使用 class 风格的, 所以第一版的 klg-logger 长这样:
import {console, Tracer} from 'tracer'
export class Logger {
private logger: any
constructor (config?: Tracer.LoggerConfig) {
this.logger = console(config)
}
log (msg: any, ...params): void {
this.logger.log.apply(this, arguments)
}
error (msg: any, ...params): void {
this.logger.error.apply(this, arguments)
}
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用方式:
const logger = new Logger({})
logger.log('hello world')
2
就是使用一个 Logger class 把 tracer 封装了一次。
# this 域问题
过了一段时间,klg-logger 出现了一个错误:
TypeError: Cannot read property 'logger' of undefined
经排查,是 this 作用域的问题,调用 logger 的代码长这样:
this.connect().then(function () {
// code
}).catch(logger.error)
2
3
logger.error 这个 function 是作为 参数直接传递个 catch, 那么 logger.error 内部的 this 将会指向 catch 对象,而不再是 logger 对象。
我们可以改变写法来避免这个错误:
this.connect().then(function () {
// empty
}).catch(function (err) {
logger.error(err.message)
})
2
3
4
5
但如果要更彻底地解决这个问题,还是要改变一下 logger.error 的实现才行。
# class extends function
考虑到这个包 klg-logger 已经在多个系统中使用,为了保持兼容,我们要继续保持 class 风格,所以我们要把上文提到的“封装”的实现改为继承实现。
import {console} from 'tracer'
export class Logger extends console {
// Error:(3, 29) TS2507: Type '(config?: LoggerConfig) => Logger' is not a constructor function type.
}
2
3
4
5
直接继承 tracer 的 console 是会报错的,因为 console function 没有 construction。
在 stackoverflow 上可以找到解决方案:https://stackoverflow.com/questions/36871299/how-to-extend-function-with-es6-classes (opens new window)
通过一个 Function 把 console 包装成一个具有 construction 的对象,然后在 construction 里面 return console 实例即可
class Logger extends Function implements Tracer.Logger {
constructor (config?: LoggerConfig) {
super()
Object.setPrototypeOf(console, Logger.prototype)
const instance = console(config as any) as Logger
instance.err = instance.error
return instance
}
// 下列方法都会被覆盖,只做声明用, 无需实现
debug (...args: any[]): Tracer.LogOutput {
return undefined
}
error (...args: any[]): Tracer.LogOutput {
return undefined
}
/// ......
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
经测试,最后的写法和功能都是原版保持了一致。