javascript遍历对象 js在for循环中使用java代码

背景大力教育的在线教室中台提供封装了核心能力的教室 SDK,业务方基于教室 SDK 开发面向用户的在线教室 App 。最近对教室 SDK 做一次比较大的改动时,我遇到了一个懵逼的问题 。这个问题耗费了我 3 天左右时间,让我压力一度大到全身发热 。当时虽然解决了问题,但并没有很理解原因 。直到一个多月后,才有时间做一些更深入的分析,并写下这篇文章 。
当时的情况是,业务方 App 工程能通过 TypeScript 编译,但在运行时会报错 。就不同的使用教室 SDK 的方式,报错有两种 。图 1 为在业务方 App 工程里正常安装教室 SDK 后进行调试时的报错;图 2 为在业务方 App 工程里 yarn link 教室 SDK 后进行调试时的报错 。

javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


javascript遍历对象 js在for循环中使用java代码


使用不当的问题由于子模块先于父模块被执行,子模块直接执行从父模块导入的变量会导致 JS 错误 。
// 文件 parent.jsimport {} from './child.js';export const parent = 'parent';// 文件 child.jsimport { parent } from './parent.js';console.log(parent); // 报错代码 2
如代码 2 所示,child.js 中的导入变量 parent 被绑定为 parent.js 的导出变量 parent,当执行 child.js 的最后一行代码时,parent.js 还没有被执行,parent.js 的导出变量 parent 未被初始化,所以 child.js 中的导入变量 parent 也就没有被初始化,会导致 JS 错误 。注意,本文说的变量是统称,包含 var、let、const、function 等关键字声明的变量 。
console.log(parent)^ReferenceError: Cannot access 'parent' before initialization如果是异步执行,则没问题,因为异步执行的时候父模块已经被执行了 。例如,代码 3 是能正常运行的 。
// parent.jsimport {} from './child.js';export const parent = 'parent';// child.jsimport { parent } from './parent.js';setTimeout(() => {console.log(parent) // 输出 'parent'}, 0);代码 3
纠正教程观点《ECMAScript 6 入门教程》一书说的三个重大差异如下:
  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用 。
  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口 。
  3. CommonJS 模块的 require() 是同步加载模块,ES6 模块的 import 命令是异步加载,有一个独立的模块依赖的解析阶段 。
对于第 1 点,CommonJS 和 ES6 模块输出的都是变量,变量都是值的引用 。该章节的评论中也有人质疑这个点 。对于第 2 点,前半句基本正确,后半句基本错误 。CommonJS 模块在执行阶段加载子模块文件,ES6 模块在预处理阶段加载子模块文件,当然 ES6 模块在执行阶段也会加载子模块文件,不过会使用预处理阶段的缓存 。从形式上看,CommonJS 模块整体导出一个包含若干个变量的对象,ES6 模块分开导出单个变量,如果只看父模块,ES6 模块的父模块确实在预处理阶段就绑定了子模块的导出变量,但是预处理阶段的子模块的导出变量是还没有被赋最终值的,所以并不能算真正输出 。对于第 3 点,CommonJS 模块同步加载并执行模块文件,ES6 模块提前加载并执行模块文件 。异步通常被理解为延后一个时间节点执行,所以说成异步加载是错误的 。
分析问题对 JS 模块机制有了更深刻的理解后,我们回来分析我遇到的问题 。
问题一首先分析图 1 的报错 。业务方 App 的工程代码用 webpack 打包,所以实际运行的是 CommonJS 模块 。上面讲过 CommonJS 模块循环引用使用不当一般不会导致 JS 错误,为啥这里会出现 JS 报错呢?这是因为,循环引用使用不当导致变量的值为 undefined,我们的代码使用了 extends[21],而 extends 不支持 undefined 。由于使用了 Bable[22] 进行转码,所以由垫片 _inherits[23] 报错 。另外一个典型的不支持的 undefined 的 case 是 Object.create(undefined) 。

推荐阅读