Some Node.js fun facts
This repo is for education & research. Here you can find some interesting and sometimes confusing facts about javascript, node and some commons libraries.
Content
- Basic concepts:
- Performance:
- Mongo Related:
- Mongoose:
Basic Concepts
Type conversion
On javascript the type conversion could be confusing when you are starting, but there are some things you should keep in mind especially when you need to validate empty parameters.
When you make a simple comparison like the following you are forcing types to convert.
null != false //true
undefined != null //false
this is the comparison table:
equality(==) | null | undefined | false | ”” | 0 | NaN |
---|---|---|---|---|---|---|
null | = | = | ≠ | ≠ | ≠ | ≠ |
defined | = | = | ≠ | ≠ | ≠ | ≠ |
false | ≠ | ≠ | = | = | = | ≠ |
”” | ≠ | ≠ | = | = | = | ≠ |
0 | ≠ | ≠ | = | = | = | ≠ |
NaN | ≠ | ≠ | ≠ | ≠ | ≠ | ≠ |
actually this is so funny:
NaN == NaN// false
typeof NaN // 'number'
this is the best meme so far:
let example = NaN;
example === example; //false
Null is an object
In fact, null is an object unlike undefined that is type undefined.
so when you are validating things keep this in mind:
typeof null// object
typeof undefined//undefined
Floating point precision
This is an important thing to keep in mind.
Example:
0.3 * 3 //0.8999999999999999
This can cause many problems specially if your are using floating point numbers to handle money.
In javascript every number is really a floating point and because of IEEE_754 there are some rounding errors.
This is a well explained and interesting article about this. HERE
Copying Objects
As you may know copying basic types variables in javascript is quite easy.
Example:
let a = 2;
let b = a; // done!
But when you try the same way for objects you aren’t copying the objects itself, you are copying the reference.
let library = {name:"hiroki", starts:11};
let anotherLibrary = library;
anotherLibrary.name = "micron-runner"; //here you are changing also the library variable.
console.log(library.name); //output: micron-runner
So there some ways to solve this problem:
- The old way:
let secondLibrary = Object.assign({},library);
- Ecma 2018 adds spread operator
let secondLibrary = {...library};//this copy all properties from library object.
So you have 2 easy solutions for these problems depending on your current ECMA version. Also, you can create your own recursive function to copy objects but this way is easier.
Spread operators also works for arrays
let a = [1,2,3];
let b = [...a];
Optional chaining
Optional chaining also called Null Propagation Operator is useful when you want to access one deep value of an object normally you should make an if validating all sub-objects.
Also, you should know this is an experimental operator but you can use it in some context like react apps.
let name = player.session.main.username;//i need to access this.
This is the old way to check the values:
if (
player &&
player.session &&
player.session.main &&
player.session.main.username &&
) {
let name = player.session.main.username;
}
Using Null propagation operator:
let name = player?.session?.main?.username; //done!
when some property is undefined
name will be also undefined.
How to enable Optional chaining?
you can use this experimental feature using a babel plugin:
yarn add @babel/plugin-proposal-optional-chaining --dev
Sources:
- https://ponyfoo.com/articles/null-propagation-operator
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
- Browser compatibility: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining#Browser_compatibility
Performance:
Console.log is expensive
If you are using console.log
to keep information logged in production, let me tell you that you are downgrading your performance.
Adding a simple console.log
add an extra time. The simpler explanation: Node.js is asyncronous so when you add a console.log you are forcing it to make some syncs operations affection your performance.
Some data about this
I made some test using micron-runner
(a benchmark library that i made), you can find the results here and the repo here
micron test this type of things making a simple loop and measuring times.
Solution:
Use some loggers writing to a file or you can use process.stdout.write
process.env isn’t that innocent
Are you using env variables? well there is a bad way and a good way to do that.
When you make a simple process.env
you are calling a system function so it is expensive too in terms of time.
The best choice is using those variables only one time per file, maybe just in the top saving those values in a const.
const EXAMPLE = process.env.EXAMPLE;
function getExample(){
//...
//awesome code here
}
module.exports = getExample;
if you use the same thing inside the function each time you run getExample
it’ll consume an extra time.
Data here
as always i made a quick test using micron-runner
.
You can find it here
As expected before running the test, using process.env
each times consume more time.
This was a simple example but in real life, you maybe will use a huge .env file and times would be much higher.
Mongo & mongoose fun facts
this section es specially dedicated to mongoose & mongo related fun facts.
Mongoose:
Find vs FindOne
Some time ago i made a benchmark between update
, find and save
, findOne & save
& findOneAndUpdate
. The results was kind obvious but it is interesting to keep in mind.
update
is only one operation so make sense if update
is faster than making two operations find & save
or findAndUpdate
.
The really interesting thing here is that sometimes find
is faster than making findOne
.
Results
save
script makes a separated find
& save
, findOne-save
is similar but using findOne
.
check the results here and the code here.
It is quite fun that using findOne
make his job worst than a simple find
.
The quick explanation is that find returns a cursor and not actual data, but findOne yes so this cost some time.