13 min read

(For more resources related to this topic, see here.)

CommonJS

CommonJS is a set of specifications, and its purpose is to give a common guide to building JavaScript frameworks. It is not a framework in its own right. Appcelerator implementing these standards to their Titanium framework brings it inline with other frameworks such as NodeJS, and enables us as developers to use the same practices in more than one framework.

The implications of this cannot be overstated. Developers can now switch between JavaScript frameworks, which have implemented the CommonJS model, without having a massive learning curve on the framework itself.

CommonJS works by using an initial bootstrap file, which in Titanium is app.js. You then abstract your code into separate modules that are required into other modules when needed.

Within Titanium, you can have native modules that enhance and expand the Titanium framework. These should not be confused with a CommonJS module, which is part of the main application.

Code formatting

How often have you needed to modify code that was written by other people or has been updated, modified, rewritten, or just generally messed around with? You find different people using different coding styles, braces on different lines, and equals signs at different positions; the list is endless. You then have to work your way through this code, getting more and more annoyed.

Titanium Studio has a solution—an automatic code formatter, which you can set up as you require. To get to the configuration settings, go through the main menu to Preferences | Titanium Studio | Formatter, select JavaScript , and click on the edit icon. You will see the Preferences panel and you can configure it as required. If you wish to have these configurations across your development team, then you can export and import them as required.

To format a file’s code, open the file, go to the main menu, and select Source | Format. Alternatively, if you are on a Mac, the relevant shortcut keys are shift +command +F. If you have selected a section of code then that will be formatted, otherwise the whole file will be formatted.

A gotcha with the formatter: If there are too many JavaScript errors it won’t format the code.

Code validation

As developers, we spend our days hunting down spurious code anomalies. It could be a missing comma, semicolon, or that an event listener wasn’t added while looping an array of objects. Anything that can make this task easier and show potential issues while we are writing code is a good thing. JSLint is one of those tools that you could use, as it highlights potential issues as you code. It has been described as your worst nightmare but also your best friend. When you first start using it, it may drive you mad with some of the seemingly meaningless warnings, but sort them out and be persistent; it will improve your codebase in the long term.

Titanium Studio has JSLint built in but switched off by default. To enable it, go to Preferences | Titanium Studio | Validation, select JavaScript , and switch on JSLint Validator. As you can see, there are other validators on the list, which also help give you a hit list of potential issues. It is worth spending a little time making sure you understand what these are and how they work.

Comment meaningfully

Adding comments to your code should not be seen as a chore, instead they can be as important as the code itself. A well-commented codebase can and will save hours in the future, as you or a colleague go to update and maintain it.

Always put a comment block at the start of source files, explain what the file does, and include a maintenance log, where the date, time, developer’s name, and a brief description of the changes can be maintained. If you have a complex function put a comment block before it, explaining what it does. Also place inline comments where needed to explain certain pieces of code.

/* * A CommonJS Module. * * This module does something … * * Author : Name * * Date : Today * * Maintenance Log * * Date : Author: * Changes : * */

Do not add comments for the sake of adding them. Make them meaningful, relevant, and useful. Too many comments can confuse the code structure.

Do not pollute the global object

Within an application you can define various application objects. Declare and use these with caution, as they use up resources and can easily cause clashes with other private and local objects. Application-level variables are not really required and should be avoided. If you require a small piece of data to move around the application, consider using persistent data or passing it to the required modules.

Application-level event handlers are required for various tasks including, amongst others, background services and geolocation. If you do use them, always remove them when they are no longer needed. To control the flow of an application, you may need to set up a global listener, but you only need one with a common function to control the flow.

In CommonJS there is no global scope; declaring a variable within a module makes it private to that module. Declaring variables in app.js as Ti.App.varName = [], does make them global, but is highly discouraged.

JavaScript instance

A JavaScript instance is where a session of the interpreter is invoked. In Titanium it is possible to create multiple instances within a single application. This is done by creating a window object with a url property to load the content.

Var win = Ti.UI.createwindow({ url: '/app/ui/newWindow.js' });

Don’t do this unless you have a very, very specific requirement. To benefit from all the advantages CommonJS provides, always work in a single JavaScript instance. The consequences of multiple instances include no scope across them, additional memory and resource usage, and a high risk of memory leaks.

CommonJS modules

With the adoption of the CommonJS specification by Appcelerator into the Titanium framework, you should only use the CommonJS modules, which are also referred to as factories. This provides many advantages: separation of code into specific modules, a more structured codebase, separate object scope, code maintainability, and much more.

By using this method it becomes very difficult to pollute the global scope, as each module has its own object scope. Understanding this is key to the CommonJS method. Each module or factory contains functions and variables that can be exported; unless they are exported they are private to that module. This enables variable or function names to be the same in different modules without a scoping clash. By exporting only what is required at the end of the module, it enables the module to be self contained.

/* A typical module format myModule.js */ var object1 = "1234"; var object2 = "5678"; /* My modules function */ functionmyModule(args){ … do something nice return mainObject; } exports.object1 = object1; exports.myModule = myModule;

When a CommonJS module is required by another module it is loaded into memory. If you then require the same module elsewhere, it doesn’t reload it into memory; it just makes it available to the new calling module. This means t hat if the first requiring module sets a value in the called module, when the called module is required by another module, the values which have been set are still set. It doesn’t load a new instance of the module and reinitialize all the values.

A few rules about modules:

  • Only load them when needed

  • Only export what is required by the calling module

  • Use prototype where appropriate

  • Avoid recursive requires

