Let's say I have two ES6 classes like this:
class Base {
static something() {
console.log(this);
}
}
class Derived extends Base {
}
And then I make a call like this:
Derived.something();
Note that I am making a call to a static method defined on the super class via sub class.
This does not give me errors. It prints
[Function: Derived]
So accessing this within a static method seems to work here.
I need a common static method for all sub-classes of a super class and I need to be able to know what sub-class is calling this method.
Now my question is whether using this within a static method is legal. I know these static methods become class methods, and hence this would naturally point to the class object they are called on. (The class object being the constructor.)
But I can't seem to find any definitive resource that states that this is allowed by the ES specification.
This looks like a good introduction to ES6 classes but does not talk about this with static.
Under typical circumstances, the this in any call to something.method() will refer to something as long as the function is not an arrow function, bound function, or something like that (and it is neither of those in this case).
Class inheritance, or even ES6, aren't really relevant here. All you need to know is that you are calling Derived.something(), so this will refer to Derived.
Yes, this is legal in static methods, that's the way this should be done.
this refers to class instance in prototype methods and refers to class constructor in static methods, unless a method was unbound from its original context.
Similarly, super refers to parent class prototype in instance methods and refers to parent class constructor in static methods.
As long as the static method is invoked as a member expression, e.g.
Derived.something();
as opposed to
const { something } = Derived;
something();
then this will refer to Derived. Derived.something() is identical to something.call(Derived) if Derived.something is stored to an intermediate variable, because that's how a member expression with a nested call expression is evaluated, essentially.
Related
I'm currently learning Javascript. When I run the following code, I am getting undefined. Not sure what is wrong. Everything seems to be fine. Any help will be appreciated. Thank you.
class User{
constructor(){
this.array = [1, 2, 3]
}
static getNumber(){
return console.log(this.array)
}
}
User.getNumber()
First of all, you have to create a new instance of User with new User() or new User if you want to be able to access instance properties (such as array), because they are created with this.array = .... From the Mozilla Web Docs:
The static keyword defines a static method or property for a class, or
a class static initialization block (see the link for more information
about this usage). Neither static methods nor static properties can be
called on instances of the class. Instead, they're called on the class
itself.
Static methods are often utility functions, such as functions to
create or clone objects, whereas static properties are useful for
caches, fixed-configuration, or any other data you don't need to be
replicated across instances.
Because getNumber is called on the constructor itself, it doesn't have access to any of the instance properties (the only one is this.array). When new User is called, it creates the this.array, but only for the instance, not the constructor.
I am new to learning objects and classes in Javascript. I was just wondering, why would you attach a static method to a class, like so:
class MyClass {
static myFunction(){
console.log('foo');
}
}
When you can just declare a regular, custom function outside of the class like one usually does?
function myFunction() {
console.log('foo');
}
A static "method" is just a regular function that is attached to a class. This is useful when it belongs to that class semantically, and in extreme cases necessary in an inheritance hierarchy. The class name provides a visual namespace for accessing the function, e.g. Map.from does something different than Set.from.
However, you would only ever do that when you already have an existing class. You would never create an empty class only to put a static method inside it. In such a case, a simple object literal with a regular object method suffices:
const MyObject = {
myFunction() {
console.log('foo');
},
};
MyObject.myFunction();
Mostly for namespacing. Imagine you need 10 functions of related feature. Instead of taking up 10 names from global scope, using static methods you use just one global name, being the class name.
Remember that apps often end up being complex, using 3rd party libraries. Name clashes is a real problem when complexity comes to play.
class StaticMethodCall {
static staticMethod() {
return 'Static method has been called';
}
static anotherStaticMethod() {
return this.staticMethod() + ' from another static method';
}
}
StaticMethodCall.staticMethod();
// 'Static method has been called'
StaticMethodCall.anotherStaticMethod();
// 'Static method has been called from another static method'
Why this work ? I expected fatal error in this case before.
this in JS is not the same as this in the likes of an actual OO language like Java etc. Where this in the likes of java refers to the current object of a class, in JS there are no 'classes', this in JS just refers to a current context scope, which may be, in your case, a 'static' method on a 'class', which will essentially boil down to being the current scope of a plain old object
When you call a function via a reference like someobject.property(), the this value will be set to someobject. That's a basic feature of the language. Static methods are properties of the constructor function, so when you call them via a reference to the constructor the value of this will be that function.
In languages like Java, the static methods belong to a class rather than an instance so when you use this inside static method you get an compilation error as the method doesn't know which instance to refer to (as there could be many instances of the class).
But the class is just a syntactic sugar for the JavaScript function object, so when you call StaticMethodCall.anotherStaticMethod(); here this will point to the context of the StaticMethodCall class object which is nothing but a function object under the hood. JavaScript functions are also objects which can have properties declared on them. So when you use a constructor function (es6 class) you could declare properties on the function object which is nothing but the static in es6.
Let me explain this way, the following code is what is happening under the hood if you transpile it to es5 through Babel:
//This is what static means in es6 class. they are properties of the constructor function
function StaticMethodCall(){ //This represents your class
}
StaticMethodCall.staticMethod = function(){
return 'Static method has been called';
}
StaticMethodCall.anotherStaticMethod = function(){
return this.staticMethod() + ' from another static method';
}
console.log(StaticMethodCall.staticMethod());
// StaticMethodCall is a reference to the class object, this points to.
console.log(StaticMethodCall.anotherStaticMethod());
Here when you define static methods on es6 classes the methods are added as properties of the constructor function so when you call the static method with the reference of the class StaticMethodCall this is actually pointing to that class/function object.
So this is not the same as you would expect in Java, where you cannot refer to this inside a static context as it does not know which object to refer to (as classes are not objects). In Java, classes are not actually objects like JavaScript.
Another way to verify that es6 class is actually a function is by using the typeof:
class StaticMethodCall{
}
console.log(typeof StaticMethodCall); //outputs "function"
To understand it better you should check the transpiled code from
Babel
the scope of static functions is the same scope of the class (that compiled is a Constructor function).
Static functions are declared as properties on the Constructor function, whether normal functions are declared as properties on the Constructor.prototype and are available consequently on all the instances.
How to get (console.log for ex.) B class's methods in A class's constructor?
class A {
constructor() {
// GET B's method names ('ok', ...) here
}
}
class B extends A {
constructor() {
super();
}
ok() {
}
}
In the "base" constructor you have access to complete object, so can check what is its real constructor and so its prototype const childClassPrototype = this.constructor.prototype. Having a "child" prototype you can get a list of its properties with Object.getOwnPropertyNames(childClassPrototype). From that list you want to filter out "constructor" and properties that are not functions.
Note: this technique will only give you access to "leaf" prototype, once you may have a multi level prototype chain. Thus you have to iterate over prototype chain.
Note2: for autobinding you may like to consider using a decorator. One implementation is here: https://github.com/andreypopp/autobind-decorator - this technique gives you better control over unexpected behavior that may come from metaprogramming
Use either new.target.prototype or Object.getPrototypeOf(this) to get the prototype object of the instantiated subclass. Then traverse the prototype chain to all superclasses, and get the own properties of each object. Don't forget non-enumerable properties.
Of course, using this in a constructor for more than logging/debugging purposes is a code smell. A class should not need to know about its subclasses. If you want to do autobindings, let each subclass constructor autobind its own methods.
So I define classes by using their constructor functions, and in these functions I define all their properties, and do constructor-y things like setting up listeners and such. After defining the constructor, I then go and add all of it's functions directly to its prototype.
To do inheritance, I essentially do this:
namespace.Class = function(param) {
namespace.BaseClass.call(this, param);
//Other constructor stuff
}
namespace.Class.prototype = new namespace.BaseClass();
namespace.Class.constructor = namespace.Class;
The problem is that the BaseClass' constructor gets called when creating the prototype, which causes the prototype to be an instance of that type. This may not be a problem, but it just feels "messy" and performance lagging, and potentially buggy because I have to check if the params are defined.
So my question is: Is it possible to extend a base class without calling its constructor? Or will I have to use tricks such as checking if param is undefined, or using a loop to do stuff?
Sorry if I've missed the whole point of prototypal inheritance, I am being taught Java in uni, and Java doesn't approve of this type of stuff.
Yes it is possible. The preferred way is to use Object.create [MDN]:
namespace.Class.prototype = Object.create(namespace.BaseClass.prototype);
This creates a new, empty object which inherits from BaseClass.prototype, which is all you need at this point.
This is a much cleaner solution than creating an instance of the parent class, because, as you already mentioned, if the super class expects arguments, you don't know which ones to pass.