A way to retrieve the context without passing `.this` as parameter - javascript

Here is a little background.
So I have my parent class. Constructor receives two objects: the first one is the class where the instance of the parent object was created (using others classes which are inheriting parent class).
The second object are just some parameters
parent.js
class ParentClass {
constructor(nativeObject, params) {
this.nativeObject = nativeObject;
this.param1 = params.param1;
this.param2 = params.param2;
}
// some logic here
}
module.exports = { ParentClass };
child.js
class ChildClass extends ParentClass {
// some logic here
}
module.exports = { ChildClass };
I'm using the child class in the other class methods for creating it. I can have multiple files like below:
usingclasses.js
let { ChildClass } = require('path/to/my/child/class');
let opts = {
param1: 'idkSomeString';
param2: 42;
}
class MyCoolClass {
createChild() {
return new ChildClass(this, opts);
}
}
module.exports = { MyCoolClass };
anotherclasses.js
let { ChildClass } = require('path/to/my/child/class');
let opts = {
param1: 'thatAstringToo';
param2: 123;
}
class AnotherCoolClass{
alsoCreatesChild() {
return new ChildClass(this, opts);
}
}
module.exports = { AnotherCoolClass};
I can create multiple instances of the ChildClass in a different methods within one class:
thirdexample.js
let { ChildClass } = require('path/to/my/child/class');
let opts1 = {
param1: 'aStringAgain';
param2: 27;
}
let opts2 = {
param1: 'ABC';
param2: 33;
}
class ClassAgain{
firstMethod() {
return new ChildClass(this, opts1);
}
secondMethod() {
return new ChildClass(this, opts2);
}
}
module.exports = { ClassAgain };
The reason why I'm passing .this because I'm working with it in ParentClass
e.g for logging console.log(`I know this was created in ${this.nativeObject .constructor.name}`);
What I'm looking for is the way to get rid of passing .this every time I'm creating a new instance of the ChildClass
Reason behind this is that in one file (class) I can create up to 10+ instances of the ChildClass.
In the previous question I already got an answer which helped me to make the code more structured and more easy to maintain:
Instead of:
createChild() {
return new ChildClass(this, param1, param2, param3, paramN);
}
I'm doing:
createChild() {
return new ChildClass(this, paramObj);
}
So now I can store all my objects with parameters at the top of the file (of even separate file) and easily find and change it if needed.
...
Now I'm trying to find a way to get rid of .this every time I'm creating ChildClass
Is there any other way how ParentClass can know where does this ChildClass instances "lives"?
I'm trying to figure out can I do something using .apply or .call but seems like this is not a droids I'm looking for
UPDATE:
Since I have zero progress on this and I even got a comment that most likely this is not possible let's assume for now that I need a name of the class that is creating instance of the ChildClass only for logging.
How can I retrieve the name of the class without passing it every time when creating a ChildClass?
let opts = {
className: 'MyCoolClass'
param1: 'idkSomeString';
param2: 42;
}
class MyCoolClass {
createChild() {
return new ChildClass(opts);
}
}
module.exports = { MyCoolClass };
The problem is that I will have to set className: 'MyCoolClass' in each version of opts again and again and again... Just repeat the same for 10-15 times

You can just instantiate your classes using Reflect.construct.
class ParentClass {
constructor (nat, x) {
this.nat = nat
this.x = x
}
log () {
console.log('from ', this.nat.constructor.name, this.x)
}
}
class ChildClass extends ParentClass{}
class ClassAgain{
firstMethod () {
return this.makeInstance({ a: 1 })
}
secondMethod () {
return this.makeInstance({ b: 2 })
}
makeInstance (params) {
return Reflect.construct(ChildClass, [this, params])
}
}
const ca = new ClassAgain()
ca.firstMethod().log()
ca.secondMethod().log()

Related

Getting a reference to a parent class instance from within an injected object

