从常规 ES6 类方法调用静态方法访问类型结论

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

下面是一个(人为的)示例:

class SomeObject {
  constructor(n){
    this.n = n;
  }

  static print(n){
    console.log(n);
  }

  printN(){
    this.constructor.print(this.n);
  }
}

答案 1

这两种方式都是可行的,但是在使用覆盖的静态方法进行继承时,它们会做不同的事情。选择您期望其行为的那个:

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

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

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


答案 2

我偶然发现了这个线程,寻找类似案例的答案。基本上所有的答案都找到了,但仍然很难从中提取要点。

访问类型

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

然后访问

  • 从 Foo 的静态方法/getter
    • 一些可能被覆盖的静态方法/getter:
      • this.method()
      • this.property
    • 一些可能被覆盖的实例方法/getter:
      • 设计不可能
    • 自己的非重写静态方法/getter:
      • Foo.method()
      • Foo.property
    • own 非重写实例方法/getter:
      • 设计不可能
  • 从 Foo 的实例方法/获取器
    • 一些可能被覆盖的静态方法/getter:
      • this.constructor.method()
      • this.constructor.property
    • 一些可能被覆盖的实例方法/getter:
      • this.method()
      • this.property
    • 自己的非重写静态方法/getter:
      • Foo.method()
      • Foo.property
    • own 非重写实例方法/getter:
      • 除非使用一些解决方法,否则不可能有意为之
        • Foo.prototype.method.call( this )
        • Object.getOwnPropertyDescriptor( Foo.prototype,"property" ).get.call(this);

请记住,当使用箭头函数或调用显式绑定到自定义值的方法/getter 时,using 不是以这种方式工作的。this

背景

  • 在实例的方法或 getter 的上下文中时
    • this是指当前实例。
    • super基本上是指相同的实例,但是在当前某个类的上下文中编写的一些寻址方法和 getters 正在扩展(通过使用 Foo 原型的原型)。
    • 创建时使用的实例类的定义根据 提供。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 );