Javascript Module Pattern Memory Footprint and Performance - javascript

I am using the Javascript Module Pattern to try and implement C# enumeration-like functionality. I have two ways that I am currently thinking about implementing this functionality but I do not understand all the benefits or advantages of one way versus the other.
Here is implementation 1:
var MyApp = (function (app) {
// Private Variable
var enums = {
ActionStatus: {
New: 1,
Open: 2,
Closed: 3
}
};
// Public Method
app.getEnum = function (path) {
var value = enums;
var properties = path.split('.');
for (var i = 0, len = properties.length; i < len; ++i) {
value = value[properties[i]];
}
return value;
};
return app;
})(MyApp || {});
// Example usage
var status = MyApp.getEnum("ActionStatus.Open");
And now implementation 2:
var MyApp = (function (app) {
// Public Property
app.Enums = {
ActionStatus: {
New: 1,
Open: 2,
Closed: 3
}
};
return app;
})(MyApp || {});
// Example usage
var status = MyApp.Enums.ActionStatus.Open;
The main difference is in using a "private" variable vs a "public" property to store the enums. I would think implementation 1 is a little slower but I was not sure if keeping the enums as "private" reduced the memory usage. Can anyone explain the difference in memory footprint and performance for the two (if any)? Any other suggestions/advice are appreciated.

