【问题标题】:ES2015 grandchild inheritence bypass chain with Babel transpilerES2015 孙子继承绕过链与 Babel 转译器
【发布时间】:2018-04-17 09:19:35
【问题描述】:

我正在使用 ES2015 Babel 转译器。

我遇到了一些非常奇怪的事情。实际的类很复杂。所以我将使用一个我实际上没有运行的示例。这是一个时间问题我不知道我是否可以用下面的代码完全复制它,但想法是这样的:

class A {
  static instance() {
    if (!A._instance) {
       A._instance = new A();
    }
    return A._instance;
  }

  foo() {
    console.log('A')
  }
}

class B extends A {
  static instance() {
    if (!B._instance) {
       B._instance = new B();
    }
    return A._instance;
  }

  foo() {
    console.log('B')
    super.foo();
  }
}

class C extends B {
  static instance() {
    if (!C._instance) {
       C._instance = new C();
    }
    return C._instance;
  }

  foo() {
    console.log('C')
    super.foo();
  }
}

// Somewhere else
class User {
  static bar() {
    C.instance().foo(); // Sometimes, this calls B.foo() directly, C.foo() is bypassed!
  }
}

// create A._instance first
A.instance().foo();

// now C will inherint _instance from B which inherits is from A.
User.bar()

我在一个普通的 Cordova 项目中使用 Gulp 运行 ES6 转译器。但是当我尝试在桌面 Chrome 中运行它时,同样的事情发生了。

有时C.instance().foo() 实际上并不调用 C 中定义的foo,而是调用B.instance().foo()。 “有时”是指当我加载登录页面并照常登录时,我可以在 Chrome 中 100% 复制它。但我永远无法复制它,如果选中了应用程序的Remember me 选项,并且用户直接登录到主页。这似乎是一个时间问题。但我不知道它到底是什么。有什么线索吗?

编辑 1: 通过在 index.html 中单独使用普通的旧嵌入脚本标签,将代码文件包含到项目中。

【问题讨论】:

  • 我已将您的代码转换为可运行的示例。转译由 babel 完成。它没有显示所描述的错误。我建议您创建一个演示问题并发布转译代码的最小示例。
  • 我添加了可能演示您所描述问题的可能场景。 :) 如果不只是恢复我的更改:)
  • 单例已损坏。具有继承性的单例更加破碎。根本不要尝试这样做。
  • @YuryTarabanko 哇,非常感谢!我不知道我能做到这一点。
  • @Bergi 你说的坏了是指设计坏了?或者你的意思是它的语言被破坏了?很多时候我不知道还能用什么。

标签: javascript ecmascript-6 babeljs


【解决方案1】:

其实问题如下。

如果您首先调用父类instance 方法,您的子类也将由于继承而具有_instance 属性。您需要检查您的子类是否有自己的名为_instance 的属性。

class A {
  /**
   * OOP all the way. Let's define static method that 
   * checks for own property `_instance`
   */
  static hasOwnInstance() {
    return Object.prototype.hasOwnProperty.call(this, '_instance')
  }
  static instance() {
    // rewrite to use this instead of class name
    if (!this.hasOwnInstance()) { // now we check only own properties
       this._instance = new this();
    }
    return this._instance;
  }

  foo() {
    console.log('A')
  }
}

class B extends A {
  /* Now safe to simply inherit implementation from A
  static instance() {
    if (!B.hasOwnInstance()) {  // now we check only own properties
       B._instance = new B();
    }
    return B._instance;
  } */

  foo() {
    console.log('B')
    super.foo();
  }
}

class C extends B {
  foo() {
    console.log('C')
    super.foo();
  }
}

// Somewhere else
class User {
  static bar() {
    C.instance().foo(); // Sometimes, this calls B.foo() directly, C.foo() is bypassed!
  }
}

// call B.instance first
console.log('B.foo')
B.instance().foo();

console.log('C.foo')
User.bar()

【讨论】:

  • 为什么不继承instance 方法,使用this 而不是各自的类名?
  • @Bergi 是的,更好的选择是将instance 重写为使用this
  • 以前我使用ES5的时候也是这样,为什么没有发生呢?我只是幸运吗?我一直认为C._instanceB._instance 是独立的命名空间。毕竟是function C()function B()
  • @huggie “我一直认为 C._instance 和 B._instance 是独立的命名空间。”他们有一种方式“连接”。 C.[[prototype]] 设置为 B。您稍后添加到 B 的所有内容都将通过原型链立即在 C 中可用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 2016-04-27
  • 1970-01-01
相关资源
最近更新 更多