Working with CommonJS recursive requires can cause major issues and, basically, you can’t do it. A recursive require is where module A requires module B and module B then requires module A. You will quickly notice if you try this that this leads to a loop trying to process the continual call of the requires, and finally dies with a nondescript error message.

CommonJS best practices

CommonJS is one of the best things to have happened to the Titanium framework. To get the most out of the framework and the enhanced performance, these are a few things that should be considered:

  • Be modular

  • Be private

  • Return an object

  • Protect the global scope

  • Control file loading

One of the main advantages of CommonJS is the way it lends itself to creating well-structured, separated code. By being modular you create specific modules for separate sections of the code, i.e. a separate module for each window. This methodology facilitates the creation of common modules enabling a constructive codebase that is easy to understand and maintain. A good example of a common module would be one that contains the geolocation code and is then used across the whole application.

Common modules enable the code to be extracted down, but don’t go too far. It is tempting to extract code out into its own module when it actually lives in the module it is in. Do not be tempted to take code modularization to the extreme, having a module for each function is not necessary or productive. Remember that modules are loaded into memory once they have been required and they remain there.

Making functions and variables private to a module maintains the module’s integrity. Do not export all the functions in a module unless they are actually called from the requiring module. Export the required functions and variables at the end of the module, not by default. The following code example shows the two methods for exporting functions:

// This exports the function inline exports.outFunc = function () { .. code .. return object; } // This only exports the function when and if required. function outFunc() { .. code .. return object; } exports.outFunc = outFunc;

By defining the module functions in the second method, they become local to that module. This means that they can be used directly by any other function within the module. As you separate your code into modules, separate your modules into functions. Having one exported parent function that returns the main object after processing through other functions is a good practice.

It is quite easy to declare module variables; you just declare them outside of any function. This gives them a scope that is global to the module. It is a very good way of maintaining a persistent state across the calling modules, but use them sparingly as they use up more resources.

JavaScript, by default, returns the last action of a function to the calling function. This may not always be what is required, especially in the CommonJS model. Always return what you require from a function even if that is nothing.

// New window function returning the null object. functionnewWin() { // Do something return; } exports.newWin = newWin;

For an exported function always return the function’s main object. Hence, creating a new window in a function should return that window object as the following code example shows:

// New window function returning the parent object. function newWin() { var win = Ti.UI.createWindow({ .. parameters .. }); return win; } exports.newWin = newWin;

The global scope should be considered as nonexistent, but you can still add application-level variables. If you have to use these, only declare them in the app.js file; do not declare them in any of the modules. At times you will have to create an application event listener in a module. Only do this when you have to and always, always remove the listener after you have finished with it. The following code example shows an event listener added with an in-built function. This is not good practice, as you cannot remove it later. The only way it will be removed is when the parent object is destroyed, which for application-level listeners is when the application is stopped, not put into background.

function adList() { varmainObj = Ti.UI.createImageView(); mainObj.addEventListener('click', function(e) { .. do something here .. }); return mainObj; }

Instead of defining listeners with an in-built function, always declare them to a calling function as the following example shows. They can then easily be removed, as you have to declare the called function to be identical in both addEventListener and removeEventListener.

varmainObj = null; function eveList(e){ .. do something here … mainObj.removeEventListener('click', eveList); } function adList() { mainObj = Ti.UI.createImageView(); mainObj.addEventListener('click', eveList); return mainObj; }

Managing memory

We have explored some of the practices that will help in managing the application’s memory. These practices range from controlling when the modules file is loaded to not using application-level events or variables.

When you open a window, it is added to the window stack, and every time you open that window it is added to the stack. If you have a navigation system that enables you to move through the windows in any order, it is likely that you will end up with a large stack. Close the windows when they are not in use.

varwin =i.UI.createWindow(); varwinBut =i.UI.createButton({ title : 'Press' }); function loadWin2() { varwin = Ti.UI.createWindow(); win.close(); win = null; win2.open(); } winBut.addEventListener('click', loadWin2); // opening a window win.add(winBut); win.open();

Some additional memory-intensive APIs in Titanium are web views, large tables, and events. It is not a good idea to have more than one web view instance running in the application at a time. If you do not close the window containing the web view instance and move to another one, at least remove the web view instance from the previous window and reload it when focus is made. The same applies to tables and map views.

When you remove an object such as a web view from a window it may not always release the memory—even closing a window does not always release the memory. In all cases, null is your friend. The final code example shows a window with web view, an event listener, and how to clean up when a window is closed:

// clean up example module function createWin() { var win = Ti.UI.createWindow(); varwebV = Ti.UI.createWebView(); win.addEventListener('open', openFunc); win.addEventListener('close', function(e) { win.removeEventListener('open'openFunc); win.remove(webV); webV = null; win = null; } win.add(webV); return win; }

In this example the close event doesn’t call another function. This is acceptable because when we close the window, the close event will fire, which nulls the win object and this removes the object’s event listener. It is done this way to prevent having to use module variables to handle the window and web view objects being cleaned up in another function.

Summary

As you have seen, there are quite a few considerations to take into account when coding with Titanium. Some of these can also be applied to other programming languages. It is completely optional to follow best practices; they are there as a guide, as a place to start, and as a way to manage your code going forward.

As a developer you will find your own way to implement the methodology used within the application, you will decide when, where, and what comments to add, which code format to use, and which module to put what code into. But best practices and guidelines are developed for a reason; they keep code consistent within the application, they allow other developers to pick up what is going on quickly, and enable clean and reliable code.

Always apply a good coding style to your application. You will thank yourself in the future.

Resources for Article :


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here