Im wondering if there is any way to pass a reference down to an injected object in Javascript. Cannot use inheritance in this case as this class will take many objects as parameters within its constructor that are created elsewhere.
class Parent {
private Child child;
constructor(child: Child) {
this.child = child;
}
// method needs to be called from within the `child`
public log(message: string) {
// logs out "Parent logged: This is a log message from the child"
console.log(`Parent logged: ${message}`);
}
}
class Child {
private Parent: parent;
constructor() { }
log() {
parent.log("This is a log message from the child");
}
}
If the object is built in the Parent constructor, you can just pass a reference in and then assign it to a property within Child. However, unfortunately the object is created outside the Parent class.
this.child = new Child(this);
It sounds like you just want a setter method on the child that allows you to set the parent, right?
class Child {
private Parent: parent;
constructor() { }
setParent(parent) {
this.parent = parent;
}
log() {
this.parent.log("This is a log message from the child");
}
}
Editing here, since I can't put code in the comment well. This is a much more complex example, but it's a well-defined pattern.
A similar example is the Datasource class in Apollo GraphQL, which has a base class that all of your "children" would implement, and you use that for cross-dependency with anything:
interface SomeDataSources {
someChild1: SomeChild1;
someChild2: SomeChild2;
someChild3: SomeChild3;
}
interface SomeContext {
datasources: SomeDataSources;
logger: Logger;
dbConnection: Connection;
}
class BaseDataSource {
context: SomeContext;
initialize({ context }: { context: SomeContext }) {
this.context = context;
}
}
class EachChildDoesThis extends BaseDataSource {
...
}
/**
* Instead of using a `Parent` class, this method does all of the
* dependency injection and closures necessary.
*/
const buildContext = async ({ logger, dbConnection }): Promise<SomeContext> => {
const context: SomeContext = {
logger,
dbConnection,
dataSources: {
someChild1: new SomeChild1(),
someChild2: new SomeChild2(),
someChild3: new SomeChild3(),
}
}
for (const dataSource of Object.values(context.dataSources)) {
await dataSource.initialize({
context,
});
}
return context;
};
Now you have an entire suite of functionality here that all have access both to the things you're passing in and to each other (dependency-injectedly) by doing this:
const logger = new Logger();
const dbConnection = createConnection(dbConfig);
const context = buildContext({ logger, dbConnection });
Any one of those child classes could do something like:
class SomeChild1 extends BaseDatasource {
async someMethod() {
this.context.logger.info('something something');
// or use the db
await this.context.dbConnection.doSomething();
// or even access its siblings
await this.context.dataSources.someChild2.delete('some-user-id');
}
}
Because of the dependency injection, each one of these is fully stand-alone, testable, and composable.
You could the following:
class Parent {
name: string;
age: number;
child = {};
constructor(name: string, age: number, child: Child) {
this.child = child;
this.name = name;
this.age = age;
}
static countParents() {
return `Parent count at: 10.`;
}
log(message: string) {
console.log(`Parent logged: ${message}.`);
}
}
class Child extends Parent {
name: string;
age: number;
favoriteHero: string
message: string
constructor(name, age, favoriteHero) {
super(name, age, favoriteHero);
this.favoriteHero = favoriteHero;
}
bio() {
return `${this.name} is 12 and his favorite super hero is ${this.favoriteHero}!`
}
}
const charlie = new Child('Charlie', 12, 'Batman')
charlie.log('Good things') // Parent logged: Good things. 
console.log(charlie.bio()); // Charlie is 12 and his favorite super
hero is Batman!
console.log(Parent.countParents()); // Parent count at: 10

nested properties on a class instance

