Understand don’t imitate. Nodejs in deep
Nodej in deep
This article is for those who have basic knowledge of javascript
Javascript aside
Javascript thinks you should know to make sure you understand nodejs properly
-
With functions you can do everythink you can do with other types because functions are First-Type
-
Objects are a collection of name/value pairs
// creating an object var person = { name: "Jon Doe", age: 22 } // you can call in two ways person.name; person['name']
-
Function constructors are normal functions that are used to construct objects
function Person(name, surname) { this.name = name; this.surname = surname }
-
Inheritanc in javascript: One object gets access to the properties and methods of another object
Inheritance in javascript is prototypial: remember that every object has .prototype as property
// we can add methods to the previos object in prototype, because by default it has .prototype as property Person.prototype.say = function(){ console.log(this.name + " " + this.surname) } // now each object created from Person has it's properties and methods and this is called inheritance var jon = new Person('Jon', 'Doe') jon.name; jon.say()
-
Objects are passed by reference
var a = { name: "jon" }; // so when we pass it to another object it points at the same // place in memory, so any change made to a will cause changes to because var b = a; // now b.name is equal to 'john' a.name = jane // we see that b. name never changed but as a changed // this cause changes to b because are different object pointing at the same spot in memory // with other words are the same object console.log(b.name)
-
Imediataly invoked functions are functions that are invoced at the moment they are created
Everythink inside the function is scope of this function, in other words they are isolated to this function
var name = "Jane" // immediat function (function(){ var name = "Jon"; console.log('I invoked my self !') }()) // name inside function is different from name outside it, it is isolated console.log(name); // will print Jane
Node.js
-
V8
- V8 is a C++ library that can run stand alone or implemented in another c++ project
- V8 purpous is to take a file writed in javascript and compilie it to machine code
- Any program that implement v8 can extands it's featur
-
The Node core
-
NodeJS is a c++ program that implements v8 and added new features
// inside node c++ #include "v8.h"
-
Inside Node are also .js files that are the node js core
-
-
Modules, Exports and Require
-
Module
A reusable block of code who's existence doesnt accidentally impact other code
// example //-------------------------------- test.js function hello(){ console.log("Hello!") } // module.exports tell what is avaliable from this module module.exports = hello //--------------------------------- app.js // using test.js module var hello = require('./test') hello()
-
require and exports
-
When node execute a file or you require a file node creates a Module object that is part of node core
//you try to require somethink var a = require('./something') // Now lets see what really happen inside onde to make this avaliable //module.js in node core libraries [..] // path is path to your file in this case ./something Module.prototype.require = function(path) { assert(path, 'missing path'); assert(typeof path === 'string', 'path must be a string'); return Module._load(path, this, /* isMain */ false); }; [..] //script is the code you rite to your file NativeModule.wrap(sript) { return '(function (exports, require, module, __filename, __dirname{ '+ script + '});' } // than this code is compiled throw v8 engine [..] var module = new Module(filename, parent) [..] // return whatever you exported in your file return module.exports;
As you can see your code is not immediataly compiled but it is wraped inside a function that has module, require etc as params
and than compiled that function and than executed. That is why everythink is isolated because your code in fact is boddy of a function
that is than compiled. And this function has require, module etc as params and that is why those thinks are avaliable in your code.
This Module object that is created is saved in an array of object that node use to keep track of your modules -
Require is a function, that you pass a path too
-
module.exports is what require function returns
-
This works because your code is actually wrapped in a functionthat is given this things as function params
-
As i mentiond every module you require is saved in an array calld _cache
This is done to keep track of modules you create
and if you call the same module several times in fact it will not
execute your code again but will return the code you executed before.
So you will get the same module.exports as the first require// inside module.js in node core // as you can see if your module is required once it is saved // in cache so when you require it another time will return that done var cachedModule = Module._cache[filename]; if (cachedModule) { return cachedModule.exports; }
Be careful of what you export from your module now that you know this
-
-
Module patterns
There are some patterns that your module can use to export. There is no right way to do it
it is all about architectual choise.So you can use each of this depending on your choise.// export a function module.exports = function() { console.log("i exported a function"); } /* usage */ var a = require(./myModule) a(); //--------------------------------------- // exports an object module.exports.hi = function() { console.log("i exported a function"); } /* usage */ var hi = require(./myModule) hi.hi(); /* or */ var hi = require(./myModule).hi; hi(); //--------------------------------------- // exports object created from function constructor // this is dangerous because if you require it another time // it will return the same object so any changes anywhere // will cause changes everywhere you required the same module function Person() { this.name = "Jon"; this.say = function () { console.log("Hi from " + this.name) } } module.exports new Person(); /* usage */ var person = require(./myModule) person.say(); //--------------------------------------- // exports a function constructor // this will solve problem we mantiond before as you manage to create // object every time function Person() { this.name = "Jon"; this.say = function () { console.log("Hi from " + this.name) } } module.exports = Person; /* usage */ var Person = require(./myModule) person1 = new Person(); person1.say(); //--------------------------------------- // exports only a part of module // we expose only the function // so we are sure that anyone who call this module // cant change the name variable var name = "Jon"; function say() { console.log(name); } module.exports = { say: say } /* usage */ var a = require(./myModule); a.say();
-
exports vs module.exports
//remember when we saw how node wrapped our code into a function and its first param was export (function (exports, require, module, __filename, __dirname{ ...
exports infact is a shorthand of module.exports they are different references pointing at the same object
you can use both but be careful!// as we sou in node core module.js //when we create a module what is returned is module.exports [..] //module.js // return whatever you exported in your file return module.exports; [..] // but if in your module you say // you are breaking the reference // and what will be returned is an empty object {} because //that is what Module object return andit is empty because we never added anything to id exports = function(){}
right way to use this is to mutate it not to change reference but to mutate it
exports.name = "Jon" // so now exports.name = module.exports.name = "Jon" // we used it the right way
You can use both exports and module.exports but be careful when you use exports. In my opinion you should use always module.exports
-