Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS 类实例属性写法的区别 #74

Open
xiaoxiaojx opened this issue Sep 29, 2024 · 0 comments
Open

JS 类实例属性写法的区别 #74

xiaoxiaojx opened this issue Sep 29, 2024 · 0 comments

Comments

@xiaoxiaojx
Copy link
Owner

xiaoxiaojx commented Sep 29, 2024

今天(2024-09-29)排查的一个编译问题,关键词: esbuild、decorators、esnext、mobx

如下代码被 esbuild 编译时

import { observable } from 'mobx'

class MyStore {
    @observable
    data = []
}

const instance = new MyStore()
instance.data.toJS();

Case1: 当 target: esnext 时被编译为,此时 data.toJS() 会抛错 ❌,data.toJS is not a function

import { observable } from 'mobx'

class MyStore {
+  data = [];
}
__decorateClass([
  observable
], MyStore.prototype, "data", 2);

const instance = new MyStore()
instance.data.toJS();

Case2: 当 target: es6 时被编译为,代码正常运行 ✅

import { observable } from 'mobx'

class MyStore {
+  constructor() {
+      this.data = [];
+  }
}
__decorateClass([
  observable
], MyStore.prototype, "data", 2);

const instance = new MyStore()
instance.data.toJS();

这两种写法在 ChatGPT 看来是几乎等价的,Case1 是新语法,Case2 是旧语法,那么造成 Case1 代码运行异常的原因是什么 ?

两者的共同点是使用 __decorateClass 函数中的 Object.defineProperty 给原形链对象 MyStore.prototype 的 key 设置拦截函数

var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result)
    __defProp(target, key, result);
  return result;
};

不同点是

  • 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 不会触发

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant