I have an existing code with multiple JS files. Each file adds new property with object literal onto global namespace. It seems bunch of Java developers worked on this project looking at the pattern where you would end up with a method foo in App.product.foo()
Current (as is) code:
// product.js
var app = app || {};
app.product = {
changeColor = function(e) {};
clickEventHandler = function() {
app.cart.addToCart(e.target.dataset['code']);
};
}
// cart.js
var app = app || {};
app.cart = {
addToCart = function(productCode) {}
}
Can I convert this into ES6 modules pattern so that I can export / import required code across modules?
// product.js
import { addToCart } from './cart';
export default {
changeColor = function(e) {};
clickEventHandler = function() {
addToCart(e.target.dataset['code']);
};
}
Related
I am creating an application by TypeScript and using WebPack to transpile and bundle.
I am using the final result of this application in a pure JavaScript website. In this application I have defined an enum as below:
export const enum ShapeType {
Actor,Ellipse,Border,Connector
}
I also exported it as:
export { ShapeType } from "./shape/shape.type";
But when I try to use it like:
var createdShape = new shapeFactory.createShape(ShapeType.Ellipse);
It does not create a shape and when I debug I see this error:"ShapeType is not defined"
I also try to find ShapeType in the final JavaScript bundle file, but I found out there is no ShapeType in the bundle file too.
I don't have a problem when I import it inside TS.
The code below is the js code. ChartDraw is the library name I defined in my webpack config file. All other functions work fine. The only problem is with
var aa = shapeFactory.createShape(ChartDraw.ShapeType.Ellipse);
because ShapeType is not defined.
var svg = new ChartDraw.Svg("drawing");
var shapeFactory = new ChartDraw.ShapeFactory(svg);
var ob1 = null;
var ob2 = null;
//aa.draw();
var cc = new ChartDraw.Connector(svg);
var bb = new ChartDraw.MouseReader();
bb.setExportFunction(info => {
var aa = shapeFactory.createShape(ChartDraw.ShapeType.Ellipse);
aa.rectangularPosition = info;
aa.draw();
if (ob1 == null)
ob1 = aa;
else {
ob2 = info;
cc.beginObject = ob1;
cc.endObject = aa;
cc.draw();
}
});
And the code below is where I import ShapeType:
import { ShapeType } from "./shape.type";
import { Actor } from "./actor";
import { Svg } from "../svg";
import { Shape } from "./shape";
import { Ellipse } from "./ellipse";
export class ShapeFactory {
private svg: Svg;
constructor(svg: Svg) {
this.svg = svg;
}
public createShape(shape: ShapeType):Shape {
switch (shape) {
case ShapeType.Actor:
let actor = new Actor(this.svg);
return actor;
case ShapeType.Ellipse:
let ell = new Ellipse(this.svg);
return ell;
}
}
}
Eventually, I found out what is the problem.
The first problem was because of the const. When typescript code is converting to JavaScript, an enum converts to an object, but if I add the const keyword it is not an object anymore. So to fix it I removed the const as below:
export enum ShapeType {
Actor,Ellipse,Border,Connector
}
The second issue was in my JavaScript code. At first step, I forgot to add library name(ChartDraw) to the ShapeType when calling shapeFactory.createShape(ChartDraw.ShapeType.Ellipse)
I have a file called helpers.js in the 'helpers' folder. The contents are like below:
class Helpers {
constructor(config) {
if (this._singleton) {
throw new Error('A singleton has already been created.');
}
this._singleton = this;
}
/**
* Gets the singleton object.
* #returns {Helpers}
*/
static getSingleton() {
return this._singleton;
}
}
module.exports = Helpers;
Then in /helpers/user.js I want to get the helper's singleton instance.
This is my code:
const helpers = require('../helpers').getSingleton();
or
const Helpers = require('../helpers');
const helpers = Helpers.getSingleton();
The error I keep getting is:
TypeError: require(...).getSingleton is not a function
or
TypeError: Helpers.getSingleton is not a function
If I hover over Helpers in VSCode, I get this tooltip
And, whenever I hover over getSingleton() I get this tooltip:
So the path is correct, but it still gives me the errors.
The easiest way to implement the singleton pattern in JavaScript is to just not export the class at all, e.g.
class Helpers {}
let helper;
module.exports = function() {
if (!helper) helpers = new Helpers();
return helper;
};
// loaded with
var helpers = require('../helpers')(); // note the extra () to call it
or even better, since we aren't restricted to Java-like behavior, just skip the function entirely and do
class Helpers {}
module.exports = new Helpers();
// loaded with
var helpers = require('../helpers');
but then if all your module is exporting is a single instance of a class, there's very little reason to use a class in the first place. You might as well do
exports.helperMethodOne = function(){};
exports.helperMethodTwo = function(){};
exports.helperMethodThree = function(){};
// loaded with
var helpers = require('../helpers');
or
module.exports = {
helperMethodOne() {},
helperMethodTwo() {},
helperMethodThree() {},
};
// loaded with
var helpers = require('../helpers');
Your require statement is wrong, but its hard to tell you precisely the right syntax without knowing your environment.
const config = require('/path/to/file');
Is typical. So try:
const Helpers = require('../helpers');
You wrote '../helpers.js' in your screenshot, not '../helpers'
You get the error:
TypeError: require(...).getSingleton is not a function
Because require(...) resolves to something else, like null, and null.getSingleton is not a function.
Also, you cannot reference this meaninfully inside a static context. this ought to only be used for class instances, not static members.
You can do something like this to use it as Singleton.getInstance();
class Singleton {
static instance = new Singleton();
static getInstance = () => Singleton.instance;
constructor() {
throw new Error('Use Singleton.getInstance()');
}
}
module.exports = Singleton;
or even something more sneaky and use it as new Singleton()
class Singleton {
static instance;
constructor() {
if (!Singleton.instance) {
Singleton.instance = this;
}
return Singleton.instance;
}
}
module.exports = Singleton;
I've created an app object which contains an empty modules object within it defined in the constructor.
var App = function() {
this.modules = {};
};
The modules object contains references to other objects (modules) that make up the app and contain their logic.
I instantiate the app like this
window.app = new App();
app.createModules();
createModules() fills the modules object with the modules of the app. Then when it is done, it runs initModules() that calls each modules init() function. In this way I hope to be able to control the execution order so that objects dependent on other objects will load correctly. The code for this is as follows:
App.prototype.createModules = function() {
console.log("Creating Modules");
this.modules = {
radio: new Radio(),
visualiser: new Visualiser()
};
this.initModules(this.modules);
}
App.prototype.initModules = function() {
console.log("Initialising modules");
for (var key in this.modules) {
if (this.modules.hasOwnProperty(key)) {
var obj = this.modules[key];
console.log(obj);
console.log(obj.init); //logs 'undefined'
}
}
}
Currently the console log on obj.init is returning undefined, meaning that I can't run the init code for that object. But when the page is finished loading if I call
app.modules.radio.init
It returns the function. I can't seem to figure out why is this happening.
Cheers!
EDIT
Here is the structure of app:
var app = function() {
...
}
window.app = new App();
app.createModules();
function Radio() {
...
}
Radio.prototype.init = function() {
...
}
function Visualiser() {
...
}
Visualiser.prototype.init = function() {
...
}
//Event code
Codepen: https://codepen.io/anon/pen/EvggLZ?editors=0011
Here is the structure of app
Yes, that calls the methods before they are created on the prototypes. Move the
window.app = new App();
app.createModules();
to the bottom of the script and it will work.
I've read a few pages on extending a module.They revolve around using a functional form of a module and I get how to do it (from https://toddmotto.com/mastering-the-module-pattern/)
var Module = (function () {
return {
publicMethod: function () {
// code
}
};
})();
but what I have is two modules like this
util.js
module.exports = {
thing1: function() {// do thing1 stuff }
}
extend.js a package I can't change (from npm)
module.exports = {
thing2: function() {// do thing2 one stuff}
}
now pretending I am going to use my util.js module
const _ = require('util.js);
let catin = _.thing1; // that's easy
let thehat = _.thing2;. // this was util.js extended.
I could in util.js just do this.
const ex = require('extend.js')
module.exports = {
thing1: function() {// do thing1 stuff }
thing2: ex.thing2
}
and that's ok since extend.js only has one function/method to extend, but I would like to extend this into my util library https://github.com/dodekeract/bitwise/blob/master/index.js but it has 22! items to extend.
There must be a better slicker way yes?
I'm open to refactoring my util.js file (but not hand coding each extension like I showed) so it extends automatically but obviously can't refactor that package I'm not maintaining, short of a fork...ugh. Also not interested in adding a sub deal like
ex: ex
_.ex.thing2
Ideas?
So given Molda's hint I'll share what I put together to make this question more useful for others. I put together a simple way of building a (utility) module from a folder of (utility) modules plus other one off packages (e.g. bitwise)
Make a utils.js module in say lib/ with this (you'll need require-all or some such package)
let utils = require('require-all')(__dirname + '/util');
let bw = require('bitwise');
let self = module.exports = (function(){
let util={};
for (var key in utils) {
util = utils.object.merge_keys(util,utils[key])
}
util = utils.object.merge_keys(util,bw)
return util;
}());
now make a subdirectory lib/util/ and fill it with your utility modules. Have one of those modules contain this key/function
merge_keys: function (obj1,obj2){
var obj3 = {};
for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
return obj3;
}
and be sure that module name matches the key used in this line util = utils.object.merge_keys(util,utils[key]). In my case I have a module object.js in lib/util/ containing merge_keys
Then just require the utils.js module and all will be as one including access to the merge_keys function for other merging :-).
let _ = require('./lib/utils');
// see if it worked
console.log(_);
beware: there is no checking for duplicate key names between modules
notes:
let self= allows one to refer to any other key within the merged object itself like self.myfunctionkeyname( )
This question already has an answer here:
"Uncaught ReferenceError: this is not defined" in class constructor
(1 answer)
Closed 6 years ago.
I'm trying to write a fake downloader which basically sleeps for certain interval and emits a 'downloaded' event. I also want to have my own variable that tracks the number of events emitted.
This piece of code throws the following error:
ReferenceError: this is not defined
Code:
'use strict';
const EventEmitter = require('events');
class Downloader extends EventEmitter{
constructor(){
this.totalEmitted = 0;
}
download(delaySecs){
setTimeout(() => {
this.emit('downloaded',delaySecs);
this.totalEmitted ++;
},delaySecs*1000)
}
}
module.exports = Downloader;
where as, if I comment this.totalEmitted, it works fine.
UPDATE:
When I completely eliminate the constructor it started working fine.
And even using an empty constructor is causing the this Reference error.
I'm not sure if the constructor is yet supported in node 4.4.5.
Work around:
'use strict';
const EventEmitter = require('events');
class Downloader extends EventEmitter{
download(delaySecs){
setTimeout(() => {
if(this.totalEmitted == undefined) this.totalEmitted = 0;
this.totalEmitted ++;
this.emit('downloaded',delaySecs,this.totalEmitted);
},delaySecs*1000)
}
}
module.exports = Downloader;
If it doesn't bother you so much switching from ES6 Class to functions, then you could try:
'use strict';
const util = require('util');
const EventEmitter = require('events');
function Downloader() {
var self = this;
self.totalEmitted = 0;
this.on('downloaded', function() {
self.totalEmitted ++;
console.log(self.totalEmitted);
});
}
util.inherits(Downloader, EventEmitter);
Downloader.prototype.download = function(delaySecs) {
setTimeout(() => {
this.emit('downloaded', delaySecs);
}, delaySecs*1000);
};
var downloader = new Downloader();
downloader.download(1);
module.exports = Downloader;