Default export new instance of class - javascript

Will the snippets below produce new instance every time it's imported?
// 1st implementation
class ConnectionManager {
...
}
export default new ConnectionManager();
// 2nd implementation
class ConnectionManager {
...
}
const connectionManager = new ConnectionManager();
export default connectionManager;
If yes, how can I get the same instance in every import?

ES6 modules follow single instance pattern. That is, the instance is created when the module is loaded.
Here is an article about it.
// File: yolo.js
class Yolo {}
export let yolo = new Yolo();
// File: laser.js
import { yolo } from "./yolo.js";
// yolo is a single instance of Yolo class
// File: cat.js
import { yolo } from "./yolo.js";
// same yolo as in laster.js

It should be the same.
The following example uses both the implementations, imports them into 2 different files, and imports them all into single index file. Everytime an instance is created, we generate a random value for the class, and log its creation.
// ConnectionManagerImpl1.ts
class ConnectionManagerImpl1 {
public value;
constructor() {
this.value = Math.random().toString(36).substring(7);
console.log(`New ConnectionManagerImpl1 instance created: ${this.value}`)
}
}
export default new ConnectionManagerImpl1();
// ConnectionManagerImpl2.ts
class ConnectionManagerImpl2 {
public value;
constructor() {
this.value = Math.random().toString(36).substring(7);
console.log(`New ConnectionManagerImpl2 instance created: ${this.value}`)
}
}
const connectionManagerImpl2 = new ConnectionManagerImpl2();
export default connectionManagerImpl2;
// import1.ts
import connectionManagerImpl1 from './ConnectionManagerImpl1';
import connectionManagerImpl2 from './ConnectionManagerImpl2';
export { connectionManagerImpl1, connectionManagerImpl2 };
// import2.ts
import connectionManagerImpl1 from './ConnectionManagerImpl1';
import connectionManagerImpl2 from './ConnectionManagerImpl2';
export { connectionManagerImpl1, connectionManagerImpl2 };
// index.ts
import * as import1 from './import1';
import * as import2 from './import2';
console.log(import1)
console.log(import2)
console.log("Done")
Ran the above setup using tsc --module 'commonjs' * && node index.js
Output:
New ConnectionManagerImpl1 instance created: ddt3re
New ConnectionManagerImpl2 instance created: uv5z6
{ connectionManagerImpl1: ConnectionManagerImpl1 { value: 'ddt3re' },
connectionManagerImpl2: ConnectionManagerImpl2 { value: 'uv5z6' } }
{ connectionManagerImpl1: ConnectionManagerImpl1 { value: 'ddt3re' },
connectionManagerImpl2: ConnectionManagerImpl2 { value: 'uv5z6' } }
Done
As you can see, only 1 instance of ConnectionManagerImpl1 and ConnectionManagerImpl2 were created. So, both the implementation should create only 1 instance.

The export statement is used when creating JavaScript modules to export functions, objects, or primitive values from the module so they can be used by other programs with the import statement.
There are two different types of export, named and default. You can have multiple named exports per module but only one default export.
export default class ConnectionManager { .. }
Or
class ConnectionManager {
...
}
export default connectionManager;

Related

Webpack ES6 modules multiple class app organization

