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

sim*_*ack 161 javascript static class ecmascript-6 es6-class

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

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

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

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

  printN(){
    this.constructor.print(this.n);
  }
}
Run Code Online (Sandbox Code Playgroud)

Ber*_*rgi 197

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

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
Run Code Online (Sandbox Code Playgroud)

通过类引用静态属性实际上是静态的,并且不断给出相同的值.使用this.constructor反而会使用动态调度和引用类的当前实例,其中静态属性的可能有遗传的价值,但也可以覆盖.

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

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

  • @Chris:每个类都是一个构造函数(就像您从ES5知道的那样,没有类语法),方法的定义没有区别。只需通过[继承的`constructor`属性](http://stackoverflow.com/q/4012998/1048572)或直接按其名称查找即可。 (2认同)

Tho*_*ban 60

我偶然发现了这个线程,寻找类似案例的答案.基本上所有的答案都找到了,但是仍然很难从中提取出必需品.

各种访问

假设一个类Foo可能派生自其他一些类,可能有更多来自它的类.

然后访问

  • 来自 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);

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

背景

  • 在实例的方法或getter的上下文中
    • this 是指当前的实例.
    • super 基本上是指同一个实例,但有些寻址方法和getter在一些类当前类的上下文中编写正在扩展(通过使用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 );
Run Code Online (Sandbox Code Playgroud)


And*_*dri 17

如果你打算做任何类型的继承,那么我会建议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());
Run Code Online (Sandbox Code Playgroud)
  • 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());
Run Code Online (Sandbox Code Playgroud)
  • test3.callPrint()将登录NamedSuper Hello NamedSuper!到控制台
  • test4.callPrint()将登录NamedSuper Hello NamedSub!到控制台

查看以上所有在Babel REPL中运行的内容.

你可以从中看到test4仍然认为它属于超级阶级; 在这个例子中,它可能看起来不是很大,但如果你试图引用已被覆盖的成员函数或新的成员变量,你会发现自己陷入困境.

  • 但静态函数是否没有重写成员方法?通常你会尝试*不*静态地引用任何被覆盖的东西. (3认同)
  • @Bergi 我不确定我是否理解您所指出的内容,但我遇到的一个具体案例是 MVC 模型水合模式。扩展模型的子类可能想要实现静态水合物函数。但是,当这些被硬编码时,仅返回基本模型实例。这是一个非常具体的示例,但是许多依赖于注册实例的静态集合的模式都会受到此影响。一个重要的免责声明是,我们试图在这里模拟经典继承,而不是原型继承......而且这并不流行:P (2认同)