Trouble understanding call of method within an instance/declaration - javascript

I'm taking an online web development course. I've come across some code that is confusing me. Within the index.js file there is a variable titled defaultCardList which instantiates a new instance of Section class. However, before the declaration is finished, within the renderer property, the defaultCardList variable is used to call the setItem method.
How is it possible to call the method from the defaultCardList variable before it's completely declared?
const defaultCardList = new Section({
data: items,
renderer: (item) => {
const card = new DefaultCard(item, ".default-card");
const cardElement = card.generateCard();
defaultCardList.setItem(cardElement); //How can this be called before its completion?
}
}, cardListSelector);

Related

Javascript Class constructor - instances, pointers, and private var references

I have a class I reuse, often, to the tune of possibly tens of thousands of instances in a given session. It occurred to me that creating all these properties within the constructor may be replication, that is each function is unique in memory and not a pointer, so I created a little test setup:
const testTree = function (){
console.log(this, this.konnichiwa);
}
const testFjord = function (aloha){
return function() {
console.log(this, aloha, this.konnichiwa);
}
}
class Clown extends Object{
constructor(props){
super(props);
const aloha = "Hello!"; //<- Private party
this.konnichiwa = "Also hello I think"; //<- Everyone's invited
this.testWan = () => {
console.log(this, aloha, this.konnichiwa);
}
this.testTree = testTree;
this.testFjord = testFjord(aloha);
}
testToo = () => {
console.log(this, this.konnichiwa);
}
}
//export default Clown; //this is an export in my application, used lots
const test = new Clown();
const otherTest = new Clown();
console.log(test.testWan === otherTest.testWan);
console.log(test.testToo === otherTest.testToo);
console.log(test.testTree === otherTest.testTree);
console.log(test.testFjord === otherTest.testFjord);
test.testWan();
test.testToo();
test.testTree();
test.testFjord();
Part 1
As you can test above, testWan, testToo, and testFjord are all unique per instance, but testTree is not. Is there any way to declare a "pointer"/"reusable function" but inside class constructor?
The issue here with testToo and testTree is that they can't access private vars within the constructor like testWan can. testFjord is a factory and can be passed these, but then the returned function is unique and won't be able to interact well with vars passed into it.
It's very likely not possible - I think it's a catch 22 scope thing - but you may know better. The only recourse I can think of is to add a property to this for each thing I need to use in testTree, but that exposes things I may not want exposed outside of the class.
Part 2
This part only applies if this is a generally consistent behavior, and not something completely unique per-browser. Does the engine hold onto references to things like conditionals (which I suspect are sorta anonymous-function-like behind the scenes) once the constructor has run?
I have a fairly knarly conditional setup I'm not going to shove in the code here. This is entirely within the constructor right now. I suspect that, although not a function declaration itself, it is also not a pointer, but an entirely fresh instance per the 'new' in new Clown. It needs to manipulate some private vars and so per Part 1 I haven't figured out a good way to extract this.
Example, there are references to private vars inside the constructor for exposed functions: aloha above is private but used by public testWan function, and so needs to be held after constructor has executed. Is the entire constructor held for the life of test & otherTest or is the constructor going to be dropped after use and just the reference to aloha held in memory?

Async data for singleton pattern

