从常规的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
- 一些可能重写的静态方法/ 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);
-
- 除非使用一些解决方法,否则不可能是有意的
- 一些可能重写的静态方法/ getter:
请记住,在使用箭头函数或调用显式绑定到自定义值的方法/获取器时,使用这种方式不是这样工作的。
背景
- 在实例方法或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,你会发现自己陷入困境。