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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28627908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 02:08:02  来源:igfitidea点击:

Call static methods from regular ES6 class methods

javascriptclassstaticecmascript-6es6-class

提问by simonzack

What's the standard way to call static methods? I can think of using constructoror using the name of the class itself, I don't like the latter since it doesn't feel necessary. Is the former the recommended way, or is there something else?

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

Here's a (contrived) example:

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

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

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

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

回答by Bergi

Both ways are viable, but they do different things when it comes to inheritance with an overridden static method. Choose the one whose behavior you expect:

这两种方式都是可行的,但是当涉及到使用重写的静态方法进行继承时,它们会做不同的事情。选择您期望的行为:

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

Referring to the static property via the class will be actually static and constantly give the same value. Using this.constructorinstead will use dynamic dispatch and refer to the class of the current instance, where the static property mighthave the inherited value but could also be overridden.

通过类引用静态属性实际上是静态的,并不断给出相同的值。使用this.constructor替代将使用动态调度并引用当前实例的类,其中静态属性可能具有继承的值,但也可能被覆盖。

This matches the behavior of Python, where you can choose to refer to static properties either via the class name or the instance self.

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

If you expect static properties not to be overridden (and always refer to the one of the current class), like in Java, use the explicit reference.

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

回答by Thomas Urban

I stumbled over this thread searching for answer to similar case. Basically all answers are found, but it's still hard to extract the essentials from them.

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

Kinds of Access

访问类型

Assume a class Foo probably derived from some other class(es) with probably more classes derived from it.

假设一个类 Foo 可能派生自其他一些类,并且可能有更多的类从它派生而来。

Then accessing

然后访问

  • from staticmethod/getter of Foo
    • some probably overridden staticmethod/getter:
      • this.method()
      • this.property
    • some probably overridden instancemethod/getter:
      • impossible by design
    • own non-overridden staticmethod/getter:
      • Foo.method()
      • Foo.property
    • own non-overridden instancemethod/getter:
      • impossible by design
  • from instancemethod/getter of Foo
    • some probably overridden staticmethod/getter:
      • this.constructor.method()
      • this.constructor.property
    • some probably overridden instancemethod/getter:
      • this.method()
      • this.property
    • own non-overridden staticmethod/getter:
      • Foo.method()
      • Foo.property
    • own non-overridden instancemethod/getter:
      • not possible by intention unless using some workaround:
        • Foo.prototype.method.call( this )
        • Object.getOwnPropertyDescriptor( Foo.prototype,"property" ).get.call(this);
  • 来自Foo 的静态方法/getter
    • 一些可能被覆盖的静态方法/getter:
      • this.method()
      • this.property
    • 一些可能被覆盖的实例方法/getter:
      • 设计不可能
    • 自己的非覆盖静态方法/getter:
      • Foo.method()
      • Foo.property
    • 自己的非覆盖实例方法/getter:
      • 设计不可能
  • 来自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);

Keep in mind that using thisisn't working this way when using arrow functions or invoking methods/getters explicitly bound to custom value.

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

Background

背景

  • When in context of an instance's method or getter
    • thisis referring to current instance.
    • superis basically referring to same instance, but somewhat addressing methods and getters written in context of some class current one is extending (by using the prototype of Foo's prototype).
    • definition of instance's class used on creating it is available per this.constructor.
  • When in context of a static method or getter there is no "current instance" by intention and so
    • thisis available to refer to the definition of current class directly.
    • superis not referring to some instance either, but to static methods and getters written in context of some class current one is extending.
  • 在实例的方法或 getter 的上下文中时
    • this指的是当前实例。
    • super基本上指的是同一个实例,但有些寻址方法和 getter 是在当前正在扩展的某个类的上下文中编写的(通过使用 Foo 原型的原型)。
    • 用于创建实例的类的定义在每个this.constructor.
  • 当在静态方法或 getter 的上下文中没有意图的“当前实例”时,因此
    • this可以直接引用当前类的定义。
    • super也不是指某个实例,而是指在当前正在扩展的某个类的上下文中编写的静态方法和 getter。

Conclusion

结论

Try this code:

试试这个代码:

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 );

回答by Andrew Odri

If you are planning on doing any kind of inheritance, then I would recommend this.constructor. This simple example should illustrate why:

如果您打算进行任何类型的继承,那么我会推荐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()will log ConstructorSuper Hello ConstructorSuper!to the console
  • test2.callPrint()will log ConstructorSub Hello ConstructorSub!to the console
  • test1.callPrint()将登录ConstructorSuper Hello ConstructorSuper!到控制台
  • test2.callPrint()将登录ConstructorSub Hello ConstructorSub!到控制台

The named class will not deal with inheritance nicely unless you explicitly redefine every function that makes a reference to the named Class. Here is an example:

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

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()will log NamedSuper Hello NamedSuper!to the console
  • test4.callPrint()will log NamedSuper Hello NamedSub!to the console
  • test3.callPrint()将登录NamedSuper Hello NamedSuper!到控制台
  • test4.callPrint()将登录NamedSuper Hello NamedSub!到控制台

See all the above running in Babel REPL.

见通天REPL运行上述所有

You can see from this that test4still thinks it's in the super class; in this example it might not seem like a huge deal, but if you are trying to reference member functions that have been overridden or new member variables, you'll find yourself in trouble.

从中可以看出,test4仍然认为它在超类中;在这个例子中,它可能看起来不是什么大问题,但是如果您试图引用已被覆盖的成员函数或新的成员变量,您会发现自己遇到了麻烦。