Referring to child class in function inherited from parent - javascript

I'm have method a that decorates a given array of objects:
class Vehicle {
static fromArray(vals) {
return vals.map((v) => { return new Vehicle(v); });
}
}
...I would like to extend that to work on a series of child-classes:
class Boat extends Vehicle {
}
class Car extends Vehicle {
}
Car.fromArray([{name: 'volvo'}]) // should return Car objects, not Vehicle
What do I replace Vehicle with in the parent? Or do I have to override fromArray in the child objects?

You could slightly modify your code:
'use strict'
class Vehicle {
static fromArray(vals) {
var constr = this; // <-- don't need this if you use an arrow function below
return vals.map(function(v) { return new constr(v); });
}
}
class Boat extends Vehicle {
}
class Car extends Vehicle {
drive() { console.log('driving'); }
}
var base = Vehicle.fromArray([1])[0];
var car = Car.fromArray([1])[0];
car.drive();
//base.drive(); // <-- will throw exception
The key here is that for static methods, the this variable is set to the type itself, so you can new this() to create an object of the same type.
Note: this is tested on Chrome (V8), so should work on Node / iojs, BUT, I'm not 100% sure this is according to spec.

In static methods, the this context is typically the constructor function itself, so you can just use that instead of referring to your Vehicle explicitly:
class Vehicle {
static fromArray(vals) {
return vals.map(v => new this(v));
}
}
Notice that static methods are not bound, so if you reference them as a callback or so you have to care about getting this right.

Related

When are JS object variables assigned a value?

I'm just a little confused as to when Object variables are assigned a value.
As far as I remember (and this is from long-lost and hazy Java knowledge so may not be relevant), object variables are initialised prior to constructor calls. I had thought that explicitly declared values would be too, but that doesn't seem to be the case.
For example:
I've a parent class, Shape:
class Shape {
constructor(data) {
this._data = data;
this._data.markup = this._generateMarkup();
}
}
and a child, Circle:
class Circle extends Shape {
_elementName = 'test';
constructor(data) {
super(data);
console.log({data});
}
_generateMarkup() {
console.log(this._elementName);
return `<div class="${this._elementName}">Test</div>`
}
}
this._elementName is returning undefined.
This could well be a mistake on my part, but why is _generateMarkup() - essentially a property of the child - accessible in the parent constructor, but _elementName is not?
codepen link
class Shape {
constructor(data) {
this._data = data;
this._data.markup = this._generateMarkup();
}
}
class Circle extends Shape {
_elementName = 'test';
constructor(data) {
super(data);
console.log({data});
}
_generateMarkup() {
console.log(this._elementName);
return `<div class="${this._elementName}">Test</div>`
}
}
new Circle({});
Thanks
AFAIK the properties of a class are added and initialized in the constructor.
The _generateMarkup is not a property per se, it's a method on the class; it's defined before the constructor is called.
In this case if you want the _elementName to be defined and have a value, you need to add it to the constructor code:
constructor() {
...
this._elementName = 'test';
...
}
On a sidenote, it's generally a bad practice to call methods which aren't inherited or defined on the class.

Call a method when an object (specifically an object) is initialized

