从常规的ES6类方法调用静态方法

什么是调用静态方法的标准方法? 我可以考虑使用constructor或使用类本身的名称,我不喜欢后者,因为它不觉得有必要。 前者是推荐的方式,还是有其他的东西?

这是一个(人为的)例子:

 class SomeObject { constructor(n){ this.n = n; } static print(n){ console.log(n); } printN(){ this.constructor.print(this.n); } } 

这两种方法都是可行的,但是当涉及到用重载的静态方法进行inheritance时,它们做了不同的事情。 select你期望的行为:

 class Super { static whoami() { return "Super"; } lognameA() { console.log(Super.whoami()); } lognameB() { console.log(this.constructor.whoami()); } } class Sub extends Super { static whoami() { return "Sub"; } } new Sub().lognameA(); // Super new Sub().lognameB(); // Sub 

通过类引用静态属性实际上是静态的,并不断给出相同的值。 相反,使用this.constructor将使用dynamic分派,并引用当前实例的类,其中静态属性可能具有inheritance的值,但也可以被覆盖。

这与Python的行为相匹配,您可以select通过类名称或实例self来引用静态属性。

如果您期望静态属性不被覆盖(并且始终引用当前类的一个), 就像在Java中一样 ,使用显式引用。

我偶然发现这个线程寻找类似案例的答案。 基本上所有的答案都可以find,但是从他们那里提取基本要素仍然很困难。

访问的种类

假设一个Foo类可能从其他类派生出来,可能派生出更多的派生类。

然后访问

  • 来自 Foo的静态方法/ getter
    • 一些可能重写的静态方法/ getter:
      • this.method()
      • this.property
    • 一些可能被重写的实例方法/ getter:
      • 不可能的devise
    • 自己的非重载的静态方法/ getter:
      • Foo.method()
      • Foo.property
    • 自己的未覆盖的实例方法/ getter:
      • 不可能的devise
  • 来自 Foo的实例方法/ getter
    • 一些可能重写的静态方法/ getter:
      • this.constructor.method()
      • this.constructor.property
    • 一些可能被重写的实例方法/ getter:
      • this.method()
      • this.property
    • 自己的非重载的静态方法/ getter:
      • Foo.method()
      • Foo.property
    • 自己的未覆盖的实例方法/ getter:
      • 除非使用一些解决方法,否则不可能是有意的
        • Foo.prototype.method.call( this )
        • Object.getOwnPropertyDescriptor( Foo.prototype,"property" ).get.call(this);

请记住,在使用箭头函数或调用显式绑定到自定义值的方法/获取器时,使用这种方式不是这样工作的。

背景

  • 在实例方法或getter的情况下
    • this是指当前的实例。
    • super基本上是指同一个实例,但是在某些当前类的上下文中编写的方法和getter正在扩展(通过使用Foo原型的原型)。
    • 在创build它时使用的实例类的定义可以通过this.constructor
  • 当在静态方法或getter的上下文中不存在“意图等”的“当前实例”
    • this可以直接引用当前类的定义。
    • super也不是指某个实例,而是静态方法和getter在某些类的上下文中编写的当前正在扩展。

结论

试试这个代码:

 class A { constructor( input ) { this.loose = this.constructor.getResult( input ); this.tight = A.getResult( input ); console.log( this.scaledProperty, Object.getOwnPropertyDescriptor( A.prototype, "scaledProperty" ).get.call( this ) ); } get scaledProperty() { return parseInt( this.loose ) * 100; } static getResult( input ) { return input * this.scale; } static get scale() { return 2; } } class B extends A { constructor( input ) { super( input ); this.tight = B.getResult( input ) + " (of B)"; } get scaledProperty() { return parseInt( this.loose ) * 10000; } static get scale() { return 4; } } class C extends B { constructor( input ) { super( input ); } static get scale() { return 5; } } class D extends C { constructor( input ) { super( input ); } static getResult( input ) { return super.getResult( input ) + " (overridden)"; } static get scale() { return 10; } } let instanceA = new A( 4 ); console.log( "A.loose", instanceA.loose ); console.log( "A.tight", instanceA.tight ); let instanceB = new B( 4 ); console.log( "B.loose", instanceB.loose ); console.log( "B.tight", instanceB.tight ); let instanceC = new C( 4 ); console.log( "C.loose", instanceC.loose ); console.log( "C.tight", instanceC.tight ); let instanceD = new D( 4 ); console.log( "D.loose", instanceD.loose ); console.log( "D.tight", instanceD.tight ); 

如果你打算做任何types的inheritance,那么我会推荐this.constructor 。 这个简单的例子应该说明原因:

 class ConstructorSuper { constructor(n){ this.n = n; } static print(n){ console.log(this.name, n); } callPrint(){ this.constructor.print(this.n); } } class ConstructorSub extends ConstructorSuper { constructor(n){ this.n = n; } } let test1 = new ConstructorSuper("Hello ConstructorSuper!"); console.log(test1.callPrint()); let test2 = new ConstructorSub("Hello ConstructorSub!"); console.log(test2.callPrint()); 
  • test1.callPrint()将会loggingConstructorSuper Hello ConstructorSuper! 到控制台
  • test2.callPrint()会loggingConstructorSub Hello ConstructorSub! 到控制台

命名的类不会很好地处理inheritance,除非您明确地重新定义每个引用该命名的类的函数。 这里是一个例子:

 class NamedSuper { constructor(n){ this.n = n; } static print(n){ console.log(NamedSuper.name, n); } callPrint(){ NamedSuper.print(this.n); } } class NamedSub extends NamedSuper { constructor(n){ this.n = n; } } let test3 = new NamedSuper("Hello NamedSuper!"); console.log(test3.callPrint()); let test4 = new NamedSub("Hello NamedSub!"); console.log(test4.callPrint()); 
  • test3.callPrint()会loggingNamedSuper Hello NamedSuper! 到控制台
  • test4.callPrint()会loggingNamedSuper Hello NamedSub! 到控制台

看到所有上面运行Babel REPL 。

从这个angular度可以看出, test4仍然认为这是超级课程。 在这个例子中,它可能看起来不是什么大问题,但是如果你试图引用被覆盖的成员函数或新的成员variables,你会发现自己陷入困境。