Skip to content

Instantly share code, notes, and snippets.

@pmuellr
Created December 15, 2013 16:43
Show Gist options
  • Save pmuellr/7975152 to your computer and use it in GitHub Desktop.
Save pmuellr/7975152 to your computer and use it in GitHub Desktop.
// module a
b = require("./b")
module.exports = function() {
console.log("I'm in module a")
}
b()
// module b
a = require("./a")
module.exports = function() {
a()
}

In node.js, you need to be careful when you export functions out of a module using the module.exports = style of export.

Try re-creating the two files a.js and b.js on your computer now, and then run node a. You should see an error like the following:

b.js:4
    a()
    ^
TypeError: Property 'a' of object #<Object> is not a function
    at module.exports (b.js:4:5)
    at Object.<anonymous> (a.js:7:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:901:3

How does this happen? Let's walk the steps.

Node loads module a because you specified loading it on the command-line. Internally, node creates a new empty object {} for the exports for the object; this object will actually be the value of the exports variable when module a is run.

The first thing module a does is load module b. So, let's move to module b.

Again, node will create a new empty object {} for the exports for the module. The first thing module b does is to require module a. Node is actually still in the process of loading module a - it hasn't finished - but it did already create the exports for module a, and so it returns them. At this point, the variable a in module b will be set to the new empty object {}. When a is invoked in the exported function below this, the error will result.

You can see in module a that it's supposed to be exporting a function. The problem is that this isn't done before module b does a require() on a, so b will end up getting access to the empty module export initially set for a.

@pmuellr
Copy link
Author

pmuellr commented Dec 16, 2013

I'm trying to remember, in some of my old, early CommonJS experiments, if I had code to handle this. I thinking the best I could do is provide a warning, in cases where I recognized mutually recursive require()'s. Not helpful, because you don't want to see this "warning" all the time, so you would need to have folks opt-in for it, and then no one is going to use it.

I guess it's moot if no one ever sees this in practice. I will note that when I started seeing this myself, I was often confused as to what was going on. So even if it's a rare problem, it's a difficult to diagnose one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment