Good way of accessing "upper class" methods inside a nested class? - javascript

Let's say I have a program:
class Program {
constructor() {
this.profileManager = new ProfileManager();
}
saveProgramConfig() {
// ...
}
}
If something happens inside ProfileManager, and it needs to save the program configuration, what would be the best way of accessing this specific instance's saveProgramConfig method from inside the ProfileManager class?

You could pass the instance as an argument so that both instances have a link to each other:
this.profileManager = new ProfileManager(this);
and have ProfileManager save the instance to one of its own properties, and call saveProgramConfig on it when needed.

What the issue with the following approach? you can access the test method of Program inside ProfileManager class.
class Program {
test() {
return "Hello World";
}
}
class ProfileManager extends Program {
constructor() {
super();
}
main() {
return this.test();
}
}
const tp = new B;
console.log(tp.main());

Related

How to reuse a class method defined in one class which can be used in another class in typescript?

So, I'm new to Typescript, and recently started learning from documentation. I was looking at its documentation and there were no signs of reusing method from other class.
Class A file
export class A{
... constuctor(){
const queue = this.createQueue()
}
createQueue(){
console.log("Has access to this", +this);
}
}
And there is a class B with exact same definations and uses the same method. How do I make this reusable so that, both of them call with "this"?
One solution I thought of is to create a seperate helper Class that could I use, but I'm not sure how to do that.
Any thoughts?
In this case, inheritance would probably be your best bet. This basically means that you can extend upon another class and include all of the other one's properties/getters/setters/methods.
// we denote this class as abstract because it must be extended and cannot be directly initialized, i.e. new BaseFoo()
abstract class BaseFoo<T> { // `T` is the item type within the queue
queue: T[];
constructor() {
this.queue = this.createQueue();
}
createQueue() {
// can access `this`
return [];
}
}
class FooA extends BaseFoo<number> {
constructor() {
super(); // runs child class's constructor
console.log(this.queue); // []
}
}
class FooB extends BaseFoo<string> {
constructor() {
super();
console.log(this.queue); // []
}
}
TypeScript Playground Link

Is it possible for a javascript class to define a method which fires if the class is called?

In python there's something like __call__ for this. Consider the following example:
class MyClass {
__call__() { return 'called!' }
}
const myType = new MyClass();
myType(); // called!
The question is what should I replace __call__ with?
I was doing some research, and most of the answers recommend __proto__, but it doesn't seem to work.
It is not possible out-of-the-box, but you can extend Function, and use the Function constructor to forward a call to __call__. If you have multiple classes that need this feature, extend Function only once into -- let's say -- a Callable class, and then inherit your other classes from that:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
__call__() { return 'called!' }
}
let inst = new Class();
console.log(inst());
Background
In JavaScript an object is callable when, and only if, it has the [[Call]] internal slot. But there is (currently) no way to give any given object this slot via JavaScript code. One must start with a function object and extend that.
Adding a constructor, inheritance
The above solution allows the constructor to define properties in the usual way: the constructed object is an instance of the class:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
constructor(data) {
super();
this.x = data;
}
__call__() { return 'called!' }
}
let inst = new Class(42);
console.log(inst instanceof Class); // true
console.log(inst.x); // 42
console.log(inst());
You can use constructor.
class Example {
constructor() {
// gets called on class initialization
}
}
Inside the constructor you can also call other methods if you want.
However this won't create an invoke function like using PHP's __invoke if that's what you meant. If that's what you're looking for then I don't know.

Implementing JS decorator to wrap class

I'm trying to wrap class constructor and inject to some logic by using class decorator. Everything worked fine until I have tried to extend wrapped class: Extended class don't have methods in prototype.
function logClass(Class) {
// save a reference to the original constructor
const _class = Class;
// proxy constructor
const proxy = function(...args) {
const obj = new _class(...args);
// ... add logic here
return obj
}
// copy prototype so intanceof operator still works
proxy.prototype = _class.prototype;
// return proxy constructor (will override original)
return proxy;
}
#logClass
class Base {
prop = 5;
test() {
console.log("test")
}
}
class Extended extends Base {
test2() {
console.log("test2")
}
}
var base = new Base()
base.test()
var ext = new Extended()
console.log(ext.prop)
ext.test()
ext.test2() // TypeError: ext.test2 is not a function
Okay so I tried to figure out what is "wrong" with your code, but I was not able to make it work because it didn't typecheck. So, as a last resort, I'm posting a partial answer of my attempt, which works (with some quirks) so I can help other users who are more savvy with TypeScript.
First of all, the quirks: class decorators in TS cannot modify the structure of a type, so if you wanted to, for example, add a method to the decorated class, you would be able to do it but you would have to eat up/suppress unavoidable type errors (TS2339) when calling those methods.
There is a work around for this in this other question: Typescript adding methods with decorator type does not exist, but you would lose this current clean syntax for decorators if you do this.
Now, my solution, taken more or less directly from the documentation:
function logClass<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
constructor(...args: any[]) {
super(args);
// ...add programmatic logic here
// (`super` is the decorated class, of type `T`, here)
}
// ...add properties and methods here
log(message: string) { // EXAMPLE
console.log(`${super.constructor.name} says: ${message}`);
}
}
}
#logClass
class Base {
prop = 5;
test() {
console.log("test");
}
constructor() {}
}
class Extended extends Base {
test2() {
console.log("test2");
}
}
var base = new Base();
base.test();
var ext = new Extended();
console.log(ext.prop);
//base.log("Hello"); // unavoidable type error TS2339
ext.test();
ext.test2();

Instantiate subclass without constructing

I'm trying to write a subclass using the es6 class syntax. The subclass has some complicated logic to perform before calling the superclass constructor, so I tried factoring it out into a function. However, this seems to be impossible since this is not defined until after super() is called.
I've tried simply calling super() twice, once at the start of the constructor and once at the end, but it feels wrong and wastes the work that the superclass constructor does the first time.
class Parent {
constructor(x) {
console.log('some expensive thing with ' + x);
}
}
class Child extends Parent {
constructor() {
let x = this.f();
super(x);
}
f() {
// complicated logic
return 3;
}
}
let c = new Child();
Running the code as written results in ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor at new Child. Removing this and attempting to call f() results in ReferenceError: f is not defined at new Child.
Is there any way to factor the subclass constructor logic somewhere else, even if it's okay if this isn't bound?
I would use an initialization function separate from the constructor as that give you more control over when/if parent initialization happens.
class Parent {
constructor(x) {
this.init(x);
console.log("parent constructor does other stuff");
}
init(x) {
console.log("parent init runs")
}
}
class Child extends Parent {
constructor(x) {
super(x);
}
init(x) {
console.log("child init runs");
super.init(x); // This call is optional if you don't want to run the parent's init code
}
}
let c = new Child();
Using a static method could be a solution.
class Parent {
constructor(x) {
console.log('some expensive thing with ' + x);
}
}
class Child extends Parent {
constructor() {
let x = Child.f();
super(x);
}
static f() {
// complicated logic
return 3;
}
}
let c = new Child();

webview js interface can not use prototype

I use webview inject a object just like this:
public class mediaplayer {
#JavascriptInterface
#SuppressWarnings("unused")
public void testInterface(int num) {
Log.d("mediaplayer","testInterface2...." + num);
}
}
public mediaplayer _mediaplayer = new mediaplayer();
WebView.addJavascriptInterface(_mediaplayer, "IPTVPlayer");
and i want use it in web page, just like this:
function MediaPlayer2()
{
return IPTVPlayer;
}
MediaPlayer2.prototype.setPlayerParams = function (channel) {
console.log('MediaPlayer2.prototype.setPlayerParams2................');
};
var mp2 = new MediaPlayer2();
mp2.setPlayerParams("this is test");
when i run it ,it come out error:
TypeError: Object [object Object] has no method 'setPlayerParams'
I must use webview method just like that, so must return 'IPTVPlayer' .I don't know why i can not use prototype method, please give me some suggestions. I'll appreciate it.
Prototype-based inheritance without classes can be quite tricky in JavaScript.
There are two main things you have to do:
// 1. Correctly set up the constructors to call the super constructor
function MediaPlayer2() {
// Call the "super" constructor with the new instance (this).
IPTVPlayer.call(this)
}
// 2. Correctly set up the lookup chain via the prototype
MediaPlayer2.prototype = Object.create(IPTVPlayer.prototype);
MediaPlayer2.prototype.constructor = MediaPlayer2;
// Now you can start adding methods.
MediaPlayer2.prototype.setPlayerParams = ...
I suggest to keep on reading a bit more background work as well and start using ES6 classes which are more explicit:
class MediaPlayer2 extends IPTVPlayer {
constructor() { super(); ... }
setPlayerParams(...) { ... }
}

Categories