Codementor Events

Understand don’t imitate. Nodejs in deep

Published Jul 17, 2019
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

  1. With functions you can do everythink you can do with other types because functions are First-Type

  2. 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']
    
  3. Function constructors are normal functions that are used to construct objects

    function Person(name, surname) {
        this.name = name;
        this.surname = surname
    }
    
  4. 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()
    
  5. 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)
    
  6. 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

  1. 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
  2. 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

  3. 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

    1. 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();
      
    2. 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!

      Imgur

      // 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(){}
      

      Imgur

      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

Discover and read more posts from Marjo Ballabani
get started