I need to build a structure (i need to do it for jestjs, in order to stub a module) that allows me to access a method in the following way:
// I make an instance of a class
const myTest = new Test()
// I access a method in this way
const result = myTest.prop1.prop2.prop3.getAll()
A basic class would look like
class Test2 {
constructor(){}
//getter
getAll(){
return 'Salutes'
}
}
And I can access getAll()
const myTest2 = new Test()
const result = myTest.getAll()
//in this way result contains a string (*salutes*)
So how can I add more properties in the middle? I found something, but in typescript... I need to do it in javascript
Assuming you need exactly the structure you've specified, in Test you'd have to create the object and have any methods you need be arrow functions or bound functions (if they need to access information from the Test instance; your example didn't so it wouldn't matter whether they had the right this). For instance:
class Test {
constructor() {
this.prop1 = {
prop2: {
prop3: {
getAll: () => {
return "Salutes";
},
getAll2() { // Also works
return "Salutes";
},
getAll3: function() { // Also works
return "Salutes";
}
}
}
}
}
}
const myTest = new Test();
const result = myTest.prop1.prop2.prop3.getAll();
console.log(result);
console.log(myTest.prop1.prop2.prop3.getAll2());
console.log(myTest.prop1.prop2.prop3.getAll3());
Example of using information on the Test instance (it's the same except for the constructor parameter and message property):
class Test {
constructor(message) {
this.message = message;
this.prop1 = {
prop2: {
prop3: {
getAll: () => {
// `this` refers to the `Test` instance because
// this is an arrow function
return this.message;
}
// `getAll2` and `getAll3` wouldn't work because
// they would get `this` based on how they're called
// (it would be the `this.prop1.prop2.prop3` object).
}
}
}
}
}
// I make an instance of a class
const myTest = new Test("Salutes encore!");
// I access a method in this way
const result = myTest.prop1.prop2.prop3.getAll();
console.log(result);
If you want to achieve it without modifying the Test2 class, you can use Proxy class and define custom handler for get method:
class Test2 {
constructor(){}
getAll(){
return 'Salutes'
}
}
let instance = new Test2();
const handler = {
get: function(target, prop, receiver) {
if (['prop1', 'prop2', 'prop3'].includes(prop)) {
return receiver;
}
return Reflect.get(...arguments);
}
};
const proxy = new Proxy(instance, handler);
console.log(proxy.prop1.prop2.prop3.getAll());
console.log(proxy.prop7);
console.log(proxy.getAll());

How to inject custom function to class in typescript

I would like to inject a custom function into my class from variable functions, how can I do this?
My code is:
const functions = {
foo: () => console.log('foo')
}
class ParentBase {
parentHello() {
console.log('parentHello')
}
}
function Base() {
class BaseClass extends ParentBase { }
BaseClass.foo = functions['foo']
return BaseClass;
}
const Class = Base();
const instance = new Class();
instance.parentHello() // it work
instance.foo() // foo is not a function
You could directly declare a new method in the class declaration:
class BaseClass extends ParentBase {
foo() {functions["foo"](); }
}
If the method name should be dynamic, you can also use computed method names:
class BaseClass extends ParentBase {
[window.location]() {functions["foo"](); }
}
If you really want to "dynamically inject" a new method (e.g. conditionally, in a loop, or some other strange construct), you'd have to write into BaseClass.prototype, which is the object BaseClass instances will inherit from:
BaseClass.prototype.foo = functions["foo"];
This will however be an enumerable property, to create a non enumerable one, use Object.defineProperty:
Object.defineProperty(BaseClass.prototype, "foo", {
value: functions["foo"],
/* enumerable: false,
writable: false */
});
Here is a small showcase:
class ParentBase {
parentHello() {
console.log('parentHello')
}
}
function Base() {
class BaseClass extends ParentBase {
instanceMethod() { console.log("BaseClass.prototoype.test", this); };
static classMethod() { console.log("BaseClass.classMethod", this); };
}
BaseClass.classMethod2 = function() { console.log("BaseClass.classMethod2", this); };
BaseClass.prototype.instanceMethod2 = function() { console.log("BaseClass.prototype.instanceMethod2", this); };
Object.defineProperty(BaseClass.prototype, "instanceMethod3", { value() { console.log("BaseClass.prototype.instanceMethod3", this); } });
return BaseClass;
}
const Class = Base();
const instance = new Class();
Class.classMethod();
Class.classMethod2();
instance.instanceMethod();
instance.instanceMethod2();
instance.instanceMethod3();
You have 2 places to access properties:
Instance based
Class Based
What you have is adding to class but you are trying to access using instance.
What you are trying to achieve is called Mixins. These are partial utility functions list that can be shared across multiple classes.
To inject properties to instances, you can either use constructor based approach or you can inject them to prototype
class ParentBase {
parentHello() {
console.log('parentHello')
}
}
const mixin = {
foo: function() { console.log('foo') },
bar: function() { console.log('Bar') },
fooBar: function() { console.log('foo-bar') }
}
function Base() {
class BaseClass extends ParentBase {}
Object.assign(BaseClass.prototype, mixin)
return BaseClass;
}
const Class = Base();
const instance = new Class();
instance.parentHello() // it work
instance.foo() // foo is not a function
instance.fooBar()
You should also know that functions/ properties added on prototype are shared across instances. So its advice to have function but not variables as it will introduce side-effect

is there any chance to get the outer scope in Proxy?

I am Proxing an object in a custom class and I'd like to get access to the methods and properties of that same class within my Proxy Object. Is it possible?
I thought there is a way to bind the context but it didn't work for me neight with apply, call nor bind.
Any suggestions would be appreciated!
class MyClass {
constructor() {
this.state = new Proxy(this.initialState, {
set(target, prop, value) {
// some logic goes here
}
})
}
methodIneedToReach() {}
}
I need it to structure the code and prevent the mess.
Either store the value of this in a variable called that for example and use that.methodIneedToReach inside the set method, or, better yet, use an arrow function for set. Since arrow functions don't have their own this keyword, they will use the surrounding one which is, in this case, the instance of your class:
class MyClass {
constructor() {
this.state = new Proxy(this.initialState, {
set: (target, prop, value) => { // set is an arrow function
this.methodIneedToReach(); // works because 'this' inside here points to your class
}
})
}
methodIneedToReach() {}
}
Demo:
class MyClass {
constructor() {
this.initialState = { message: "Hello World!" };
this.state = new Proxy(this.initialState, {
set: (target, prop, value) => {
this.methodIneedToReach();
}
})
}
methodIneedToReach(value) {
console.log("'methodIneedToReach' is called");
}
}
let inst = new MyClass();
inst.state.message = "Bye world!";

How to call a function within a class from another file

I'm trying to figure out how to accomplish the following task. I want to be able to call a function that is within a class in another file:
file1
export class Something {
constructor() {
...
}
myFunction = () => {
...
}
}
file2
import { Something } from 'file1';
export function theFunction() {
if (condition met) {
Something.myFunction(...) // The myFunction is saying it's not a function
}
}
class Canonical { /* in ES5 */ function Canonical() {}
myMethod() { ... } Canonical.prototype.myMethod = function() { ... };
}
You can call that like this:
Canonical.prototype.myMethod();
// Or, to call it as a method on anObject
Canonical.prototype.myMethod.call(anObject);
What you have created however is not a method, but a property created on each individual instance, that happens to be a function:
class Unusual { /* in ES5 */ function Unusual() {
myFunction = () => { ... }; this.myFunction = function() { ... };
} }
It only exists on instances and therefore you have to create one to call it:
new Unusual().myFunction();
However I can't recommend this way of defining a "method", unless you specifically need it to be pre-bound. That is useful in React.Component classes, but now with React hooks that use case is waning.
class Test {
constructor() { this.a = '🍏'; }
myMethod() { console.log(this.a); }
myFunction = () => console.log(this.a);
}
const methodReference = new Test().myMethod;
try {
methodReference(); /* This will not work.
This is where an instance property can be advantageous */
} catch (ex) { console.error(ex.message); }
const functionReference = new Test().myFunction;
functionReference();

Categories