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

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

这里有一个(虚构的)例子:

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


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


printN(){
this.constructor.print(this.n);
}
}
133663 次浏览

如果你打算做任何类型的继承,那么我会推荐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()将记录ConstructorSuper Hello ConstructorSuper!到 李控制台< / >
  • test2.callPrint()将把ConstructorSub Hello ConstructorSub!记录到控制台

命名类不能很好地处理继承,除非显式地重定义每个引用命名类的函数。这里有一个例子:

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()将记录NamedSuper Hello NamedSuper!到 李控制台< / >
  • test4.callPrint()将把NamedSuper Hello NamedSub!记录到控制台

看到所有以上运行在Babel REPL

你可以从中看到test4仍然认为它在超类中;在本例中,这似乎不是什么大问题,但如果您试图引用已覆盖的成员函数或新成员变量,那么您将遇到麻烦。

这两种方法都是可行的,但是当涉及到带有重写静态方法的继承时,它们所做的事情是不同的。选择一个你期待其行为的人:

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的行为相匹配,在Python中,您可以选择通过类名或实例self引用静态属性。

如果你希望静态属性不被覆盖(并且总是引用当前类中的一个),比如在Java中,使用显式引用。

我无意中在这个帖子中寻找类似情况的答案。基本上所有的答案都找到了,但仍然很难从中提取出要点。

访问类型

假设一个类Foo可能派生自另一个class(es),并且可能派生出更多的类。

然后访问:

  • static方法/ Foo类的getter
    • some probably 覆盖static method/getter:
      • this.method()
      • this.property
    • some probably 覆盖实例 method/getter:
      • 设计上不可能
    • own non-overridden静态 method/getter:
      • Foo.method()
      • Foo.property
    • own non-overridden实例 method/getter:
      • 设计上不可能
  • Foo类的从实例方法/getter:
    • some probably 覆盖static method/getter:
      • this.constructor.method()
      • this.constructor.property
    • some probably 覆盖实例 method/getter:
      • this.method()
      • this.property
    • own non-overridden static method/getter:
      • Foo.method()
      • Foo.property
    • own non-overridden实例 method/getter:
        <李> 不可能的意图,除非使用一些变通办法:
        • Foo.prototype.method.call( this )
        • Object.getOwnPropertyDescriptor(Foo.prototype,'<property>' ).get.call(this)

请记住,在使用箭头functions或调用显式绑定到自定义值的方法/getter时,使用this不是这样工作的。

背景

  • 在实例的方法/getter的上下文中
    • this引用当前实例。
    • super基本上引用了相同的实例,但是在某些class当前的上下文中编写的寻址方法/getter正在扩展(通过使用Foo的原型的原型)。
    • 实例创建时使用的class的定义在this.constructor中可用。
  • 当在static方法/getter的上下文中没有“当前实例”;有意地:
    • this可以直接引用当前class的定义。
    • super也不是指某个实例,而是指在某个class当前实例正在扩展的上下文中编写的static方法/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 );