I'm building an app with webpack for the first time and i'm trying to wrap my head around organizing class files. I'm having trouble getting the code to work. I'm still new to ES6 and such, so my code below is probably very wrong but i'm not sure its my approach/concept or its my syntax.
entry is index.js and I also have these files
import App from './js/app.js';
import User from './js/user.js';
import Guest from './js/guest.js';
const app = new App();
const user = new User();
const guest = new Guest();
$(document).ready(function () {
app.DatabaseStore.senddata();
console.log( user.getall() );
});
src/js/app.js the main global method/variable class
import CookieStore from './cookie.js';
import DatabaseStore from './database.js';
export default class App {
constructor() {
this.cookieStore = new CookieStore();
this.databaseStore = new DatabaseStore();
}
gettime() {
return 'time';
}
}
src/js/user.js methods that are for users
import App from './app.js';
export default class User extends App {
constructor() {
this.mydata = App.cookieStore.getData();
console.log(this.mydata );
}
getall() {
return ['foo', 'bar', 'baz'];
}
}
src/js/guest.js methods that are for guests
import App from './app.js';
export default class Guest extends App {
constructor() {
this.mydata = App.cookieStore.getData();
}
}
src/js/cookie.js cookie manipulating methods
export default class CookieStore {
constructor() {}
mydata() {return 'foo';}
}
src/js/database.js firebase methods
export default class DatabaseStore {
constructor() {}
senddata() {
this.mydata = App.cookieStore.getData();
}
You are trying to access instance property statically. You need to create an instance of App class before trying to access cookieStore property. You can create an instance and export it in your app.js to have singleton instance.
//in your app.js
export const app = new App();
in other files
import {app} from './js/app.js'
app.cookieStore.getData();

Webpack: reference bundled components from outside bundle

I have a class inside my bundled app that I want to allow users of the app to extend.
Here's what the definition of the bundled class looks like:
import * as d3 from 'd3';
class VizPlugin {
constructor(options) {
this.options = options;
}
build() {
}
...
}
export default VizPlugin;
The application is a full client/server nodejs app, installed on a customers server. After the app is bundled/deployed/installed on the server, I want the customer to be able to extend the app and add their own custom modules as extensions/plugins, like this:
import VizPlugin from './js/viz-plugin'; //there is no viz-plugin.js because it's bundled
class ScatterPlot extends VizPlugin {
constructor(options) {
super(options);
}
build() {
//I'd like to also use the reference to d3 from VizPlugin here
}
...
}
export default ScatterPlot;
They would put their javascript code in a directory along side the other bundled client javascript and import from that. Basically, there needs to be a named file called "viz-plugin.js" that can be imported from "scatter-plot.js".
Add d3 to your class, and give the extender some way of using it:
import * as d3 from 'd3';
class VizPlugin {
constructor(options) {
this.options = options;
this.d3 = d3;
}
useD3 (callback) {
callback(this, this.d3);
}
build() {
}
...
}
module.exports = VizPlugin as VizPlugin;
You can use the webpack SplitChunksPlugin and give names to individual modules by using splitChunks.name.
The following code will look for the viz-plugin.js module and tell it to keep it's name:
optimization: {
splitChunks: {
name(module, chunks, cacheGroupKey) {
const moduleId = module.identifier();
if( moduleId && moduleId.indexOf('viz-plugin.js') >= 0 ) {
return 'viz-plugin'
}
return null;
}
}
},
Now, in the dist/build output, there will be a file called "viz-plugin.js" that you can extend, as such:
import VizPlugin from './dist/js/viz-plugin';
class ScatterPlot extends VizPlugin {
constructor(options) {
super(options);
}
build() {
//I'd like to also use the reference to d3 from VizPlugin here
}
...
}
export default ScatterPlot;

Importing an instantiated class sets global prototype

Given the following react component, I am able to import the following component from multiple components. I'm confused on why after importing into each module and calling increment, it will increment the value as if it were the same instance. I don't think it's attached to the 'window', because I did some inspection.
Could this be, because this sets a global prototype? That still doesn't explain why it's seemingly updating the same instantiated class.
import React, { Component } from 'react';
class FeatureFlags extends Component {
constructor (props) {
super (props);
this.featureFlagList = [ 'test': true ];
this.i = 0;
}
get showFeatureFlagList () {
return this.featureFlagList;
}
increment () {
this.i++;
return this.i;
}
setList (list) {
this.featureFlagList = list;
return this.featureFlagList;
}
render () {
return (
<div></div>
);
}
}
export default new FeatureFlags;
//First component -
import FeatureFlags from './FeatureFlags';
console.log('first module ', FeatureFlags.increment() ); //Logs 1
//Second component
import FeatureFlags from './FeatureFlags';
console.log('second module ', FeatureFlags.increment() ); //Logs 2
No, it's because you
export default new FeatureFlags;
That's one single instance! Importing the module multiple times will always import the same value.
Instead, you should always export the class:
export default class FeatureFlags extends Component { … }
and instantiate it in the other modules as often as you need it:
import FeatureFlags from './FeatureFlags';
const myLocalFlags = new FeatureFlags;
console.log('first module ', myLocalFlags.increment()); //Logs 1

Typescript: Inject generic & get ES6 module name

I am trying to build a generic repository using:
Typescript
ES6
Angular 1.x
But I can't figure out how I should inject the Entity and then get its module name.
The reason why i want to get the name:
Is because i follow a naming convention where a file called order-count.ts should render the URL '/order/count'
Is this solvable with Typescript/Javascript?
Here is what i have:
order-module.ts
import {App} from '../../App';
import {OrderService} from './order-service';
const module: ng.IModule = App.module('app.order', []);
module.service('orderService', OrderService);
order-service.ts
import {CrudService} from '../../shared/services/crud-service'
import {OrderCount} from '../order/entities/order-count';
export class OrderService {
// #ngInject
constructor(private crudService: CrudService<OrderCount>) {
this.crudService = crudService;
}
getOrders() {
var promise = this.crudService.getAll();
promise.then(response => {
console.log(response, 'success');
}, error => {
console.log(error, 'failed');
});
}
}
order-count.ts
import {Entity} from '../../../shared/models/entity';
export class OrderCount extends Entity {
storeId: string;
storeName: string;
}
entity.ts
export interface IEntity {
id: number;
}
entity.ts
import {IEntity} from '../../module/contracts/entities/entity';
export class Entity implements IEntity {
new() { }
id: number;
}
crud-service.ts
'use strict';
import { Entity } from '../models/entity';
import { EndpointService } from './endpointService';
export class CrudService<TEntity extends Entity> {
private baseCallPath: string;
private entity: { new (): Entity };
// #ngInject
constructor(private endpointService: EndpointService, private $http: ng.IHttpService) {
this.baseCallPath = new this.entity().constructor.name.replace('-', '/');
}
getAll(): ng.IHttpPromise<any> {
return this.handleResponse(
this.$http.get(this.endpointService.getUrl(this.baseCallPath)),
'getAll'
);
}
handleResponse(promise: ng.IHttpPromise<any>, callerMethodName: string): ng.IHttpPromise<any> {
return promise.success((data: any) => {
Array.prototype.push.apply(this.baseCallPath, data);
}).error((reason: any) => {
console.log(this.baseCallPath + callerMethodName, 'ERROR', reason);
});
}
}
endpoint-service.ts
export class EndpointService {
private baseUri: string = 'http://localhost:3000/api/';
getUrl(moduleName: string): string {
return this.baseUri + moduleName;
}
}
This link may be helpful in order to implement a generic repository with Typescript
Regarding the usage of class name as a value you may check this relevant question.
The good thing it can be retrieved and used as Foo.name or this.constructor.name. The bad thing is that it isn't available in every browser and should be polyfilled. Another bad thing is that minified function won't save its original name.
Wouldn't it be great to annotate function with Foo.name = 'Foo' on its definition and stick to pre-made property? Not really. Function.name is originally non-configurable, so it is read-only in a plethora of browsers.
If you don't plan to avoid minimization at all, or you're not too fond of configuring minifier to preserve class names (a solution faulty by design), don't use Function.name for anything like that.
The typical case for extendable ES6/TS class in Angular is
export class Foo {
static _name = 'Foo';
}
export default angular.module('app.foo', [])
.factory('Foo', Foo)
// if DRY is a must,
// .factory(Foo._name, Foo)
.name;
import { Foo } from './foo';
export class Bar extends Foo {
static _name = 'Bar';
}
export default angular.module('app.bar', []).factory('Bar', Bar).name;
import moduleFoo from './foo';
import moduleBar from './bar';
angular.module('app', [moduleFoo, moduleBar]);
So exports for Angular modules and classes should go hand in hand, they are not interchangeable.

Typescript, cant load internal module

I m actually learning typescript, and I m facing some problems with internal modules.
In fact, I have three files :
index.ts in which I start my app
///<reference path='RouteManager.ts'/>
import RouteManager = RestifyRouting.RouteManager;
var myManager = new RouteManager();
myManager.init("superpath");
RouteManager.ts that manage my REST routes
///<reference path='RouteParser.ts'/>
module RestifyRouting {
export class RouteManager {
routeParser:RouteParser;
constructor() {
}
public init(filePath) {
this.routeParser = new RouteParser();
this.routeParser.register("zfaf","callback");
console.log(filePath);
}
}
}
RouteParser which has to parse some string to get some informations
module RestifyRouting {
export class RouteParser {
constructor() {
}
public register(path, callback) {
console.log('super register');
}
}
}
I have a gulp file that creates my .js and d.ts files and it works great, except for the index.js file. The compiler tells my that RestifyRouting (which is my internal module) is undefined and I dont know why...
Can you help me ?
PS : every files are in the same folder, it's just a learning application.
Thanks for advance
As of TypeScript 1.5 the module syntax is aligned with ES6 module syntax and that is what I have been using as well...
You can remove any references to TypeScript modules and just export the classes directly
index.ts
import { RouteManager } from './RouteManager';
var myManager = new RouteManager();
myManager.init("superpath");
RouteManager.ts
import { RouteParser } from './RouteParser';
export class RouteManager {
routeParser:RouteParser;
constructor() {}
public init(filePath) {
this.routeParser = new RouteParser();
this.routeParser.register("zfaf","callback");
console.log(filePath);
}
}
RouteParser.ts
export class RouteParser {
constructor() {}
public register(path, callback) {
console.log('super register');
}
}
Keeping modules
If you'd like to keep using internal modules then you have to be sure to export your module as well as the classes inside the module.
// RouteManager.ts
export module RestifyRouting {
export class RouteManager{}
}
//index.ts
import { RestifyRouting } from './RouteManager';
//usage
var manager = new RestifyRouting.RouteManager();
Something to keep in mind is that you will not be able to import multiple items into the the same name.
// i.e.
import { RestifyRouting } from './RouteManager';
import { RestifyRouting } from './RouteParser';
NOTES
the {} syntax in the import statement can allow multiple imports
{ Class1, Class2 }
The {} can be skipped if you exporting a default:
//Source (foo.ts):
export default class Foo{}
//Reference:
import Foo from './foo';
//usage:
class User {
foo: Foo;
}

Categories

Resources