...but I was not sure if keeping the enums as "private" reduced the memory usage
The opposite, if anything: You still have to have the enums object, and you have to have a function to access it.
In terms of speed, I wouldn't worry about it. The added function call won't make any real difference (I looked into it when worried about using the new forEach and such, and even on IE6 with its massively slow JS engine, it just doesn't matter).
In a couple of years, you'll probably be able to have the best of both worlds: Enums that are read-only, thanks to ECMAScript5's Object.defineProperties feature:
var Enums = Object.defineProperties({}, {
ActionStatus: {
value: Object.defineProperties({}, {
New: {value: 1},
Open: {value: 2},
Closed: {value: 3}
})
}
});
// Usage
var n = Enums.ActionStatus.New; // 1
By default, properties created with defineProperties are read-only.
In fact, you can basically have that now if you add an ES5 "shim" to create Object.defineProperties on browsers that don't yet have it natively. The "shimmed" version would create read-write properties, since only the natively-supported version can really create read-only properties, but you can write the code now and know that it will work as you like on modern browsers (about half of all web surfers currently have them) while still working, just with less robustness, on less-modern ones.
And of course, EMCAScript6 may take things further, but that's still a future thing.

Related

Truly protecting data members (of classes/IIFE variables) in JavaScript

Context
I have been tasked with fixing a big bug on the menu-edit page, which was caused by a stale element issue, caused by the HTML elements for it being rendered server-side. In my three-day fight against this bug, I got some inspiration from Angular and decided to try to make a menu state that will power everything on the page (adding/removing categories/items, and later, pagination of the modals for the adding)
Some Code
I came up with this IIFE (to be the "controller" of the MVC. Selector modals hit the add methods of this, and delete buttons hit the remove methods of this. Also, this gets passed to template-render function, which is literally the first thing hit when a modal gets popped):
/* all the categories, items, and modifiers that power this page */
var menuState = (function() {
let _categories = {
attached: [],
available: []
}, _items = {
attached: [],
available: []
}, _modifiers = {
attached: [],
available: []
}
function getExposedMethodsFor(obj) {
return {
all : function() { return obj.attached.concat(obj.available) },
attached : function() { return obj.attached },
available : function() { return obj.available }
// ... other methods that work on obj.attached,obj.available
}
}
let categoryExposedMethods = getExposedMethodsFor(_categories)
// other exposer objects
return {
getAllCategories : categoryExposedMethods.all,
getAttachedCategories : categoryExposedMethods.attached,
getAvailableCategories : categoryExposedMethods.available
// the rest of the exposed methods irrelevant to this question at hand
}
})()
OK, so what's the problem?
The problem is that this is false sense of security, it seems. When I try to XSS-test this structure alone, it fails.
I test it with three entities in _categories, all of which are attached, causing
menuState.getAllCategories().length
to return 3 and
menuState.getAvailableCategories().length
to return 0. Good news is that when I tried
menuState.getAllCategories().push('a')
menuState.getAllCategories().length
I still get three.
However, when I go
menuState.getAvailableCategories().push('b')
menuState.getAvailableCategories().length
I get 1, instead of 0 !!
Is there truly a way to lock down the other getters here?! If not, what are my alternatives?
I fixed it with Object.freeze, which I already used for refactoring the "enums" the dev before me wrote when he was working on this project. What it does is fully protect a state from any type of changes, including:
adding properties
deleting properties
modifying properties
re-assigning the object/array being "frozen"
How I use it
In the helper method, I did the following :
attached : function() { return Object.freeze(obj.attached) },
available : function() { return Object.freeze(obj.available) },
This prevents the arrays being changed from those methods, thus shutting down this type of XSS. Also, menuState was declared with const.

Replace JS object by reference [refactor working code]

tl;dr Working code is at the bottom, can it be made more elegant.
I am building a metalsmith (static site generator) plugin. Metalsmith plugins always take the form:
const myPlugin = options => (files, metalsmith, done) => {
// Mutate `files` or `metalsmith` in place.
done()
}
I have written my plugin in a functional (immutable) style (with Ramda.js) and would like to completely overwrite files with the new value. The following is conceptually what I want, but won't work because it is reassigning files to updated not manipulating the files object on the heap.
const myPlugin = options => (files, metalsmith, done) => {
const updated = { foo: "foo" }
files = updated
done()
}
I have achieved the desired functionality, with the following, but it seems inelegant.
const myPlugin = options => (files, metalsmith, done) => {
const updated = { foo: "foo" }
deleteMissingKeys(old, updated)
Object.assign(old, updated)
done()
}
const deleteMissingKeys = (old, updated) => {
Object.keys(old).forEach(key => {
if (!updated.hasOwnProperty(key)) {
delete old[key]
}
})
}
Is there a better way to achieve these ends?
There is no super elegant way to do this in JavaScript, but this is not a bad thing. The truth is, well-written JavaScript should not need a behavior like this. There is no such thing as "pass-by-reference" in JavaScript, so it is natural that attempts like yours will be inelegant.
Shall a library need a behavior like this, instead of trying to work out a "hack" to pass-by-reference, there is a much more "javascriptonic" way to do it, which is pass around a wrapper object for the desired object:
// instead of trying to use a "hack" to pass-by-reference
var myObj = { /* ... */ };
function myFunc(obj) {
// your hack here to modify obj, since
// obj = { /* ... */ }
// won't work, of course
}
myFunc(myObj);
// you should use a wrapper object
var myWrapper = {
myObj: { /* ... */ }
}
function myFunc(wrapper) {
wrapper.myObj = { /* ... */ };
}
myFunc(myWrapper);
I strongly suggest you reconsider why you really want to do this in the first place.
But if you insist, your solution isn't that bad, I like how you used Object.assign() instead of a clunky for loop to add the fields.
I should add, though, that depending on the situation you might also want to set the prototype of the object to the intended value (if for example the old object was an instanceof Date and you want to make it a plain object, you certainly need to call Object.setPrototypeOf(old, Object.prototype)).

Is it good to have unique method names across your application?

I might have the same method name across different and disparate objects:
Frame.hide = function() {
//
}
Dialog.hide = function() {
//
}
Treasure.hide = function() {
//
}
Is it good to keep method names across the application unique, such as:
Frame.hideFrame = function() {
//
}
Dialog.hideDialog = function() {
//
}
The best efforts of an IDE such as Webstorm, can't differentiate between the 3 hide() methods in the top snippet. As our application is growing bigger and bigger (we are at around 80 classes now), it is becoming almost impossible to navigate around code sometimes because method names are the same, and refactoring becomes downright dangerous.
Well, that's a matter of taste really. I personally do not like repeating object names in the functions.
So I favor :
Frame.hide = function() {}
Frame.show = function() {}
Frame.validate = function() {}
over:
Frame.hideFrame = function() {}
Frame.showFrame = function() {}
Frame.validateFrame = function() {}
Your IDE should never force you to do things which might not be the preferable way, in such cases you might want to search for another IDE (Netbeans?)

JavaScript Scope and setTimeout inside of a "class"

I have trouble to solve a scope issue. Actually I'm working on a project for an HMI browser frontend. It's should visualise variables from an automation system. For the HMI it's required that the user can switch between different pages. To solve the general process flow I have created a state machine function, which coordinates loading, drawing and interaction with user. My problem now is that I use setTimeout to call the run function (which is actually my state machine) and now run in trouble with var-scope.
Look at following code:
function frontend() {
// Public properties:
this.soundEnable = true;
// Private Properties:
var p1 = 0;
var p2 = [1,2,3];
var p3 = {a:1, b:2, c:3};
var runState = 1;
var runWait = false:
// Public Methods
// stops the state machine until m_continue is called
this.m_wait = function() {
runWait = true;
}
// continues the state machine
this.m_continue = function() {
if (runWait) {
runWait = false;
setTimeout(run, 100);
}
}
// Private Methods
function drawFrame(finish_callback) {
...<Drawing of HMI-Objects on the canvas>...
finish_callback();
}
function run() {
switch (runState) {
case 1:
this.m_stop();
drawFrame(this.m_continue());
case 2:
for(i=0; i<p3.length; i++) {
p2.push(externalObjectCreator(p3[i]));
}
}
if (!runWait) {
runState++;
setTimeout(run, 100);
}
}
// Constructor
...<code to assign public and private properties>...
// Finally call the state machine to activate the frontend
runState = 1;
run();
}
Problem is scope in run-Function. In case of the first call from end of constructor everything is ok. run can access all the private properties and manipulate them. But when it is called later on via setTimeout from m_continue or by itself I can't access the private properties. In firebug I can just see the public properties and functions and none of the private properties I need.
Using of global variables will help, but is not possible, because on multi monitor solution I have 2 separated canvas objects which need to show a separated version of the HMI - for that case I need 2 instances of frontend running parallel in one browser window.
Does anyone know a solution for that problem? I'm on the end of my knowledge and totally confused.
The easiest way will be to define your scope like. Any many renound javascript libraries also use this technique.
this.m_continue = function() {
that = this;
if (runWait) {
runWait = false;
setTimeout(that.run, 100);
}
}
Otherwise you may also use scope binding using apply
You should bind the run function in each setTimeout, since run uses this.
setTimeout(run.bind(this), 100);

Having troubles implement my app in an Object Oriented manner

I've been coding JS for a while, but I've never did anything object oriented. I usually just defined all my variables at the top, and then just used them all. I kept hearing over and over to use OO, but now I can't do what I want and I can't get any help.
Here is a fiddle, along with semi identical code:
http://jsfiddle.net/zDeAJ/1/
var App = {
options: {
/* ------------------------------------
Options (PREFERABLY DONT CHANGE)
--------------------------------------- */
baseDomain : 'google.com',
apiVersion : '/api/v1'
},
state: {
current: App.options.baseDomain + App.options.apiVersion
}
}
So doing App.options.baseDomain (or this.options.baseDomain) won't work for me. What's the usefulness of defining Application level variables if I can't define other application level values based on them? I know this is a vague question but I really don't know what I'm asking... I just have a problem in that what I was easily able to accomplish with just a bunch of variables that held not only settings, but state within my application, is not so easy with my knowledge of Javascript OO patterns....
​
Edit: Alright, this is specifically what I want to do:
http://i.imgur.com/ak5YD.png
But I wasn't aware of the limitations... so I need a way around it, which sticks as close and elegant as possible to this implementation.
You can think of your approach as creating an "Instance" object called App.
Here's a slightly different approach.
function App () {
// Save a reference to the object
var that = this;
that.options = {
baseDomain: "google.com",
apiVersion: "/api/v1"
};
that.state = {
current: that.options.baseDomain + that.options.apiVersion
};
}
var myApp = new App();
// Write the current state to the screen
document.write(myApp.state.current);
Here's the JSFiddle: http://jsfiddle.net/zDeAJ/1/
Hope this helps!
Your question is quite generic; you should be more specific about what you're trying to accomplish.
That aside, read the documentation on objects;
Taken from w3schools:
With JavaScript you can define and create your own objects.
There are 2 different ways to create a new object:
1. Define and create a direct instance of an object.
2. Use a function to define an object, then create new object instances.
Way 1:
personObj=new Object();
personObj.firstname="John";
personObj.lastname="Doe";
personObj.age=50;
personObj.eyecolor="blue";
Way 2:
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
this.changeName=changeName;
function changeName(name) {
this.lastname=name;
}
}
Not one of you answered my question.... I thought about it a little... I could use a named function inside of the literal to access it... and if I wanted (not necessary) I could even assign it back to the options object
http://jsfiddle.net/zDeAJ/9/
var App = {
options: {
/* ------------------------------------
Options (PREFERABLY DONT CHANGE)
--------------------------------------- */
baseDomain : 'google.com',
apiVersion : '/api/v1',
blah: ''
},
state: function(){
this.options.blah = this.options.baseDomain + this.options.apiVersion;
}
}
App.state();
console.log(App.options.blah);
​
JavaScript is an interpreted language. That means your code is evaluated from the inside out or the most inner expression is evaluated and passed to the next outer expression.
In your example the value of options get's evaluated first and next the value of state. The problem is that you can't access the associative array of App before it is fully evaluated, wich is not the case during the evaluation of the value of state.
EDITED
Sorry for not answering correctly. Here is a refined approach from your second:
var App = {
options: {
/* ------------------------------------
Options (PREFERABLY DONT CHANGE)
--------------------------------------- */
baseDomain : 'google.com',
apiVersion : '/api/v1',
blah: ''
},
blah: function(){
return App.options.baseDomain + App.options.apiVersion;
}
}
console.log(App.blah());
You could do the following (in JavaScript, functions are objects):
function App () {
// Save a reference to the object
this.options = {
baseDomain: "google.com",
apiVersion: "/api/v1"
};
this.state = {
current: this.options.baseDomain + this.options.apiVersion
};
}
var myApp = new App();
console.log(myApp);

Categories