How can I improve my javascript pattern for greater flexibility/maintainability? - javascript

Currently I load all my javascript with a pattern similar to the code below. One file would reflect a single page or component in a site. For example, if I had a contact page the associated javascript file might be:
(function () {
$(function () {
contactForm.init();
locationMap.init();
});
var contactForm = {
init: function () {
... Do form manipulation here
}
};
var locationMap = {
init: function () {
... Do map manipulation here
}
};
})();
This is fine for smaller sites but as a site grows and functionality needs to be shared across pages or certain components develop a reliance on each other (e.g. the options in the contact form might change based upon the selection location map) this pattern shows it's limitations.
My question is, how can I improve this to allow for growing complexity and reliance between modules?

Good improvement will be organizing your code in namespaces and use The Revealing Module pattern:
var contact = contact || {};
var contact.form = (function () {
// private functions
function privateFunction() {}
// public functions
function init() {
privateFunction()
... Do form manipulation here
}
return {
init : init
};
})();
but for more complex applications i suggest to look for some MVVM architecture framework (i prefer AngularJS)

You should use a module loader, like requirejs or browserify. By requirejs you have to use AMD style module definitions, by browserify you have to use commonjs style module definitions. Commonjs is not compatible with browsers by default (it is for nodejs), so you have to build browser compatible js files from it (browserify does that). In your case requirejs might be a better solution (if you are not familiar with nodejs), but I suggest you read more about both.
Another possible solution is using ES6 modules. Currently you have to build by them as well, but later they will be supported by every browser I think.

Related

How to migrate legacy JS app to modules

I have a large (~15k LoC) JS app (namely a NetSuite app) written in old-style all-global way. App consists of 26 files and dependencies between them are totally unclear.
The goal is to gracefully refactor the app to smaller modules. By gracefully I mean not breaking\locking the app for long time, but doing refactoring in smaller chunks, while after completing each chunk app remains usable.
An idea I have here is to concat all the JS files we have now into single-file bundle. After that some code could be extracted into modules. And the legacy code could start importing it. The modules & imports should be transpiled with webpack\whatever, while legacy code remains all-globals style. Finally all this is packed into single JS file and deployed.
My questions are
is there a better approach maybe? This sounds like a typical problem
are there any tools available to support my approach?
I gave webpack a try and I haven't managed to get what I want out of it. The export-loader and resolve-loader are no options because of amount of methods\vars that needs to be imported\exported.
Examples
Now code looks like
function someGlobalFunction() {
...
}
var myVar = 'something';
// and other 15k lines in 26 files like this
What I would ideally like to achieve is
function define(...) { /* function to define a module */ }
function require(moduleName) { /* function to import a module */ }
// block with my refactored out module definitions
define('module1', function () {
// extracted modularised code goes here
});
define('module2', function () {
// extracted modularised code goes here
});
// further down goes legacy code, which can import new modules
var myModule = require('myNewModule');
function myGlobalLegacyFunction() {
// use myModule
}
I'm following an approach similar to that outlined here: https://zirho.github.io/2016/08/13/webpack-to-legacy/
In brief:
Assuming that you can configure webpack to turn something like
export function myFunction(){...}
into a file bundle.js that a browser understands. In webpack's entry point, you can import everything from your module, and assign it to the window object:
// using namespace import to get all exported things from the file
import * as Utils from './utils'
// injecting every function exported from utils.js into global scope(window)
Object.assign(window, Utils).
Then, in your html, make sure to include the webpack output before the existing code:
<script type="text/javascript" src="bundle.js"></script>
<script type="text/javascript" src="legacy.js"></script>
Your IDE should be able to help identify clients of a method as you bring them into a module. As you move a function from legacy.js to myNiceModule.js, check to see if it still has clients that are aware of it globally - if it doesn't, then it doesn't need to be globally available.
No good answer here so far, and it would be great if the person asking the question would come back. I will pose a challenging answer saying that it cannot be done.
All module techniques end up breaking the sequential nature of execution of scripts in the document header.
All dynamically added scripts are loaded in parallel and they do not wait for one another. Since you can be certain that almost all such horrible legacy javascript code is dependent on the sequential execution, where the second script can depend on the first previous one, as soon as you load those dynamically, it can break.
If you use some module approach (either ES7 2018 modules or require.js or you own) you need to execute the code that depends on the loading having occurred in a call-back or Promise/then function block. This destroys the implicit global context, so all these spaghetti coils of global functions and var's we find in legacy javascript code files will not be defined in the global scope any more.
I have determined that only two tricks could allow a smooth transition:
Either some way to pause continuation of a script block until the import Promise is resolved.
const promise = require("dep1.js", "dep2.js", "dep3.js");
await promise;
// legacy stuff follows
or some way to revert the scope of a block inside a function explicitly into the global scope.
with(window) {
function foo() { return 123; }
var bar = 543;
}
But neither wish was granted by the javascript fairy.
In fact, I read that even the await keyword essentially just packs the rest of the statements into function to call when promise is resolved:
async function() {
... aaa makes promise ...
await promise;
... bbb ...
}
is just, I suppose, no different from
async function() {
... aaa makes promise ...
promise.then(r => {
... bbb ...
});
}
So this means, the only way to fix this is by putting legacy javascript statically in the head/script elements, and slowly moving things into modules, but continue to load them statically.
I am tinkering with my own module style:
(function(scope = {}) {
var v1 = ...;
function fn1() { ... }
var v2 = ...;
function fn2() { ... }
return ['v1', 'fn1', 'v2', 'fn2']
.reduce((r, n) => {
r[n] = eval(n);
return r;
}, scope);
})(window)
by calling this "module" function with the window object, the exported items would be put on there just as legacy code would do.
I gleaned a lot of this by using knockout.js and working with the source readable file that has everything together but in such module function calls, ultimately all features are on the "ko" object.
I hate using frameworks and "compilation" so generating the sequence of HTML tags to load them in the correct order by the topologically sorted dependency tree, while I could write myself such a thing quickly, I won't do this, because I do not want to have any "compilation" step, not even my own.
UPDATE: https://stackoverflow.com/a/33670019/7666635 gives the idea that we can just Object.assign(window, module) which is somewhat similar to my trick passing the window object into the "module" function.

How do I keep functions out of the global namespace when using multiple files?

Last time I built a large-scale application with JS I used require.js - which is great, but can be quite an overhead, especially if you don't want to load files asychronously, so this time I'm going without it.
This time I'm writing in pure JS and concatenating and minifying everything with Grunt (I'm a Grunt n00b here). Because I'm keeping all the functions in separate files, I can't wrap everything in a closure like I could if I was using a single file. What's the best solution to keeping all functions out of the global namespace?
I'm not sure I need a full dependency management solution, but I'd consider one if it's lightweight and simple.
If you want to do it without any dependency management tool you can for example use the Revealing Module Pattern and namespaces, simplified example:
Top/Application file
window.SomeApplication = (function () {
// Add functions you want to expose to this
this.require= function (path) { // Creates namespace if not already existing, otherwise returns reference to lowest level object in path
var current = window,
i;
path = path.split('.');
for (i = 0; i < path.length; ++i) {
if (!current[path[i]]) {
current[path[i]] = {};
}
current = current[path[i]];
}
return current;
};
return this;
})();
Some other file
SomeApplication.require('SomeApplication.SomeSubNamespace').SomeModule = (function () {
// Module code
return this;
})();
Then use your grunt concat and specify the top file first. This way you will only expose one item on the window object and your module will be accessible through window.SomeApplication.SomeSubNamespace.SomeModule.
There are a number of common and easy to use JavaScript tools for managing application-wide dependencies by either implementing the CommonJS (the specification used by require.js) or the ES2015 module specification, including (as Benjamin suggested) Webpack, Browserify, and Rollup.

