Set public variables node.js - javascript

I need to access to a variable that i define in my index.js from static.js which is called with require()
Index.js
function API() {
var self = this
self.init = function(apikey, region, locale) {
//Some stuff
self.region = region
self.locale = locale
self.apikey = apikey
self.static = require('./static').static
}
}
module.exports = new API();
Static.js
module.exports = {
static: {
someFunction: function(someParameters) {
//Need to access to self.region, self.locale and self.apikey
},
otherFunction: function(someParameters) {
//Need to access to self.region, self.locale and self.apikey
}
}
My problem is to use region, locale and apikey from the static.js file
Test.js
var api = require('./index.js');
api.init('myKey', 'euw', 'en_US')
console.log(api);
does that:
RiotAPI {
region: 'euw',
locale: 'en_US',
apikey: 'myKey',
static: { someFunction: [Function], otherFunction: [Function] }
}
which is okay but when i call someFunction() with the good arguments it tells me that self.region (and the others i guess) is not defined

You need to put the methods from static.js at the top-level of your API instance.
var static = require('./static').static
function API() {
// constructor
}
API.prototype.init = function(apikey, region, locale) {
//Some stuff
this.region = region
this.locale = locale
this.apikey = apiKey
}
Object.assign(API.prototype, static)
module.exports = new API();
Then reference this.region etc. in your static methods.

Related

class instance lost when using a class method stored as an object attribute

We have two typescript class. On of them define a method as an object argument then transmitted in another class.
class MyService {
public options: { [key: string]: any };
constructor() { }
public upload(file: File): void {
const parallelHasher = new ParallelHasher('/public/md5_worker.js');
this.options = {
concurrency: 1,
autoUpload: true,
hashMethod: parallelHasher.hash // HERE
};
const uploadService = new UploadService(this.options);
this.uploadsProgress = uploadService.start(file)
}
}
class UploadService {
constructor(public options: any) { }
public async start(file: File): Promise<void> {
const hash = await this.options.hashMethod(file);
this.proceed(file, hash);
}
public proceed(file: File, hash: string): void {
// do something
}
}
We encounter an error when UploadService.start() is called due to the hash method of the third party:
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'push' of undefined
TypeError: Cannot read property 'push' of undefined
at parallel_hasher.js:25
Here is the relevant part in third party code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var ParallelHasher = (function () {
function ParallelHasher(workerUri) {
this._queue = [];
this._ready = true;
var self = this;
if (Worker) {
self._hashWorker = new Worker(workerUri);
self._hashWorker.onmessage = self._recievedMessage.bind(self);
self._hashWorker.onerror = function (err) {
self._ready = false;
console.error('Hash worker failure', err);
};
}
else {
self._ready = false;
console.error('Web Workers are not supported in this browser');
}
}
ParallelHasher.prototype.hash = function (blob) {
var self = this;
var promise;
promise = new Promise(function (resolve, reject) {
self._queue.push({
blob: blob,
resolve: resolve,
reject: reject,
});
self._processNext();
});
return promise;
};
return ParallelHasher;
}());
exports.ParallelHasher = ParallelHasher;
As you can see _queue is defined in the constructor and should be there when the hash() method tries to use it to push new element to queue.
By placing a debugger on the line with var self = this; in the third party's hash method, the value of this is equivalent to this.options in MyService class, instead of being ParallelHasher:
concurrency: 1,
autoUpload: true,
hashMethod: parallelHasher.hash
And indeed, there is no _queue in options, hence the error.
Somehow, the context of the third party's method call is set, not in the ParallelHasher instance but in the options object.
I don't really understand why, and how to prevent this from occuring.
The library's parallelHasher.hash method isn't bound to this, so when you pass the method itself, it loses its this pointer. Instead, in your options, you can create an anonymous function, so that the library method keeps its this context and you don't have to modify the library itself.
this.options = {
concurrency: 1,
autoUpload: true,
hashMethod: (blob) => {
return parallelHasher.hash(blob);
}
};
Here's some docs that are worth reading to better understand what's going on: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

Convincing TS that an object coming through a proxy is never null

I have the following fn that creates a proxy:
const getStorageProxy = (store?: Storage) => {
const proxyObj: { store?: DataStorage } = {};
return new Proxy(proxyObj, {
get(obj, prop) {
if (!obj.store) {
obj.store = new DataStorage(store);
}
return obj.store[prop];
},
});
};
And then I export several instances of this proxy:
const storageA = getStorageProxy(storeA);
const storageB = getStorageProxy(storeB);
export { storageA, storageB };
The reason for this is that I don't want to actually instantiate the DataStorage class unless it's used, and for convenience I want to be able to type:
import { storageA } from 'storage';
This all works. The problem is that according to TS both of my exported storage instances are of type DataStorage | void because of:
const proxyObj: { store?: DataStorage } = {};
They're actually not because of the getter in the proxy, but how do I tell this to TS without actually assigning something to proxyObj.store on instantiation?

How to deal with fn.call(this) replacing original this

To begin, I have an application that is used for managing employees. When the user creates a new instance of the application, I would like for them to have an option to submit a function that will run before anything else in the application does. The problem is that I need to add functionality to the end of that function so I need it passed back to the application.
However, if I use fn.call(this) in the StateManager.js class, it then overrides the this of state manager and gets rid of the functionality of StateManager. The exact error returned is Uncaught TypeError: this.onPreload is not a function
Essentially, when a new instance is created, I would like to take the user's preload function and pass it to StateManager.js where it will be adjusted.
Here is demonstration code:
class Application {
constructor(options = {}) {
return new User(options);
}
}
class User {
constructor(options) {
this._options = options;
this.state = new StateManager(this);
this.job = new Job(this);
this.init();
}
init() {
this.state.onPreload = this._options.preload;
this.state.preload.call(this);
}
}
class Job {
constructor(user) {
this.user = user;
}
changeTitle(title) {
this.user.jobTitle = title;
}
}
class StateManager {
constructor(user) {
this.user = user;
this.onPreload = null;
}
preload() {
this.onPreload();
}
}
const options = {
preload: preload
};
const app = new Application(options);
function preload() {
app.job.changeTitle('CEO');
}
index.js
import { Application } from './Application.js';
const options = {
preload: preload
};
const app = new Application(options);
function preload() {
// Access some irrelevant function in job that sets a new value
app.job.changeTitle('CEO');
}
app.js
import { User } from './User.js';
export class Application {
constructor(options = {}) {
return new User(options);
}
}
user.js
import { StateManager } from './StateManager.js';
import { Job } from './Job.js';
export class User {
constructor(options = {}) {
this._options = options;
this.state = new StateManager(this);
this.job = new Job(this);
this.init();
}
init() {
this.state.onPreload = this._options.preload;
this.state.preload.call(this);
}
}
statemanager.js
export class StateManager {
constructor(user) {
this.user = user;
this.onPreload = null;
}
preload() {
this.onPreload();
// My custom functionality to add at the end.
}
}
preload() is referring to the global variable app, but it's being called in the function used to initialize app in the first place. It needs to receive the User object being initialized, rather than referring to the global variable.
Use this.state.onPreload = this._options.preload.bind(this); to bind the context of the preload function to that object.
You could also change StateManager.preload() to use this.onPreload.call(this.user);. But this might create an inappropriate dependency that doesn't apply in all cases. If I understood all the relationships better, I might be able to decide this better.
class Application {
constructor(options = {}) {
return new User(options);
}
}
class User {
constructor(options) {
this._options = options;
this.state = new StateManager(this);
this.job = new Job(this);
this.init();
}
init() {
this.state.onPreload = this._options.preload.bind(this);
this.state.preload();
}
}
class Job {
constructor(user) {
this.user = user;
}
changeTitle(title) {
this.user.jobTitle = title;
}
}
class StateManager {
constructor(user) {
this.user = user;
this.onPreload = null;
}
preload() {
this.onPreload();
}
}
const options = {
preload: preload
};
const app = new Application(options);
console.log(app.jobTitle);
function preload() {
this.job.changeTitle('CEO');
}

How to export functions in modules?

Dear all I just started learning modules in javascript. This is my implementation of a module:
module.exports = function(logger_level) {
this.info = function(message) {
stdout(message);
};
};
function stdout(message)
{
process.stdout.write(message+'\n');
}
and I have to invoke it like this:
var logger = require('./js/logger.js');
var logger1 = new logger("info1");
logger1.info("info");
My question is how to change my implementation of the module so that I could invoke it like this:
var logger = require('./js/logger.js')("info");
logger.info("info");
If you want to use it like that, there's no need for a constructor function at all:
module.exports = {
info: function(message) {
stdout(message);
}
};
function stdout(message)
{
process.stdout.write(message+'\n');
}
I just started learning modules in javascript.
Note that this isn't "modules in JavaScript" so much as it is "modules in NodeJS". JavaScript's modules, which weren't introduced until ES2015 ("ES6"), look and act quite different, but they aren't broadly-supported yet. When they are, your module might look like this:
// ES2015+ syntax
export default {
info: function(message) {
stdout(message);
}
};
function stdout(message)
{
process.stdout.write(message+'\n');
}
...and used like this:
import logger from './logger.js';
logger.info("info");
You're looking for a factory function. Applying it to your use case it would look like:
module.exports = function(logger_level) {
return {
info: function(message) {
stdout(message);
}
};
};
function stdout(message)
{
process.stdout.write(message+'\n');
}
You can wrap your constructor function with an object and return an instance of your constructor function
module.exports = {
return new function(logger_level) {
this.info = function(message) {
stdout(message);
};
};
}
And call it
var logger = require('./js/logger.js');
logger.info("info");
You can also make a singleton pattern to work with a single instance of your constructor function

Exporting Objects with the Exports Object

Say I have one .js file containing a javascript object. I want to be able to access that object and all of its functionality from another .js file in the same directory. Can I simply export this object with the module.exports object and require() it in the other .js file? If this is possible can you give me an example?
If it helps I'm developing with node.
This is the way I create modules:
myModule.js
var MyObject = function() {
// This is private because it is not being return
var _privateFunction = function(param1, param2) {
...
return;
}
var function1 = function(param1, callback) {
...
callback(err, results);
}
var function2 = function(param1, param2, callback) {
...
callback(err, results);
}
return {
function1: function1
,function2: function2
}
}();
module.exports = MyObject;
And to use this module in another JS file, you can simply use require and use your object as normal:
someFile.js
var myObject = require('myModule');
myObject.function1(param1, function(err, result) {
...
});
Of course you can. In my example I use obj to hold my config info. I put it in a file called index.js in config folder. This makes the index the preferred choice to be picked when I import 'config'. I have 2 exports here one for my node and api stuff and the other for my db. You can ignore the first bit where I set the environment.
const environment = {
development: {
isProduction: false
},
production: {
isProduction: true
}
}[ process.env.NODE_ENV || 'development' ];
export default Object.assign({
host: 'localhost',
port: '3000',
remoteApi: {
token: {
'X-Token': '222222222222222222'
},
base: 'https://www.somedomain.com/api'
}
}, environment);
export const db = {
dbHost: 'localhost',
dbPort: 176178
};
Calling import config from '../config'; will pick the default one. And if I specify I can get the db export import { db } from '../config';
In one file:
module.exports.myObj = some object...;
In the other:
Obj = require('myFile.js').myObj;
Everything in a js file on node is local to that file unless you put it in the export object. This actually is very different from JavaScript in a browser--in the browser all files that get imported act together like one big file.
You can kinda think about node files as though you are creating a module object and passing it' into a function surrounding your code.
module = { 'exports' : {} };
(function(module){
//your js file
...
})(module)
the simplest approach, in my opinion:
write Person.js: (note it comes with ctor)
module.exports = function (firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.fullName = function () {
return this.firstName + ' ' + this.lastName;
}
}
and consume it:
var person = require('./Person.js');
var person1 = new person('James', 'Bond');
console.log(person1.fullName());
note that in this simple solution all methods are "public".
ref: https://www.tutorialsteacher.com/nodejs/nodejs-module-exports
as per es6 :-
const person = function(name){
console.log(`Name of the person is ${name}`);
}
export default person;
You can export object like
modules.js
export const events = {};
events.click = function (param1, param2...) {
//do something here
}
events.change = function (param1, param2...) {
//do something here
}
events.get = function (key, response) {
//do something here
response({status: "success"})
}
In main.js
import {events} from "./modules"
console.log(events)
Now you can use the Objects
events.get("my_key", (resp) => {
console.log(resp) // {status: "success"}
})

Categories