Lets start by writing our own very simple module so we can get an overview of how they work and are used. You need two files for this so go ahead and navigate to the directory you want to use for this tutorial and create the two files.
The module we are going to write is going to be very simple and just console.log 'Hello', not very original I know. So in hello.js lets simply
Now we have written the simply console.log lets require it in our app.js
Require is a core node function that takes a string which represent the location of the file we want to use. The './' simply means in the same directory as the current file. app.js and hello.js are in the same directory in our case, so we use ./. Now if we invoke the hello function in app.js what do you think will happen?
Go ahead and run this using
Lets wrap our console.log in a function expression.
Now in our hello.js lets export the module using module.exports.
What do we do then if we want to have our own hello function in app.js? Well lets start off by writing that function.
What do you think is going to happen here? What happens here is we overwrite the requires hello function with the hello function in app.js. We haven't overwritten the hello function in hello.js only the variable that returns this function. To fix this all we have to do is rename the require variable and we can use both of the hello functions in the same file.
And now we see that we can use both of the functions with no conflict.
So all you have to do to make a files function available to another file is module.export that function and require it in another file. But what is really going on in Node when we do this?
We are about to go deep into Node here so hold on tight and keep yours arms in the vehicle at all times! Lets step through the program with a debugger. I'm using Visual Studio Code, it's free and runs on both Windows & Mac.
You can see here that when the require is called we have lots of variables that are made avaliable to us. You can see we have the directory name, the filename, exports which is an empty object, the hello variable itself, module, require which is a core node function and this.
When we step inside the require function we head into the Node core, into a file called module.js. You can see here that require is just a function that takes a path as the argument. This require function just returns what comes back from the real require function. This is exactly how we use require in our app.js.
You can see here that require is a prototype of Module. So every module you create gets an object created to represent it and that object has access to a require method through prototypal inheritence. This require function is just a wrapper for the load function, you see that in line 365.
Lets step into that load function.
This is where we can start to see what is happening. You can see that the filename variable is being set here in line 286. There is a lot going on here which we don't need to know about so let's move on.
You can see here that the object that represents my hello module is being created here with the new constructor.
Now we have this module object we can see that it has the exports object which is an empty object. So when you use module.exports, you are really asking the module object to give you its exports property. This is the same way you access a functions name using the .name that we saw in my article about first-class functions.
Going further on we are actually going to try and load that file. In our case hello.js. Remember that the filename variable was set eariler.
With our file loaded lets move on a bit more.
and Node will assume we mean hello.js. This does mean however that if we want a non-js file we will have to add .json etc. You can start to see that what is going on in Node isn't all magic.
Now we get to the point where we are going to compile our code through Google's V8 engine. We can see that compile takes in a content variable which you can see contains our code.
If we go into the compile function we can see that eventually our code is put into a wrapper. This allows Node.js to do things to it. So Node dosen't just pass our hello function to the V8 engine it pass a lot more which is what allows use to use Node's awesome features!
If we move on into the wrapper.
We can see what's going on. The script variable is our script from hello.js. The NativeModule.wrapper is the string that is highlighted and NativeModule.wrapper is the rest of the string. This is what gets passed to the V8 engine. A string with our code in it. The string has a function which contains our code.
This means that EVERYTHING we write in Node is run in V8 inside a function.
If we keep going eventually we get into where our code is run. At this point module.exports still doesn't equal our code. But we now have the return of our code.
But if we keep going we will get to this point.
Where module.exports is being returned. The exports property on the module object, because remember module is an object with an exports property. This is the thing that is being changed when we call the require function.
And eventually you get to the point where require has finished. It's gone and got our hello.js, loaded the contents, wrapped it, run it and then returned it on the moduel.exports property which is our function.
So we now run the hello modules function.
And now we will see
Take a deep breath as we have done a lot here. Essentially what happens is Node changes what module.exports returns! All require does is give you back whatever you change module.exports to. See it really isn't that difficult!
You pass a path to the require function
The path being your file which will contain the code you want to use
module.exports is what the require function then returns
Which is what allows you to use the code in your file