Expose Global Variables, Methods, and Modules in JavaScript

In this article, you will learn how to expose methods, variables, and modules in JavaScript to the global namespace by using the browser’s window object. Furthermore, a setup to expose methods and modules with Webpack is presented.

In this article, I will show you how you can expose methods, variables, and modules in JavaScript to the global namespace via import/export statements and by using the browser’s window object. Thereby, methods can be called by other applications or even from other code languages. Furthermore, I will briefly talk about the module bundler Webpack and how to achieve the same goal.

1. Foreword

Recently, I had the pleasure to refactor a desktop application. The application is written in HTML, CSS, JavaScript, also using some jQuery to manipulate the DOM. The application runs in your browser which is started from Java via the SWT Browser widget.

Besides that the application was almost not documented, there is only a single file for the whole HTML code, one file for all CSS style declarations, and – guess what – one file containing the complete JS methods. If you put all your JavaScript methods into one file and import this file in an HTML document via <script type=”text/javascript” src=”scriptFile.js”></script>, all your methods will reside in the global name space. This will be an important issue later-on.

Initially, I was hired as the frontend guy and UI designer to implement some new features. However, they were open for new input and so I began by refactoring the code base. I am not going into much details here. Let’s just say that I refactored the whole thing into modules according to domain-driven design using Webpack to bundle everything into one shippable distribution package.

Furthermore, I introduced concepts from functional programming, such as map, reduce, and filter operations in exchange for for-loops. Interestingly, when you start refactoring, I often encounter the phenomenon that you end up with less code offering the same or more functionality.

2. Refactoring: the opportunity to improve

“Always leave code better than you found it.”

That is my motto when I start working on a new project. Therefore, I split this big mess up into a bunch of packages and files which each do one thing, following the Single Responsibility Principle.

However, now methods are only visible for other methods located in the same module. To make them usable for other methods in other files, we have to export them and import them in the other file. We achieve this by

export function funcToExport() {...}

Besides, you can use the export statement to export functions, objects or primitives. Afterwards, we import our method by

import { funcToExport as alias } from "moduleName";

Export and import of variables works the same way:

export let foo = "Test";
import { foo } from 'moduleName';

So, now we know how to import methods and variables from other modules.

However, there is still one problem. The functions are not visible in the global namespace. Thereby, we can not access them from outside our application. Just think of a library where you want to expose certain methods which should be visible to the outside world.

3. Calling Methods From Other Applications

The outside world in my example is the Java application which resembles the backend. It starts the JavaScript application from Eclipse via the SWT Browser widget. A new browser instance is created by

import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;

Browser browser = new Browser(parent, SWT.NONE);

Afterwards, we can call methods and load URLs on the JavaScript side via

browser.setUrl("http://www.matthiassommer.it");
browser.execute("setJavaPresent();");

The problem is that methods have to be visible globally, which they were in the beginning when we had them all put together in one .js file. One solution is to make them visible via the browser’s window object.

3.1 The Window Object

I am pretty sure, you have used the window object before, although you might not have thought about it. It represents the browser’s window. Every global parameter, global function, and each DOM element is a property of the window object. To place a function in the window namespace, we make a simple assignment.

window.setJavaPresent = setJavaPresent;

Pretty easy, right? You can also create objects and put several function definitions in it. This is the preferred way, as you do not pollute your namespace and minimise the propability of naming conflicts.

window.packageName = {
   func1, 
   func2,
   func3
}

You can also call a function: window.packagename.func1

Let’s check out that this is actually working. Open Chrome and press Ctrl+Shift+J (Windows / Linux) or Cmd+Opt+J (Mac) to start the DevTools Console. Enter the name of the function or window.functionName(without brackets). You will see the function declaration in the console.

4. Bundle your app with Webpack

For the project I was hired, I proposed to use the Webpack module bundler. It is a modern way to bundle everything your application consists of into one place. Finally, the distribution folder for my project looks like this:

- images
- fonts
index.html
styles.css
bundles.js

My application is configured so that all JavaScript modules are bundled into bundle.js, and all CSS styles are bundled into styles.css. The entry is index.html, where all HTML code resides. You may think “what is the benefit to the previous unrefactored condition?”. But wait a minute, I didn’t tell you about all the benfits you get by using Webpack!

Webpack can be extended via plugins. However, be careful when you look up Webpack related issues because version 1 and version 2 have different notations for the configuration and most tutorials and Stack Overflow discussions are for Webpack 1. At the moment, the newest version is Webpack 3.0.

Let’s have a look at some additional features you get through Webpack:

  • ES 6 support via Babel (transpiled to ES5 for Browser support)
  • TypeScript support
  • Sass and Less support
  • Plugins to reduce the file size for production (such as UglifyJsPlugin)
  • ESLint to enforce a common coding style and to detect code issues
  • Node Package Manager (NPM) for package management

4.1 Expose modules and variables to the global namespace

Previously, I showed you a way to expose methods and variables globally in the browser’s window object. However, we would like to let Webpack handle this. I had to search for a while before I came up with a solution. To expose modules, we can use the expose-loader. To expose global variables, we use the export-loader.

4.1.1 Expose modules with expose-loader

The expose-loader is an addon to Webpack, which adds modules to the global object. If you are using yarn for dependency management, you can install the expose-loader package via

yarn add expose-loader –dev

Alternatively, you can use the npm installer (Node Package Manager) via

npm i expose-loader --save

You can expose any function with

require(“expose-loader?package!functionName”);

and call it via package.functionName.

Coming back to our previous example, we make setJavaPresent() globally available by
require(“expose-loader?setJavaPresent”);
If you enter the method name in the developer console in Chrome it should show the function definition. You can also try window.methodName which results in the same outcome.

4.1.2 Expose variables with export-loader

First, install export-loader with

yarn add export-loader –dev

In order to export a method globally, you have to add a new rule to your webpack.config.js:

module.exports = {
  module: {
    rules: [{
      test: require.resolve("moduleName"),
      use: 'exports-loader?setJavaPresent'
    }]
  }
};

At last, you add a statement to the module where the function is defined.

exports["setJavaPresent"] = setJavaPresent;

4.2 Configuring ESLint

If you are using ESLint as Webpack plugin, it probably outputs an error message because the function is not defined: error ‘setJavaPresent‘ is not defined no-undef

To disable the error message in your project, add the following declaration to your .eslintrc.js configuration file:

"globals": {
   setJavaPresent": true
}

If you just want to disable this message for a particular file, use /*eslint no-undef: “warn”*/  at the top of this file.