Javascript的错误原因有时并不是直接导致错误的原因。因此收集一下这些开发中碰到的有些绕弯的错误。

  1. TypeError: Something is not a constructor

    TypeError: Something is not a constructor
    at Object.<anonymous> (file:///main.js:179:12)
    at Generator.next (<anonymous>)

    这个错误可能常见到MDN甚至有专门一个文档讲这个错误。然而这份文档中没有提到的是,如果你以import aclass from 'a.js'引用了另一个js文件,而这个文件没有默认导出,也会得到同样的错误。

    编写于2020年4月25日

  2. TypeError: Super expression must either be null or a function, not undefined

    在补引擎大坑的时候碰到了这个bug......特别巧的是,第二个错误也是一个TypeError。但是与第一个不同,它是在使用parcel.js时有可能被抛出的。

    堆栈跟踪:

    Uncaught TypeError: Super expression must either be null or a function
                    at _inherits (src.f69400ca.js:441)
                    at src.f69400ca.js:501
                    at Object.parcelRequire.Engine/Resource/BlobBasedHeavyResource.ts../ResourceControl
                    (src.f69400ca.js:593)
                    at newRequire (src.f69400ca.js:47)
                    at localRequire (src.f69400ca.js:53)
                    at Object.parcelRequire.Engine/Resource/Image.ts../BlobBasedHeavyResource (src.f69400ca.js:621)
                    at newRequire (src.f69400ca.js:47)
                    at localRequire (src.f69400ca.js:53)
                    at Object.parcelRequire.Engine/Resource/ResourceControl.ts.../Exception/ResIdNotFoundException
                    (src.f69400ca.js:1269)
                    at newRequire (src.f69400ca.js:47)
    堆栈跟踪最后帮助我们定位到了这段代码:
    function _inherits(subClass, superClass) {
        if (typeof superClass !== "function" && superClass !== null) {
            throw new TypeError("Super expression must either be null or a function");
        }
        subClass.prototype = Object.create(superClass && superClass.prototype,
            {
                constructor: {
                    value: subClass,
                    writable: true,
                    configurable: true
                }
            });
        if (superClass) _setPrototypeOf(subClass, superClass);
    }

    观察可知,错误是亲类(或者说,父类)没有在子类之前加载导致的。道理很简单,但是该怎么解决呢?parcel的repo上也有个老哥碰到了应该是一样的问题(issue),结果开发者好像根本没理他,issue直接给Github Action关掉了......

    那就只能自己解决问题了。

    先来分析一下原因是什么。因为parcel会把所有的Javascript打包到一个bundle里,每个类又会转换成var <className>=...的格式,所以变量声明的顺序导致子类可能在父类前加载。

    先是考虑了下代码分割这一个解决方案,毕竟目标环境Chrome还是支持esmodule的(一応.....),但是parcel的代码分割要求用import(),动态加载又会导致很多问题,然后出问题的类又不适合动态加载,那就算了吧。

    那么有没有可能改变变量声明顺序呢?当然,手动移动是可以解决的,但我们要的是自动化的解决方案,要不也没有用parcel的必要了是吧。然后看了下亲类(A字开头)是在R开头的ts里声明的,子类是在B开头的ts里声明的,如果更改文件名字的话或许可以吧。就这样抱着试一试的想法把某A字亲类独立成一个文件,毕竟A是字母表打头的,这还不能第一个加载的话就没辙了。

    然后就解决了,啊这...原来还想发个issue,想想算了。并且项目又有了新的bug,暂时还不知道是怎么回事就不讲了。

    parcel虽然集成度高所以用起来特别方便,但是细致的需求可能还是webpack舒服些。但是webpack的入门门槛属实劝退(相比于parcel的开箱即用)就像Wordpress的编辑器和VSCode写文章的区别一样。Wordpress虽然通过高集成帮你免去写一般博文要学HTML或者要写HTML的烦恼,但是一旦有更复杂的应用就还是得回归HTML。集成与可拓展性的矛盾是一直存在呢。

    编写于2020年5月1日,距离bcr n3还有三个小时