开发日历控件的时候,对方变更了一下需求,基本上将最终产品分成两个:
- 选择连续时间段
- 选择多个不连续时间
那么我们知道,对于这种大部分功能一致,只有若干函数逻辑不同的产品,最合适的就是状态模式。于是很自然的,我就拿“2”作为标准模式,“1”作为新模式,将其重构成父类和子类,大概关系如下:
// 父类
// DatePicker.js
import RangeDatePicker from './RangeDatePicker';
class DatePicker {
....
static getInstance(el, options) {
if (options.scattered) {
return new DatePicker(el, options);
} else {
return new RangeDatePicker(el, options);
}
}
// 子类
// RangeDatePicker.js
import DatePicker from './DatePicker';
class RangeDatePicker extends DatePicker {
}
因为这个类只有两个成员,所以我把工厂方法 .getInstance()
放到了父类里面,通过判断参数确定应该返回哪一类实例。代码写完,测试的时候却报错:
Super expression must either be null or a function, not undefined
这个意思很明显,被继承的父类不能未定义。然则 DatePicker
明明是定义了的,只是验证两个类文件的话,均未出现任何语法错误。
遇事不决先 Google,还真找到很多结果,不过大多数都和 React.Component 有关,翻了半天一无所获,只好自力更生。打开 Chrome 开发者工具,勾上“Pause on Exceptions”,观察发生异常时的状况,一遍又一遍,我渐渐意识到,发生这个错误的时候,DatePicker
还未能在 webpack 的环境中完成注册。问题找到了!
与其它编译类语言不同,JS 是动态语言,所有 JS 代码都是放到统一的环境里跑的,类的代码如此,import
也是如此。所以对于其他语言,比如 ActionScript、Java,循环引用,即 A 引用 B,B 也引用 A,是没问题的,因为类的代码都会编译到执行文件,执行的时候,都已经在环境中;而 JS 是边执行边置入环境,具体到我这里,在将父类 DatePicker
放入环境时,会先 import
子类 RangeDatePicker
的代码,而子类又会要求 import
父类的代码,父类的代码正在引入中,于是便产生了问题。
想明白这点,后面就好办了,直接创建一个工厂类,把工厂方法放到里面执行,问题便解决了:
import DatePicker from './DatePicker';
import RangeDatePicker from './RangeDatePicker';
export default {
createDatePicker(el, options) {
if (options.scattered) {
return new DatePicker(el, options);
} else {
return new RangeDatePicker(el, options);
}
}
}
PS:当年写依赖注入和包管理工具的时候,就卡在这个地方,怎么都想不通,于是一直也没写完。没想到这些个浓眉大眼有头发的,也都这么不负责任,这种问题都不解决就搞出来让全世界人用了。
发表回复