module.export and global objects - javascript

I'm confused.
Occasionally when my web api receives data it mixes the data up between objects and it appears to me that the global object in the file is actually being persistent..
Here is the basic layout of the code
handlers.js
const something = require('./otherfile')
let handlers ={}
handlers.customers = function (data, callback) {
let acceptableMethods = ['post'];
if (acceptableMethods.indexOf(data.method) > -1) {
handlers._customers[data.method](data, callback);
} else {
callback(405);
}
};
handlers._customers = {}
handlers._customers.post = async function (data, callback) {
customer.new(data);
callback(200, 'success')
}
otherfile.js
let contacts_list = [];
let accountData = {};
module.exports = something = {
new: {
dostuff: async function (data) {
// update and reference global objects here..
accountData.name = data.name;
accountData.otherProperty = await somefunction(data.prop)
}
}
}
I expected that since it is requiring an exported module that each time it would call the exported module it would be treated as its own object, however, it seems that the object is not being treated as unique and is instead being overwritten in part and 'randomly'. This suggests to me that I may be able to export a mutating object such as an array across files
Am I correct in that the global is being persisted across multiple requests?
Would setting the global within the export object affect the behaviour of this object in any way? In this case I don't want this data to mutate.
Thanks in advance for your constructive criticisms and guidance :)

