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

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?

Related

Access Singleton from anywhere

In my game, I've created a list of gameObjects (instances of other classes such as Player) in my Main that I can loop through and render in one function, rather than rendering them all individually. This is my render function;
this.gameObjects = [];
addObj(obj){
this.gameObjects.push(obj);
}
render(){
this.gameObjects.forEach(obj => obj.render());
}
This works no problem if I want to add another gameObject using my main class like this;
let main = new Main();
let player = new Player(10, 30, 10, 10, 'red');
main.addObject(player);
main.start();
However, I want to add another gameObject, called Projectile within my Player class. The problem is my Player class doesn't have access to Main's addObj function.
My idea was to create a singleton, that holds a list of game objects, like this;
class Repository{
constructor(){
if(this.instance){
return this.instance;
}
this.list = [];
this.instance = this;
}
get obj(){...}
addObj(obj){...}
}
The tl:dr of the question is;
Is this the best way to go forward?
How would I access this singleton? By importing it everywhere I needed it?
Create a global variable with an instance? How would I do this?
Am I over thinking this? Should I just pass main in to each class and access its methods that way?
Using a singleton might complicate unit tests, and add a hidden state to the information flow of the application. It's not that the pattern is inherently flawed, but there's a right and wrong way to use this pattern, and if you don't intend to spend the time and understand the pattern, you're probably better off not using it. Your plan for the Singleton will work, it's just not the most robust solution.
Notice that JS is a functional language. You don't need to pass main to each class, you can just pass the addObject function. That way when you change context (for example in unit tests or if you'll recycle the code for other projects) you'll be able to just switched the passed function, and keep all of your code.

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();

Create ES6 class from a function

I'm trying to explore using ES6 classes instead of how we do it currently, using the Function.prototype means. Currently our API looks like:
var myclass = createClass('MyClass', {
test : function() {}
});
We iterate through the object and apply those properties onto the Function that we return, basically a prettier way than to do so that it's more inline with other programming languages of sorts:
function MyClass() {}
MyClass.prototype.test = function() {};
We also cache the class onto an object where name is the key and the function is the value for use throughout our application. The class name can be namespaced so you can have My.Cls and it will split by the period and then cache it onto the manager but it also can be retrieved via window.My.Cls.
Looking into ES6 classes, I don't see how I can keep the createClass function. Would love something like:
function createClass(name, config) {
return class name config;
}
I didn't expect it to work and it doesn't.
Two issues I have here:
How can I create a class using a variable as the class name?
How can I create a class and assign the properties via the config object argument?
Not sure this would be possible. We don't plan on keeping the createClass, we hope to keep it for now and upgrade our legacy "classes". I'd like to start using ES6 classes but not break the whole app for however long it'll take us to fully upgrade.
The only good upgrade route is to refactor the property hashes into proper classes. You can start that work and keep using your hash-based classes in the meantime, which will lighten the requirement to do it all at once.
If you have a limited number of "class" name:config pairs -- which you should for maintainability reasons -- then you can replace createClass with an implementation that does:
class Foo { ... }
class Bar { ... }
let classes = {'Foo': Foo, 'Bar': Bar};
function createClass(name, config) {
if (classes[name]) {
return classes[name];
}
// old impl
}
This will ignore the config if a "real" implementation exists, but keep using the legacy behavior if you haven't replaced the class. If it is, you can implement createClass more like:
function createClass(name, config) {
if (classes[name]) {
return new classes[name](config);
}
// old impl
}
and pass the config arguments into the class ctor. In this case, you may want to filter out function properties (methods) first, as the class probably implements them already. Something like:
function createClass(name, config) {
if (classes[name]) {
let fields = Object.keys(config).filter(key => {
return typeof config[key] !== 'function';
}).map(key => config[key]);
return new classes[name](fields);
}
// old impl
}

View Model inheritance when using Durandal

I am building an application using Durandal and I have the need to share some functionality across view models.
I have 5 screens to build and they are all virtually the same screen except that in the activate function they will call to a different api end points but otherwise the view and view models will be identical.
Is there a pattern that I should be following to structure this correctly to promote code reuse?
If the views and the view models are identical except for calling different api actions, what about just taking in a parameter as part of the route? Then in the activate function, you can switch on the parameter. The route values can be designated so that your url is relevant, like [http://site/page/subtype], where subtype is the parameter (instead of using numeric values)
Regarding inheritance, depending on the features you need, there's so many ways to do JavaScript inheritance it can be a little confusing. There are some full-featured inheritance models provided by libraries such as base2 and Prototype. John Resig also has an inheritance model that I've used successfully.
In general, I prefer to stick to simpler solutions when it comes to JS inheritance. If you need a pretty much the full set of inheritance features, those libraries are good to consider. If you only really care about accessing a set of properties and functions from a base class, you might be able to get by with just defining the view model as a function, and replacing the function's prototype with the desired base class. Refer to Mozilla's Developer Docs for good info on inheritance.
Here's a sample:
//viewModelBase
define(function (require) {
"use strict";
function _ctor() {
var baseProperty = "Hello from base";
function baseFunction() {
console.log("Hello from base function");
}
//exports
this.baseProperty = baseProperty;
this.baseFunction = baseFunction;
};
//return an instance of the view model (singleton)
return new _ctor();
});
//view model that inherits from viewModelBase
define(function (require) {
"use strict";
function _ctor() {
var property1 = "my property value";
function activate() {
//add start up logic here, and return true, false, or a promise()
return true;
}
//exports
this.activate = activate;
this.property1 = property1;
};
//set the "base"
var _base = require("viewModelBase");
_ctor.prototype = _base;
_ctor.prototype.constructor = _ctor;
//return an instance of the view model (singleton)
return new _ctor();
});
Keep in mind this example all results in what effectively is a singleton (i.e. you'll only get the same instance, no matter how many times you require() it)
If you want a transient (non-singleton) just return _ctor. Then you'll need to instantiate a new instance after you require() it.
One more note, in general, functions should be defined on the prototype, not within the constructor function itself. See this link for more information on why. Because this example results in only a single instance, it's a moot point, so the functions are inside the constructor for improved readability and also the ability to access the private vars and functions.

Categories