I am making a mini state manager library for objects (because. don't ask.) and I want to use an approach like this (pseudo-code)
states = {}
when object is initialized {
if object.keys.states {
states[object.name] = object.keys.states;
}
}
/*
When object is initialized:
if object.keys.states exists:
states[object.name] = object.keys.states
*/
Is there a way to achieve this in typecript/javascript
is this typescript? if this is typescript you could use a class and an interface to execute that code or a constructor inside a class
class states {
states: State[] = [];
constructor(statesc?: State[])
{
if (statesc) {
for(var state in statesc)
{
//no need to do objects keys if you can rotate trough the given parameter
//btw only a class constructor can be called when an object is instantiated otherwise you need to do the
//logic after it has been created
this.states.push(statesc[state]);
}
}
}
}
interface State{
id: number;
name: string;
}
//then you can do this
let states = new States(states); and the constructor gets called

Getting the name of a class on which a static function has been called

So my question is as follows: Can I get the name of a class on which a function got called? To illustrate that problem I will give a short code example.
class Base {
static getClassName() {
// get the caller class here
}
}
class Extended extends Base { }
Base.getClassName(); // Base
Extended.getClassName(); // Extended
Can anyone tell me how to do this, or say if this is even possible.
The following code should work:
class Base {
static getClassName() {
return this.name;
}
}
class Extended extends Base {}
console.log(Base.getClassName()); // Base
console.log(Extended.getClassName()); // Extended
In a static method, this refers to the class object itself.
In ES6, functions have a read-only name field (doc). Since classes are functions, you can simply use MyClass.name to statically get the class name.
To get the class name of functions at runtime, reference the Function.name of the constructor function.
class Base {
getClassName() {
return this.constructor.name;
}
}
class Extended extends Base {}
console.log(Base.name);
console.log(Extended.name);
const base = new Base();
console.log(base.getClassName());
const extended = new Extended();
console.log(extended.getClassName());

How to set a static property on a class in Kotlin for Javascript

I have a situation where I need to define a static property on a class in Kotlin and when its compiled to Javascript have it become a true static field on that class. In this situation companion objects do not work.
For example, if I have an abstract class and its implementing class like below:
abstract class MyAbstractClass{
abstract val id: Int
}
class MyClass: MyAbstractClass(){
override val id: Int = 1 //I want this to actually be "static" on the MyClass
}
The Javascript that this compiles down to is this:
function MyAbstractClass() {
}
function MyClass() {
MyAbstractClass.call(this);
this.id_jz5fma$_0 = 1;
}
Object.defineProperty(MyClass.prototype, 'id', {
get: function () {
return this.id_jz5fma$_0;
}
});
But what I need it to compile down to is this:
function MyAbstractClass() {
}
function MyClass() {
MyAbstractClass.call(this);
}
MyClass.id = 1;
So that the id field does actually statically exist on MyClass without having to make a new instance of MyClass.
I've tried using a companion object but that creates a separate object/function called MyClass$Companion and then assigns the id field to that and never actually assigns it statically to MyClass.
How can I go about setting true static fields like this in Kotlin?
Right now we don’t have a direct way to do it, so I’ve created issue https://youtrack.jetbrains.com/issue/KT-18891
As a workaround, you can write a function like that:
inline fun <reified T : Any> addStaticMembersTo(source: Any) {
val c = T::class.js.asDynamic()
val ownNames = js("Object").getOwnPropertyNames(source) as Array<String>
val protoNames = js("Object").getOwnPropertyNames(source.asDynamic().constructor.prototype) as Array<String>
for (name in ownNames + protoNames) {
c[name] = source.asDynamic()[name]
}
}
And use like:
class A {
companion object {
init {
addStaticMembersTo<A>(object {
val bar = 1
fun foo() {}
})
}
}
}
or even make companion object's members available as a static member of class:
class B {
companion object {
val bar = 1
fun foo() {}
// should be at the end of companion object
init {
addStaticMembersTo<B>(this)
}
}
}
The full example available here:
https://try.kotl.in/#/UserProjects/uube1qikg3vsegtnefo0ad0jag/30f1qf87dt5k5vjhciirt4t108

Referencing static members from instance method on dynamically extended JS class

I have a base ES6 class that I dynamically extend given a configuration object, like so:
class Model {
constructor () {
// ...
}
save () {
// ...
}
}
function createModelFromConfig (config) {
const Impl = class extends Model {};
Object.assign(Impl, config);
return Impl;
}
const User = createModelFromConfig({store: new DbStore()});
In the save() method on the abstract Model, I'd like to reference the static object store, which will exist on the class that extends Model. This means, of course, that I need to reference a static member but the extended class is anonymous.
Just in a quick test using the Chrome console, I tried
function X () {}
X.prototype.doSomething = function () { console.log(this.constructor); };
function Y () {}
Y.prototype = Object.create(X.prototype);
new Y().doSomething(); // function X () {}
I don't know if this is a reliable test, but it appears that this.constructor does not reference the Impl that I extended, but instead the original base class, which isn't helpful.
A less elegant way is to add Impl.prototype.Impl = Impl; so I can use this.Impl.store in my save function, but it'd be preferable if I could access the static members of the Impl class without this.
Is my prototypal test in the console inadequate? Or is there any other way to access the constructor class in an instance method from an inherited method?
In my testing, I've concluded that Y.prototype = Object.create(X.prototype); is not an adequate equivalent to the ES6 extends implementation.
In running in the Node REPL:
class X {
constructor () {}
save () { console.log(this.constructor.z); }
}
class Y extends X {}
Y.z = 'z';
new Y().save(); // 'z'

Categories

Resources