I was asked to create a singleton that will have methods which it will operate on some data it holds. The data is loaded from a nearby CSV file, but I cannot find a way to do it in an asynchronous way.
This is a Vanilla JavaScript assignment.
I have this CSV file, which I want to load/ read only once, and then "hold" inside of my singleton. Since all of the singleton's methods regard this data, I don't think there is a reason to read it more than once, at the beginning.
Since reading (and parsing) it is a asynchronous work, and so are some expected methods, I can't find the right way/ place/ method to do it.
Searching the internet shows that if I choose to create a class, the constructor must NOT by asynchronous.
An IIFE pattern did not work for me as well...
I currently have no code, since nothing seems to work, unfortunately.
Any help will be highly appreciated!
There are a few ways you can do this. All of the approaches are fine, so it is personal preference.
1. Static Method
class CsvHandler {
static readFile(fileName) {
return new Promise(resolve => {
const instance = new CsvHandler()
// Process your csv file here
resolve(instance);
}
}
}
const csvHandler = CsvHandler.readFile('fileName.txt')
2. Factory Method
class CsvHandler {}
class CsvFactory {
readFile(fileName) {
return new Promise(resolve => {
const instance = new CsvHandler()
// Process your csv file here
resolve(instance);
}
}
}
var csvFactory = new CsvFactory(),
csvHandler = csvFactory.readFile('fileName.txt')
You can add a method to your class that builds an instance of the class with the required data having fetched the CSV file.an example is this:
class Importer{
private importerInstance: Importer
private constructor(private csvData){
...
}
// returns an instance of the importer
static async composeInstance(path to CSV){
... retrieve file content asynchronously
if(!this.importerInstance) {return new Importer(<result of async csv fetching>)} else {return this.importerInstance}
}
}
Let me know if it makes any sense. I could try to do a more elaborate example for you

Require module that uses a singleton array

I want to create a really basic CRUD (sort-of) example app, to see how things work.
I want to store items (items of a shopping-list) in an array, using functions defined in my listService.js such as addItem(item), getAllItems() and so on.
My problems come when using the same module (listService.js) in different files, because it creates the array, in which it stores the data, multiple times, and I want it to be like a static "global" (but not a global variable) array.
listService.js looks like this:
const items = [];
function addItem (item) {
items.push(item);
}
function getItems () {
return items;
}
module.exports = {addItem, getItems};
and I want to use it in mainWindowScript.js and addWindowScript.js, in addWindowScript.js to add the elements I want to add to the array, and in mainWindowScript.js to get the elements and put them in a table. (I will implement later on Observer pattern to deal with adding in table when needed)
addWindowScript.js looks something like this:
const electron = require('electron');
const {ipcRenderer} = electron;
const service = require('../../service/listService.js');
const form = document.querySelector('form');
form.addEventListener('submit', submitForm);
function submitForm(e) {
e.preventDefault();
const item = document.querySelector("#item").value;
service.addItem(item);
console.log(service.getItems());
// This prints well all the items I add
// ...
}
and mainWindowScript.js like this:
const electron = require('electron');
const service = require('../../service/listService.js');
const buttonShowAll = document.querySelector("#showAllBtn")
buttonShowAll.addEventListener("click", () => {
console.log(service.getItems());
// This just shows an empty array, after I add the items in the add window
});
In Java or C#, or C++ or whatever I would just create a Class for each of those and in main I'd create an instance of the Service and pass a reference of it to the windows. How can I do something similar here ?
When I first wrote the example (from a youtube video) I handled this by
sending messages through the ipcRenderer to the main module, and then sending it forward to the other window, but I don't want to deal with this every time there's a signal from one window to another.
ipcRenderer.send('item:add', item);
and in main
ipcMain.on('item:add', (event, item) => {
mainWindow.webContents.send('item:add', item);
})
So, to sum up, I want to do something like : require the module, use the function wherever the place and have only one instance of the object.
require the module, use the function wherever the place and have only one instance of the object.
TL:DR - no, that isn't possible.
Long version: Nature of Electron is multi process, code you runs in main process (node.js side) and renderer (chromium browser) is runnning in different process. So even you require same module file, object created memory in each process is different. There is no way to share object between process except synchrnonize objects via ipc communication. There are couple of handful synchronization logic modules out there, or you could write your module do those job like
module.js
if (//main process)
// setup object
//listen changes from renderer, update object, broadcast to renderer again
else (//rendere process)
//send changes to main
//listen changes from main
but either cases you can't get away from ipc.

World object in cucumber.js or where to put state in cucumber tests

I'm trying to save the current navigation state in one step (the page on a platform with multiple websites) in cucumber.js so the following steps of a scenario can deal with it. I thought using the World object for it, but mysterious things are happening.
I have a navigation state object like this:
module.exports = {
pageName:null,
siteName: null,
isLoggedIn: false
}
Then I have a NavigationStateManager like this
function NavigationStateManager() {
var state
this.setState = function(stateP) {
state = stateP
}
this.setPage = function(pageNameP, siteNameP, isLoggedInP) {
// among other things do something link this:
state.pageName = pageNameP
state.siteName = siteNameP
state.isLoggedIn = isLoggedInP
}
}
And I have a World object
var navState = require('./navigation-state')
var NavigationStateManager = require('./navigation-state-manager')
var navigationStateManager = new NavigationStateManager()
function World() {
this.navState = simpleCopy(navState)
navigationStateManager.setState(this.navState)
}
function simpleCopy(objectToCopy) {
var copy = {}
for(var key in objectToCopy) {
copy[key] = objectToCopy[key]
}
return copy
}
In my steps file I do this
var World = require('../support/world')
module.exports = function() {
this.World = World
this.Given(...)
this.Then(...)
}
For some reason the state becomes undefined in the NavigationStateManager when the Given steps have been executed and the Then steps are being executed. When I log I can't see setState being called with an 'undefined' argument. I've had a different setup, putting the NavigationStateManager on the World object, but it gave me similar issues. Apparently the World object doesn't remain the same through all steps of a scenario, but how does it behave. The error seems to go against all JavaScript knowledge I have. Where do I put state in my tests?
All support files that export a function will be called with a context that exposes the following methods:
source: https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/api_reference.md
I hadn't read this (and probably wouldn't have understood). I also confused the this reference to the context object and the this reference to a world object.
With context they mean the object that is exposed as this in the functions you export. It is the interface to interact with the Cucumber API.
This so called context object shouldn't be confused, with the world object. The world object is the this reference inside your steps, and is created by Cucumber from the World constructor you set on the context object (or the default if you don't set one) for every scenario.
Lastly you should not require and create new instances of any constructor exported in the support folder as I did. Since Cucumber automatically calls these constructors, you'll end up with two instances of the same object. Put your own helper objects, like a PageObject, in an separate folder.

Parse and share obj resources in module

I wanted to know if its good practice to use it like following since I used a global field cacheObj
I need to parse the data and share it between other modules,any module can take any property but only the first module which called to this parser is responsible to provide the data to parse(I need to do this parse just once and share properties in different modules)
This code is from other SO post and I want to use it
var Parser = require('myParser'),
_ = require('lodash');
var cacheObj; // <-- singleton, will hold value and will not be reinitialized on myParser function call
function myParser(data) {
if (!(this instanceof myParser)) return new myParser(data);
if (!_.isEmpty(cacheObj)) {
this.parsedData = cacheObj;
} else {
this.parsedData = Parser.parse(data);
cacheObj = this.parsedData;
}
}
myParser.prototype = {
//remove `this.cacheObj`
getPropOne: function () {
return this.parsedData.propOne;
},
getPropTwo: function () {
return this.parsedData.propTwo;
}
};
module.exports = myParser;
It kindda looks like the Context Object pattern, which is used for maintaining state and for sharing information. Some consider it a bad practice and prefer Singleton when it comes to share the object between layers, but if suites your case (in the same module) - my advice is to use it.
UPDATE
The main reason why you shouldn't use ContextObject through your layes is because it binds all sub-systems together( one object is referencing everything else). While Singleton is not just for creating objects, it is also services as access point that can be loaded by the corresponding sub-system. Having a Singleton represent every service access point allows for seamless vertical integration of cooperating components/modules. Simple code example:
Singleton:
// returns the "global" time
var time = Clock.getInstance().getTime();
Context object:
// allows different timezones to coexist within one application
var time = context.getTimezoneOffset().getTime();

Categories