How should I split up my js-files to be easy to maintain? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
How would I go about when trying to separate javascript in this manner:
This is my issue:
I have a huge js-file and in this file I have functions that is used for a products-filter. I want to use these "product-filter-functions" at several locations, but I don't want to include a huge js-file.
I would like something like:
huge.js // merely everything that has something to do with products
productfilter.js // inclusion of productfilter-functions */
and when I only want to use product-filter function I obviosly include productfilter.js only.
productfilter.js would have functions like these:
function getSelectedGender() {
...
}
function getSelectedColors() {
....
}
function getSelectedBrandId() {
....
}
If I have a function with the same name in both huge.js and productfilter.js I wouldn't know which of these function would be triggered. (I tried that and it seemed kind of random)
I could of course write new functions (with the same type of functionality) depending on which part of the site I am, but I think that would be poor design and very hard to maintain.
I'm looking for pointers here..
You could try namespacing the JS so that you can keep your function names, e.g.:
huge.functionName()
product.functionName()
instead of
functionName()
functionName()
I'm a fan of RequireJS for developing modules in a tidy manner with well-established dependencies. During building/optimizing the final distribution JS file (if chosen), only the required/requested modules will be included. So code can focus on dependencies and logical grouping, while the build tool includes the relevant modules. Larger or external modules (eg. jQuery) can also be kept external and sourced via manually script includes or loaded via a separate asynchronous "AMD" fetch.
However, even without RequireJS/AMD, modules can be used to keep JavaScript code tidy in separate namespaces. I refer to the "UMD" patterns, adapted to current need. Take this "AMD Web Global" pattern with a jQuery dependency, for instance:
(function (root, factory) {
// The if-branch can be eliminated if /never/ using RequireJS/AMD,
// but leaving it in keeps this pattern compatible with such loaders.
if (typeof define === 'function' && define.amd) {
define(['jQuery'], function (jQuery) {
return (root.foo = factory(jQuery));
});
} else {
root.foo = factory(root.jQuery);
}
}(this, function ($) {
function bar() {
// ...
}
return {
bar: bar
};
}));
Later on the created "namespace" can be used:
foo.bar()
This pattern works with/without AMD (eg. RequireJS) and will also work after combining, if such is chosen. If tool-based combining is not done, then the standard multiple script includes can be used to load the dependencies (a downside is that dependency order must be handled manually).
An added benefit of using such module patterns is it is trivial to change to a context-correct factory builder:
// In the module, exported as window.OptionManager using the above pattern
}(this, function ($) {
function OptionManager (form) {
this.getSelectedGender = function () {
return $(form).find(".gender").val();
};
}
return OptionManager;
}));
And usage:
var options = new OptionManager($("#optionForm"));
options.getSelectedGender();
With the clean distinction of code it is trivial to later combine the different modules, maintaining dependencies, as deemed appropriate - separate files, single monolithic file, several combined files - while the code-base is kept maintainable.

Javascript - accessing namespace in different files

I can do this in node.js
var module = require('../path/module')
module.functionname()
So I thought I'd like to do that in client side Javascript to organize things slightly.
So each of my files now has a namespace. So say login.js has a namespace login.
My question is, what's the best way in ECMAScript 5 to implement something alongs these lines?
What you are trying to achieve can be done with AMDs (asynchronous module definitions). Check out RequireJS for that: http://requirejs.org/
With Require.js you can basically define a set of dependencies, let them get loaded asynchronously and execute code once all stuff was loaded nicely:
require(['dependency1.js', 'dependency2.js'], function(dep1, dep2) {
console.log(dep1.functionname())
})
The dependency will then declare it's functionalities with require's define method:
define(['a/possible/dependency.js'], function() {
return {
functionname: function() { return 1 }
}
})

How should I think about my JavaScript application namespaces?

I'm building an app that has a single global namespace like so:
var myApp = {};
I then have a bunch of different reusable "modules" comprised of models, views and controllers.
//Bar chart module code
org.example.chart.bar.module
org.example.chart.bar.model
org.example.chart.bar.view
org.example.chart.bar.controller
I also have a big dataSource singleton and a dataManager for loading data to the dataSource:
org.example.data.dataSource
org.example.data.dataManager //populates the dataSource with CSV data
And finally translation strings and settings that should be available across the app:
org.example.static.translations
org.example.static.settings
How would you (re-)organize this so that I have easy access to the application level singletons (such as dataSource, dataManager, translations etc.) and can easily instantiate reusable modules that get scoped under the current application instance?
(Would you for example, already from the beginning, use the same namespace for your "classes" and your app? Or would you perhaps make references like so: myApp.translations = org.example.static.translations?)
No we don't namespace. We write modular code and we use module loaders.
Example of a module loader would be require.js or browserify or seajs.
And an example module would be something like:
(function () {
var jQuery = require("jQuery");
var chart = require("chart");
...
define("moduleName", moduleObject);
})();
There is nothing stopping you adding another name to the class. For example.
org.ds = org.example.data.dataSource;
then you can call
org.ds.getDatasource();
instead of
org.example.data.dataSource.getDatasource();
but both will still work.
EDIT: You could also create other simpler functions that call it taking it out of the oo structure
var dataSource = function () { return org.example.data.dataSource.getDatasource(); };
Consider using something like RequireJS for organizing your modules.
Some excellent resources from Addy Osmani :
http://addyosmani.com/largescalejavascript/
http://addyosmani.com/blog/large-scale-jquery/
http://addyosmani.com/resources/essentialjsdesignpatterns/book/

Categories

Resources