How to directly execute the inheritance chain's base class in Javascript (OOP)?
P粉718165540
P粉718165540 2024-01-10 17:20:07
0
1
411

I'm working on an inheritance chain of 3 extendable classes.

Renderer (base) -> Mailbox (child) -> MailboxInbox (final child)

Each of them has its own render method. Children just override the Renderer (Base) render method and finally they should anyway use the render method of the Renderer(Base< /strong>) class . My problem is that when I execute certain methods in the Renderer class (createElementWithTextInside), it does not fully execute the Renderer's render method (Renderer . render()), but it starts at the end of the extensible class chain. For example, it starts traversing:

1 - MailboxInbox.render

2 - Mailbox.render

3 - Renderer.render

I understand how js classes/inheritance work behind the scenes. Javascript just extends the object prototypes and the script will iterate over them etc. But my question is: How can I avoid this behavior and directly call the Renderer.render method in its method when needed?

I can't save __self in the renderer because it would point to this of the current instance (MailboxInbox) anyway. Also, I can't use .bind/lambda (arrow function) because I need to save the context

violin

class Renderer {
    
      get noResultContent() {
        const p = this.createElement('p', 'no-result-msg');
        const textNode = document.createTextNode('No result');
        p.appendChild(textNode);
    
        return p;
      };
    
    
    
      createElement(tag, className) {
        if (!tag) throw new Error('Tag must be passed!');
    
        const res = document.createElement(tag);
        res.classList.add(className);
        return res;
      }
      
      createTextNode(text) {
        const res = document.createTextNode(text);
        return res;
      }
    
      /**
       * automatically creates an el based on tag, text node and insert the text node into the el
       */
      createElementWithTextInside(tag, className, content) {
    
        const el = this.createElement(tag, className);
        const text = this.createTextNode(content);
        this.render(el, text);
    
        return el;
      }
      
      checkIfDomEl = el => el instanceof Element;
    
    
      render(el, content) {

        console.log('3: RENDERER RENDER')
        if (!this.checkIfDomEl(el)) throw new Error(`Please, pass the valid DOM el. Received: ${el}, ${typeof el} `);
    
        const finalContent = content || this.noResultContent;
        
        el.appendChild(finalContent);
      }
    
    }
    --------
    class Mailbox extends Renderer {
      rootEl;
    
      
    
      constructor(selector) {
        super();
        this.rootEl = document.querySelector(selector);
      }
    
      renderTitle(content) {
        if (!content) return null;
    
        const titleEl = super.createElementWithTextInside('h3', 'mailbox-title', content)
    
        super.render(this.rootEl, titleEl);
    
      }
    
      render() {
        console.log('2: MAILBOX RENDER')
        super.render(this.rootEl);
      }
    }
    --------
    class MailboxInbox extends Mailbox {
    
      title = "Inbox"
    
      constructor(params) {
       const { selector } = params;
       super(selector); 
      }
    
      renderTitle() {
        super.renderTitle(this.title);
      }
    
      render() {
        console.log('1: INBOX RENDER')
        super.render();
      }
    }
--------
const inbox = new MailboxInbox({selector: '#container'}); 
inbox.renderTitle()

Here it just renders in the console:

1: Inbox Rendering

2: Mailbox rendering

3: Renderer rendering

thanks for your help! kind regards!

renew

Basically I just want to have a basic render class that can accept parameters: (el,content) (content is optional) and Childreni Wanted to override it with some predefined el and content etc in their own .render() method, but if I try to execute renderer The .render() method comes from inside Renderer and it goes through the whole chain and my parameters are missing because in MailboxInbox the render method currently doesn't accept any parameters, So I should either have it accept parameters and pass them throughout the chain, or just define some dedicated class like baseRender in Renderer and call it directly< /p>

P粉718165540
P粉718165540

reply all(1)
P粉618358260

Technically you can replace this.render(el, text); with

Renderer.prototype.render.call(this, el, text);

It bypasses inherited property lookups. However, this is generally not a good practice and will lose the advantages of class inheritance.

Fortunately, you've identified the actual problem in your update:

This does incorrectly override a method with an incompatible signature, violating the Liskov Substitution Principle .

You have also identified potential solutions:

Both are good. In the latter, I recommend not naming the methods render and baseRender, but instead recommend something like renderContent(el, content) and renderDefault (el) , their signatures are actually different - and both can be overridden. However, looking at the implementation of render when called with two parameters, it seems to me that it doesn't actually do anything useful other than calling el.appendChild(content) thing, so I'd rather abandon it entirely and just call appendChild (unless you need the ability to override specific behavior, such as by doing el.prepend(content) instead).

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template