0

Assume we have some classes A and B:

Class A {
    constructor(a) {
       this.a = a;
    };
    showInfo() {
        console.log(this.a)
    };
};

Class B {
    constructor(b) {
        this.b = b;
    };
    printText() {
        console.log('its B!');
    };
};

Then we create an instance of B like this:

const objB = new B(
    new A(3)
);
  1. So now we have objB with its own method inside - printText, and we surely can call it. But what if i want somehow when calling not existing method in objB to make it pass through to encapsulated A class in there and look for invoking this method on him, like this: objB.showInfo() - to give me 3 here ?

  2. Same story, but at this time i want when calling not existing method on A to make it pass through to B outside (like that printText)?

P.S. Don't wanna use super() and inheritance, just composition and wrapping objects, hope you've got the point.

  • 1
    For context-- _why_ don't you want to use inheritance? I'm guessing this can be emulated without it, but I'm struggling to understand what purpose it could serve to use composition instead... – Alexander Nied Jul 17 '22 at 21:02
  • Understand you, @AlexanderNied, but it's my personal view - not to drag behind unnecessary bunch of code for duplication and binding Parent & Child classes. In this case neither A nor B shouldn't know about each other inner implementation, until a pass through call is appeared. – Gadzhiev Islam Jul 17 '22 at 21:25
  • And because my whole point here is to make such a veil-object around another to sort of achieve a clean transit method call. – Gadzhiev Islam Jul 17 '22 at 21:28
  • @MyNameIsNeXTSTEP ... 1/2 ... What the OP's example code demonstrates is called aggregation; an instance of `A` gets assigned as an own property's value to any instance of `B`. Thus working with instances of the latter (`B`), one also has to know the implementation of the former (`A`). – Peter Seliger Jul 26 '22 at 15:09
  • @MyNameIsNeXTSTEP ... 2/2 ... This is not a problem at all regarding the OP's 1st use case where the OP wants to achieve forwarding of `objB.showInfo()` to actually `objB.b.showInfo()` since one could argue that one somehow deals with a parent-child relationship where the parent-aggregate is well aware of the aggregated child. In my opinion it is more difficult to find arguments in favor of the OP's 2nd use case where the implementation of `class A` has no connection / relation at all to `class B`. – Peter Seliger Jul 26 '22 at 15:09
  • @PeterSeliger: I think you might have misunderstood something. The OP's first *and* second case both are very similar. It just reverses which class is containing the other. And since both classes A and B have practically the same data, just different variable/functions names. What the OP wants in their first case is A to be passed to B, and in case 2 the reverse is wanted - B is passed to A. What they want to accomplish is the ability to access the inner properties without a formal wrapping (which might be difficult to accomplish if they use multiple unrelated classes). – Lakshya Raj Jul 26 '22 at 17:26
  • @LakshyaRaj … I think I perfectly understood the OP‘s use cases and my critique does target exactly that. – Peter Seliger Jul 26 '22 at 18:03

1 Answers1

0

Just a little warning at the start: this might make your program harder to debug, and it also might be a little complicated for something like this. As others have suggested, you should probably investigate other options which may be simpler and also less in the way of everything else your code does.

Here's the code which provides the functionality:

function makeGetProxy(t){
    return new Proxy(t, {
        get(obj,prop){
            if(prop in t){
                return t[prop];
            }else{
                var keys = Object.keys(obj);
                for (var i = 0; i < keys.length; i++) {
                    var val = t[keys[i]];
                    if(prop in val){
                        return val[prop];
                        // what about a recursive function?
                    }
                }
                return undefined;
            }
        }
    });
}

And one itty bitty change to your constructor in B:

class B {
    constructor(b) {
        this.b = b;
        return makeGetProxy(this);
    };
    printText() {
        console.log('its B!');
    };
};

If you want, you can also do the same to A.

Let's slow down. What just happened? I'll explain.

  1. Since the properties we might request don't already exist, we're going to have to use a getter (see resources) to properly send back the value required. But, since we don't know the property names, we need a Proxy (see resources) to have a "catch-all" kind of get method.

  2. The proxy will check if the property requested prop already exists, and if so, returns it. If it doesn't exist, it checks all of your properties' properties (all of the sub-properties).

  3. The first result it gets, it returns it. This might cause unexpected bugs in your program. If it doesn't find it, it simply returns undefined.

  4. Then, the proxy is generalized into a function for reuse in multiple classes (as you requested).

So, this can get the properties of a class and the properties of a class' properties, but if you need to go further (with your C class that doesn't exist yet), you can use a recursive function. I currently don't have the implementation for that recursive function, but in my head it would comprise mostly of a modified version of the else block in the makeGetProxy function.

Again, be careful with this code. It might get in the way of other things and cause unnecessary difficulty in debugging.

Resources:

Lakshya Raj
  • 1,669
  • 3
  • 10
  • 34
  • Yeah, seems to me these Proxies and Prototyping chain are only "optimal" ways to do it so, thanx. – Gadzhiev Islam Jul 17 '22 at 22:42
  • @MyNameIsNeXTSTEP: You're welcome! I'll update my answer with a recursive function once I can implement it. :) – Lakshya Raj Jul 17 '22 at 22:44
  • @MyNameIsNeXTSTEP ... This is an example of how it should not be done. Instead of a clear / readable / maintainable modeling one hides the real intention behind a complex meta layer of poorly implemented glue code. The OP was better of providing example code which is closer to the OP's real problem in order to get better fitting approaches / advices. – Peter Seliger Jul 26 '22 at 15:20
  • @PeterSeliger … Hi, i have been pointed it out that's what i wanted to see in answers doesn't correlate to exactly following it in my use. Just was interested in discovering somehow. Therefore i took some goods and bads of it here, rethinking it. About providing example code of real problem - all needed code is in the description. And what i wanted from one use cases is to create at the project such a veil-object around another (as i mentioned in another comment below) to use cached layer of inner object and passes though himself method calls of new actions. – Gadzhiev Islam Jul 26 '22 at 18:58
  • The second use case is more about research, although when i wrote it - just realized then, it was strange a bit) – Gadzhiev Islam Jul 26 '22 at 19:01
  • About second case a thought more like: assume inner A object calls inside himself a method, and if something was not given to him as args or flag, he comes to outer B and asking for help. B at that time finds that method on him and calls it… – Gadzhiev Islam Jul 26 '22 at 19:06
  • @MyNameIsNeXTSTEP ... What you just described ... _"[inner] `A` object calls inside himself a method, and if something was not given to him as args or flag, he comes to [outer] `B` and asking for help"_ ... is one of JavaScript's core features; it's called prototypal inheritance (a built-in automatic delegation process). Besides of tinkering with the language for research and self learning reasons why not using what the language core comfortably provides? – Peter Seliger Jul 27 '22 at 08:58
  • @PeterSeliger Of course it is, i wrote at the beginning of this commenting on this ticket that quote, but deleted then for the sake of answers. See up here my comment: **"Yeah, seems to me these Proxies and Prototyping chain are only "optimal" ways to do it so"**. And second, maybe i missed something, then please show me the code snippet, but in my case its not an inheritance for ```A``` & ```B```, its object composition and therefore the language itself can't provide control transfer directly to ```B``` by default. – Gadzhiev Islam Jul 27 '22 at 09:24