Search code examples
ecmascript-6gulpbabeljses6-classrollupjs

How to add methods from a class to an existing object of another class


I had one big class that I split in two and load them separately on a page to improve performance. Core part is loaded first synchronously as it contains critical functionality while Extension (non-critical functionality) loads later in the page asynchronously.

I want only one object which contains functionality of both classes. But by the time Extension loads, there's already an object of Core. How do I add functionality from Extension onto the object of Core?

I'm using a Gulp based asset pipeline with

Rollup = to bundle the JS from different files into one file

Babel = to transpile ES6 to ES5

Uglify = to minimize the JS

Here's my directory structure:

js
|_ _classes
|   |_ ab.js
|   |_ cd.js
|_ core.js
|_ ext.js

I have set the gulp build task to ignore files in "_classes" directory. Rollup parses the "import" statements to bundle the code.

This is what I have in core.js

//core.js
import AB from './_classes/ab.js';

window.oab = new AB();

and this is ext.js

//ext.js
import CD from './_classes/cd.js';

//let o_cd = new CD();

window.oab.prototype = CD;

This is the Core class

// file ab.js
class AB {

    constructor() {
        this.car = 'McLaren';
        this.fruit = 'Mango';
    }

    getCar() {
        return this.car;
    }

    getFruit() {
        return this.fruit;
    }

}

and this is the Extension class

//file cd.js
class CD {

    constructor() {
        this.plane = 'Gulfstream';
    }

    getPlane() {
        return this.plane;
    }

}

I'm trying to get this to work:

console.log( window.oab.getCar() );  // prints McLaren
console.log( window.oab.getFruit() );  // prints Mango
console.log( window.oab.getPlane() );  // prints Gulfstream

Now I can very well import the AB class in CD class, set CD class to extend AB and that will give me what I want. But with my current gulp pipeline setup, that would mean that Rollup would bundle a copy of class AB with class CD as well and class AB has already loaded earlier.

Due to Rollup, Babel & Uglify, the class names AB & CD etc don't persist, so I cannot assume AB being available in CD for me to extend without importing it first and importing it would mean it being bundled with CD.


Solution

  • I applied a monkey patch in ext.js on window.oab and this gives me what I'm looking for.

    import CD from './_classes/cd.js';
    
    ( function() {
    
        let i;
        let ocd = new CD();
        let ocd_methods = Object.getOwnPropertyNames( ocd.__proto__ ).filter( p => {
            return ( 'function' === typeof ocd.__proto__[ p ] );
        });
    
        Object.assign( window.oab, ocd );   // this assigns only properties of ocd into window.oab and not the methods
    
        for ( i in ocd_methods ) {
    
            let method = ocd_methods[ i ];
    
            if ( '__proto__' === method || 'constructor' === method ) {
                continue;
            }
    
            window.oab.__proto__[ method ] = ocd[ method ];
    
        }
    
    } () );
    

    And now this works

    console.log( window.oab.getCar() );  // prints McLaren
    console.log( window.oab.getFruit() );  // prints Mango
    console.log( window.oab.getPlane() );  // prints Gulfstream