Wednesday, January 25, 2012

Simple Intro to NodeJS Module Scope

Update: Here are slides for this talk at OttawaJS: "Node.JS Module Patterns Using Simple Examples".

Update 2: More Node.JS Module Patterns - still fairly basic but more practical examples.

People often ask about scope and visibility in Node.JS modules. What's visible outside the module versus local to the module? How can you write a module that automatically exports an object? And so on.

Here are six examples showing different ways of defining things in Node.JS modules.  Not all of these are recommended but they will help explain how modules and their exports work, intuitively.

Exporting a global function

This module defines a global function called foo. By not putting var in front of the declaration of foo, foo is global. This is really not recommended. You should avoid polluting the global namespace.

// foo.js
foo = function () {
  console.log("foo!");
}


Here is an app that uses it.

// Module supplies global function foo()
require('./foo');
foo();

Exporting an anonymous function

This module exports an anonymous function.

// bar.js
module.exports = function() {
  console.log("bar!");
}


Here is an app that uses it.

// Module exports anonymous function
var bar = require('./bar');
bar();

Exporting a named function

This exports a function called fiz.

// fiz.js
exports.fiz = function() {
  console.log("fiz!");
}


Here is an app that uses it.

// Module exports function fiz()
var fiz = require('./fiz').fiz;
fiz();

Exporting an anonymous object

This exports an anonymous object.

// buz.js
var Buz = function() {}; 

Buz.prototype.log = function () {
  console.log("buz!");
};
module.exports = new Buz();


Here is an app that uses it.

// Module exports anonymous object
var buz = require('./buz');
buz.log();

Exporting a named object

This exports an object called Baz.

// baz.js
var Baz = function() {}; 

Baz.prototype.log = function () {
  console.log("baz!");
};

exports.Baz = new Baz();


Here is an app that uses it.

// Module exports object Baz
var baz = require('./baz').Baz;
baz.log();


Exporting an object prototype

This exports a prototype called Qux.

// qux.js
var Qux = function() {}; 
Qux.prototype.log = function () {
  console.log("qux!");
};
exports.Qux = Qux;

Here is an app that uses it.

// Module exports a prototype Qux
var Qux = require('./qux').Qux;
var qux = new Qux();
qux.log();

11 comments:

Anonymous said...

Good one.. citing many examples..

Dan McHarness said...

Nice. For this node newb it's the best demonstration of the difference between module.exports vs exports that I've come across.
Thanks

Anonymous said...

What is the difference between module.exports.something and exports.something

Darren said...

exports is a convenient alias for module.exports that "just works" most of the time. The key difference in the examples here is exporting "named" vs "anonymous" things. Since exports is an alias for module.exports, assigning named properties on either is equivalent:

exports.foo = "foo";
module.exports.foo = "foo";

Assigning a named property, exports dot foo, will not overwrite the original "exports" alias. However, when you want to export an anonymous function or object, you must be careful not to over-write the exports alias. Assigning anything directly to exports:

// badfoo.js
exports = "foo";

will overwrite "exports" so that it is not an alias for module.exports anymore. So in order to export anonymous things, you have to explicitly say:

// goodfoo.js
module.exports = "foo";

You'll sometimes see this line used in modules as way to preserve the exports alias:

exports = module.exports = Thing;

This could be useful if you want to export an anonymous object prototype, but that's a pattern I didn't include here.

Unknown said...

Bad post. You don't understand Node module scope. What you did was totally unecessary. When you define variables outside module.exports in a .js file they are NOT global in node. You can't even reference those variables. You can only reference what's inside the module.exports so it's completely normal to just define your variables and functions outside the module that you don't wanna expose.

Darren said...

Hello "We Do TDD". It's a fairly common misconception with Node newcomers that modules are isolated from the global scope. I'm not sure where that idea comes from, but it simply is not the case. If you try the simple example, you'll find that it is indeed possible to define a global in a node module. That said, it's not something that you should be doing.

Darren said...

PS. The difference between specifying something in the global scope or local scope is the missing `var` keyword. If you declare a variable of function without it, it is declared in the global scope. This is the case in node modules as well as in the browser.

Ole said...

These examples are good! But I'm missing some examples of passing parameters to module...
Could you please post that?

Darren said...

Good idea! That's a nice way to extend an object or do dependency injection. I'll think of some examples.

Darren said...

@Ole I posted More Node.JS Module Patterns with an example of passing in properties to the module.

AnĂ´nimo said...

This helped me, because it made me realize that functions can be made global like that in modules. I didn't realize it before and stepped into a bug.

Productivity and Note-taking

I told a friend of mine that I wasn't really happy with the amount of time that gets taken up by Slack and "communication and sched...