class原型的函数的enumerable到底从哪里设置的

试用新版本…一些自动化的功能失效了…查了半天…
发现相同的一段代码…在不同版本运行会有2个结果…

class TestBase {
    public numBase: number = 0;
    public nodeBase: Node = null;
    public on_TestBase() { }
}

class Test extends TestBase {
    public num: number = 0;
    public node: Node = null;
    public on_Test() { }
}

@ccclass('Main')
export class Main extends Component {
    public test: Test;
    protected start() {
        window['lv'] = this;
        this.test = new Test();
        for (const key in this.test)
            console.log('forin', key, this.test[key]);
        console.log('forin-end');
    }
}

image
image
结果是3.4.1的class原型上的函数是无法forin出来的…


再经过一翻折腾…发现是函数上的可枚举被关了…
3.3.2
image
3.4.1
image


项目设置…tsconfig…都对比了个遍…没找到哪里设置的…
到底是我改了什么设置…还是你们改了什么设置:face_with_monocle:

Creator 在 3.3(还是3.4 我忘了)默认将预览时代码编译为 ES6。根据规范,ES6 类方法是不可枚举的:

// 在任意 JS 运行时上直接运行这一段代码
class Foo { bar() {} };
console.log(Object.getOwnPropertyDescriptor(Foo.prototype, 'bar').enumerable); // false

如果要更改此行为,设置 [项目设置]-[脚本]-[用于预览的浏览器列表] 让其指向文件 preview.browserslistrc(或任意名称),其文件内容为:

supports es5 # 我的目标是那些支持 ES5 的浏览器

你甚至可以按浏览器来定制,参考: browserslist/browserslist: :hedgehog: Share target browsers between different front-end tools, like Autoprefixer, Stylelint and babel-preset-env (github.com)。如果你用过 babel 和 @babel/preset-env,那你应该很熟悉这个东西。

同时,你还需要启用 [项目设置]-[脚本]-[宽松模式]。

改完这些,然后重启编辑器。


后语

其实还是按规范来比较好,规范说它是不可枚举的那就换个法子获取。

1赞

感谢回答…
检测的代码我也写了一份和你一样的…打算在运行时再确定遍历方案…

方便请教几个问题吗…?
1.预览时代码编译为 ES6…那么打包的代码也是编译为 ES6 吗…?
supports es5 之后…是只更改预览的编译…还是会连同更改打包的编译呢…?

2.如何获取不可枚举的属性呢…?
我从 Chrome 的 console 中测试可以调用 Object.getOwnPropertyDescriptors 来获取对象包括非枚举的所有属性…
这又涉及了比较复杂的流程…可能需要手动获取对象的原型链并排重…
还有在VSCode中则提示这个API需要ES2017来支持…万一运行环境没有 Object.getOwnPropertyDescriptors 的话…还有其他方案吗…?

  1. 仅在预览时生效。目前在原生平台上,是编译到原生的 V8 支持的版本(很新);其它平台用的默认配置(ES5)

  2. 以下是示例代码:

function getOwnMethods(classConstructor) {
  const propertyDescriptors = Object.getOwnPropertyDescriptors(classConstructor.prototype);
  const result = {};
  for (const [key, { value, get, set }] of Object.entries(propertyDescriptors)) {
     if (!(get || set) && typeof value === 'function' && key !== 'constructor') {
        result[key] = value;
     }
  }
  return result;
}

function getMethodsRecursive(classConstructor) {
    if (classConstructor === Object) {
      return {};
    }
    const ownMethods = getOwnMethods(classConstructor);
    const base = Object.getPrototypeOf(classConstructor.prototype);
    return {
        ...getMethodsRecursive(base.constructor),
        ...ownMethods,
    };
}

function getMethods(classConstructor) {
    return getMethodsRecursive(classConstructor);
}

// 测试
class Foo {
  p = 0;
  pm = function() {};
  get g() { return 0 }
  get a() { return 0; }
  set a(_v) {}
  set s(_v) {}
  m() {}
  static sm() {}
}

class Base {
    bm() {}
    cm() { console.log('Base version'); }
}

class Derived extends Base {
    dm() {}
    cm() { console.log('Derived version'); }
}

console.log(getMethods(Foo)); // {m: ƒ}
console.log(getMethods(Derived)); // {bm: ƒ, cm: ƒ, dm: ƒ}
console.log(getMethods(Function)); // {apply: ƒ, bind: ƒ, call: ƒ, toString: ƒ}
console.log(getMethods(Object)); // {}