[Restructure your code so you are creating a new object on every request. Module's are cached on the first require so all of your variables and object properties will be persisted across calls.
// handler.js
const somethingFactory = require('./otherfile')
module.exports = function(){
let handlers = {}
const something = somethingFactory();
handlers.customers = function (data, callback) {
let acceptableMethods = ['post'];
if (acceptableMethods.indexOf(data.method) > -1) {
handlers._customers[data.method](data, callback);
} else {
callback(405);
}
};
handlers._customers = {}
handlers._customers.post = async function (data, callback) {
customer.new(data);
callback(200, 'success')
}
return handlers;
};
otherfile.js
module.exports = function(){
let contacts_list = [];
let accountData = {};
return {
new: {
dostuff: async function (data) {
// update and reference global objects here..
accountData.name = data.name;
accountData.otherProperty = await somefunction(data.prop)
}
}
}
};

Related

How do I manage context when exposing object methods in JS modules?

Okay, I realize this can be considered subjective, but I'm trying to better understand how to consider scope when writing modules that only expose what's needed publicly. I have a string utility that I've written as an object literal below:
const substrings = {
query: {},
text: "",
results: [],
exists: function (index) {
const exists = index >= 0
return exists
},
check: function () {
const q = this.query
const start = q.openIndex
const stop = q.closeIndex
if (q.hasOpen && !q.hasClose) {
console.log("Missing closing delimiter.")
}
if (!q.hasOpen && q.hasClose) {
console.log("Missing opening delimiter.")
}
if (q.hasOpen && q.hasClose && start > stop) {
console.log("Closing delimiter found before opening.")
}
if (!q.hasOpen && !q.hasClose && this.results.length == 0) {
console.log("No results found.")
}
const order = start < stop
const check = q.hasOpen && q.hasClose && order
return check
},
update: function () {
const q = this.query
const text = this.text
q.before = this.text.indexOf(q.open)
q.start = q.before + q.open.length
this.text = text.slice(q.start, text.length)
q.stop = this.text.indexOf(q.close)
q.after = q.stop + q.close.length
q.openIndex = q.before
q.closeIndex = q.before + q.stop
q.hasOpen = this.exists(q.openIndex)
q.hasClose = this.exists(q.stop)
const newPosition = q.start + q.after
q.position = q.position + newPosition
this.query = q
},
substrings: function () {
const q = this.query
const current = this.text.slice(0, q.stop)
const fullLength = this.text.length
this.text = this.text.slice(q.after, fullLength)
this.results.push(current)
this.update()
if (this.check()) {
this.substrings()
}
},
init: function (open, close, text) {
this.results = []
this.query = {
open,
close,
position: 0,
}
this.text = text
this.update()
},
getSubstrings: function (open, close, text) {
this.init(open, close, text)
if (this.check()) {
this.substrings()
return this.results
}
},
getSubstring: function (open, close, text) {
this.init(open, close, text)
if (this.check()) {
return this.text.slice(0, this.query.stop)
}
}
}
I want to use it as a Node module and expose the getSubstring and getSubstrings methods, but if I were to do:
module.exports = {
all: substrings.getSubstrings,
one: substrings.getSubstring
}
I would get an error due to the usage of this. I realize that if I replace this with the object var name substrings to reference it directly, it works. I could also refactor it to be one big function or smaller functions and just export the 2 I need.
I am trying to go about learning things the right way and am struggling with how I should be thinking about context. I understand how this changes here, but I feel like I'm not fully wrapping my head around how I should consider context when structuring my code.
Is there a more elegant solution to expose methods with code like this that wasn't written to separate private and public methods?
A simple solution would be to bind the exported functions to the proper calling context inside the exports object:
module.exports = {
all: substrings.getSubstrings.bind(substrings),
one: substrings.getSubstring.bind(substrings)
}
Personally, I prefer using the revealing module pattern over object literals for situations like this. With the revealing module pattern, create an IIFE that returns the desired functions, referring to local variables instead of properties on this. For example:
const { getSubstrings, getSubstring } = (() => {
let query = {}
let text = ''
let results = []
function exists(index) {
return index >= 0
}
function check() {
const q = query;
// ...
}
...
function getSubstrings(open, close, text) {
}
...
return { getSubstrings, getSubstring };
})();
module.exports = {
all: getSubstrings,
one: getSubstring
}
This is somewhat opinion-based, but code can be easier to read when there aren't any this references to worry about.

attach event to object/array

Let's say for you example you have this array
//Defined at the top of your class
myArray = [];
//Then somewhere else in the code
myArray["unique_id"] = {example: true, example2: false, etc..};
//somewhere else
delete myArray["unique_id"];
Could it be possible to have something like this:
//when adding
myArray["unique_id"] = {example: true, example2: false, etc..};
myArray.trigger("array:update");
//when deleting
delete myArray["unique_id"];
myArray.trigger("array:delete");
//and in a different file or somewhere else down the path
myArray.on("array:update", function(){
//do stuff
});
//and in a different file or somewhere else down the path
myArray.on("array:delete", function(){
//do stuff
});
I like the custom event system and I was wondering if thats something possible. Didn't find examples on the documentation of this specific application.
Basically attaching custom events to array/objects and not necessarily dom elements.
This is possible using a Proxy, but you'll have to make the object emulate an EventEmitter first:
//Defined at the top of your class
let myObject = {};
//Emulate EventEmitter
myObject._events = {};
myObject.emit = function emit(event, ...args) {
(this._events[event] || []).forEach(callback => {
callback.apply(this, args);
});
};
myObject.on = function on(event, callback) {
if (!this._events[event]) {
this._events[event] = [];
}
this._events[event].push(callback);
};
//Define Proxy
let myProxy = new Proxy(myObject, {
set(target, name, value) {
target.emit('update', name, value);
return (target[name] = value);
},
deleteProperty(target, name) {
target.emit('delete', name);
return (delete target[name]);
}
});
//and in a different file or somewhere else down the path
myObject.on('update', function(name, value) {
console.log('update', this, name, value);
});
//and in a different file or somewhere else down the path
myObject.on('delete', function(name) {
console.log('delete', this, name);
});
//Then somewhere else in the code
myProxy["unique_id"] = {
example: true,
example2: false
};
//somewhere else
delete myProxy["unique_id"];

How do we return function using module.exports in nodeJS?

How do we return function using module.exports in nodeJS?
file_1 book.js
module.exports = function() {
var points = 0;
return {
rate: function(value) {
points = value;
},
get: function() {
return points;
}
}
}
book.js is root file. We create two different instances but can not get the methods of root to script.js file.
file_2 main.js
var bA = require('./book.js');
var bB = require('./book.js');
bB.rate(10);
bB.get();
Output => can not find rate and get method.
Because the function returns an object with references to the rate and get functions, you need to execute it with a () on require like so:
var book = require('./book.js')();
book.rate(10);
book.get();
You're returning a function which returns an object.
Call the function and get the object
/*file_2 main.js*/
var bA = require('./book.js')();
var bB = require('./book.js')();
bB.rate(10);
bB.get();
Just in case if someone's facing same problem as me
I had something issue with my code. Lately realised I was making some API call and so return returned an object before fetching the value from API endpoint
I added async in front of function call and it worked!
var util=require('./app/Utils')
const url=await util.getInfo();
You can also provide a name to your anonymous export function as
module.exports.myfun = function() {
var points = 0;
return {
rate: function(value) {
points = value;
},
get: function() {
return points;
}
} }
Then use the function in another file as
var inc = require('./comp.js');
var mod = inc.myfun();
mod.rate(10);
console.log(mod.get());
In this way you don't need to have '()' at the time of required, though that option can also be used

Javascript: Internal array is not reset to outer objects

How do I grant access to inner properties of objects in the right way? This is what does break my application:
I have an object that handles an array (simplified here):
function ListManager() {
var list = [],
add = function (element) {
list.push(element);
},
clear = function () {
list = [];
};
return {
add: add,
clear: clear,
list : list
};
};
But I get this when using it:
var manager = new ListManager();
manager.add("something");
manager.clear();
console.log(manager.list.length); // <= outputs "1"!
Stepping through the code shows, that within the clear method, list becomes a new array. But from outside the ListManager the list ist not cleared.
What am I doing wrong?
This is because clear sets the value of var list, not the .list on the object returned from ListManager(). You can use this instead:
function ListManager() {
var list = [],
add = function (element) {
this.list.push(element);
},
clear = function () {
this.list = [];
};
return {
add: add,
clear: clear,
list : list
};
}
Using your current structure, you could do:
function ListManager() {
var list = [],
add = function (element) {
list.push(element);
},
clear = function () {
list = [];
};
getList=function(){
return list;
}
return {
add: add,
clear: clear,
list : list,
getList: getList
};
};
var manager = new ListManager();
manager.add("something");
console.log(manager.getList()); // ["something"]
manager.clear();
console.log(manager.getList()); // []
function ListManager() {
var list = [],
add = function (element) {
this.list.push(element);
},
clear = function () {
this.list = [];
};
return {
add: add,
clear: clear,
list : list
};
};
var manager = new ListManager();
manager.add("something");
manager.clear();
console.log(manager.list.length); // <= now outputs "0"!
As has already been explained, your issue is that when you do list = [], you are changing the local variable list, but you aren't changing this.list as they are two separate variables. They initially refer to the same array so if you modified the array rather than assigning a new one to just one of the variables, they would both see the change.
Personally, I think you're using the wrong design pattern for creating this object that just makes things more complicated and makes it more likely you will create problems like you did. That design pattern can be useful if you want to maintain private instance variables that are not accessible to the outside world, but it creates a more complicated definition and maintenance if everything is intended to be public.
One of my programming goals is to use the simplest, cleanest way of expressing the desired functionality.
So that end, since everything in this object is intended to be public and accessible from outside the object, this is a whole lot simpler and not subject to any of the types of problems you just had:
function ListManager() {
this.list = [];
this.add = function(element) {
this.list.push(element);
}
this.clear = function() {
this.list = [];
}
}
Or, perhaps even use the prototype:
function ListManager() {
this.list = [];
}
ListManager.prototype = {
add: function(element) {
this.list.push(element);
},
clear: function() {
this.list = [];
}
};

im i using "this" the right way in JavaScript?

This is my code
ImageCarousel = (function() {
var currentIndex, imageManager, imagesVO, jsonPath, values;
currentIndex = null;
jsonPath = "json/images.json";
imagesVO = [];
values = null;
imageManager = null;
function ImageCarousel() {
this.loadJson();
}
ImageCarousel.prototype.loadJson = function() {
var _this = this;
return $.ajax(jsonPath, {
success: function(data, status, xhr) {
console.log("yea " + data);
_this.currentIndex = 0;
_this.imagesVO = data.images;
_this.imageManager = new ImageManager(data.images);
_this.imagesCount = _this.imagesVO.length;
_this.switchToImage(_this.currentIndex);
$('#next').click(function() {
_this.currentIndex = _this.incrementIndexByOne(_this.currentIndex);
return _this.switchToImage(_this.currentIndex);
});
return $('#prev').click(function() {
_this.currentIndex = _this.decrementIndexByOne(_this.currentIndex);
return _this.switchToImage(_this.currentIndex);
});
},
error: function(xhr, status, err) {
return $('#imageHolder').html("problem loading the json file, </br>make sure you are running this on your local server");
},
complete: function(xhr, status) {}
});
};
am i right for using "this" to refer to vars inside ImageCarousel class? does it make those properties public? if so, how do I keep them private?
Nope, you're not using this correctly. Those properties you are trying to reference are all private, as in no outside code could really access them by invoking some property of ImageCarousel.
If you do want to make those variables public for whatever reason, do not declare them using var. Instead, do something like this.currentIndex = null or this.jsonPath = "json/images.json". When you do that, you're essentially making those properties publicly accessible. Any outside code can access those properties simply by invoking ImageCarousel.currentIndex, ImageCarousel.jsonPath, etc. etc.
There's a few things wrong with this. Here's the code w/ corrections:
var ImageCarousel = function () {
var currentIndex, imageManager, imagesVO, jsonPath, values;
currentIndex = null;
jsonPath = "json/images.json";
imagesVO = [];
values = null;
imageManager = null;
var _this = this;
this.loadJson = function () {
return $.ajax(jsonPath, {
success: function (data, status, xhr) {
console.log("yea " + data);
currentIndex = 0;
imagesVO = data.images;
imageManager = new ImageManager(data.images);
imagesCount = imagesVO.length;
switchToImage(currentIndex);
$('#next').click(function () {
currentIndex = _this.incrementIndexByOne(currentIndex);
return _this.switchToImage(_this.currentIndex);
});
return $('#prev').click(function () {
currentIndex = _this.decrementIndexByOne(currentIndex);
return _this.switchToImage(currentIndex);
});
},
error: function (xhr, status, err) {
return $('#imageHolder').html("problem loading the json file, </br>make sure you are running this on your local server");
},
complete: function (xhr, status) {}
});
};
this.loadJson();
};
var someCarousel = new ImageCarousel(); // Example usage.
Effectively, ImageCarousel was declared twice. Once with ImageCarousel = (function() { and once with function ImageCarousel().... I opted for the former.
If I understood you correctly, you wanted currentIndex, imageManager, imagesVO, jsonPath, and values to be private. For those, just do a var inside your function block, and they will be private to each instance of that new'd object. You can use them safely inside your ImageCarousel function with no worries (and no _this).
I left _this on the methods that you're calling inside of loadJson because (not being able to see their definitions here) I am assuming they are public methods. If they're private, just declare them inside your wrapper function and they will only be accessible within. If you want them to be public, use this[functionName] as I've done with loadJson.
So the affect of my code changes are:
currentIndex, etc. are private.
loadJson is public.
EDIT
A couple more things on using prototype. prototype is for "static" functions - meaning, that function doesn't exist for every instance of ImageCarousel objects. If you use it, you are to use it outside of the function's declaration. Otherwise, every time you new an ImageCarousel it will be re-defining loadJson unnecessarily. Here's a nice little demo app that shows what I mean a little more clearly: http://jsfiddle.net/d2BbA/

Categories

Resources