如下代码被 esbuild 编译时
1 | import { observable } from 'mobx' |
Case1: 当 target: esnext 时被编译为,此时 data.toJS() 会抛错 ❌,data.toJS is not a function
1 | import { observable } from 'mobx' |
Case2: 当 target: es6 时被编译为,代码正常运行 ✅
1 | import { observable } from 'mobx' |
这两种写法在 ChatGPT 看来是几乎等价的,Case1 是新语法,Case2 是旧语法,那么造成 Case1 代码运行异常的原因是什么 ?
两者的共同点是使用 __decorateClass 函数中的 Object.defineProperty 给原形链对象 MyStore.prototype 的 key 设置拦截函数
1 | var __defProp = Object.defineProperty; |
不同点是
- Case2: new MyStore() 实例化时,首先运行构造函数的 this.data = []; 赋值语句,这行代码非常具有迷惑性,初一看会认为是给实例化的对象 instance 设置实例属性,实际上由于原形链对象上有了 data 属性,这行代码会进入到上一步 Object.defineProperty 的拦截函数中,所以此时设置后的 data 属性仍然是 __decorateClass 中包裹的 Mobx 对象,具有 toJS 等属性
- Case1: new MyStore() 实例化时,data 会被直接设置为实例化的对象 instance 的实例属性,而不会像 Case1 一样进入到原形链对象 Object.defineProperty 的拦截函数中。至此 instance 的实例属性 data 是普通的数组没有 toJS 属性,instance 的原型链属性 data 是 Mobx 对象具有 toJS 等属性,而当 JS 中某个对象实例属性与原型链属性同名时,返回的是实例属性的值,故该代码抛错 ❌
结论: Case2 会触发原型链上的拦截函数 setter,Case1 不会触发