This Presentation travels back in time for Production-ready JS.
in that process it covers
1. Global Scope Pollution
2. IIFE
3. Closure & Scope Chain
4. Bundles
5. Prototype Code for Webpack internals
Hello Everyone, When we say production Ready JavaScript in 2020, the word that strike our mind is
Webpack
In this video we are not going to learn about Webpack, instead
We are going to build a basic bundler from scratch.In order to that, let travel two decades back
We are in 2000, hmmmm, what does it mean ? when we say production ready JavaScript in 2000.
File Size ….. Why ?Let address that with an example
Back then Web Application is Purely back end work, JavaScript was primarily used as code snippets.Here is an example , A HTML page,Which displays current data and time, when we click the button.
We developers want to keep the code clean by abstract the logic from HTML
How do we do that ?
Let move the logic from INLINE to Script Tag
We defined the logic in a function inside script tag
And we subscribed that function for button click
Cool. This looks clean, How about taking this logic to a separate file ?
Here we moved this logic to a separate file myscripts.js
Aha, Now we had to worry about our javaScript file Size.
Why ?
When our browser request for this file, loading time of this file depends on its size
Here our browser makes a request
Server responds back with a copy of this Javscript file.
Which is stored in
Browser Run time Memory.
So file Size matters in terms of download time and Client Memory.
Let see this with an example
Hers is our file, who size is
100 Bytes
To reduce our file size, lets reduce the length of our function name and variable name here
Here we replaced function name ‘displayDateAndTime’ with ‘f’
And variable name ‘date’ with ‘e’
Awesome, what else we can do here?
hmm….. lets remove white space and trailing spaces,
And check our file size again
Wow, we did it …We could bring down our file size to 75 bytes from 100 bytes.
This Minified File is our Production Ready Javscript
Did it end there ? No
Now
We want to abstract each logic to its respective file,
So our next issue is handling multiple files.
What we did here is, we combined both the files as a single file
And minified them
And now our production ready javaScript is Combined and Minified File
Awesome now we are in 2015, we successfully created production ready javaScript.
Wait, what happened now.
The JavaScript community too grown now
, new features started coming to Javscript language.Wait does that mean all our client machine needs to update to latest browser ?
Wont that be stupid, do I need to ask my grand parents to install latest browser?
Are there any other solution ? …….. Yes itLet start with an example to address this
Hers is an example with two new javaScript features
Default value for Arguments
New Syntax letThese two new feature will fail in my grandparents browser.
To fix that, we need to rewrite the same logic in old feature
we moved onlyTime argument as variable inside the function with default value fale
We replaced let with var.
This we call as transpillers
Babel is one of the most actively used tranpilers tools.
Now
We combine files
Into a single file
Traspile them to common version
And finally we minify them
Now our Production ready JavaScript is Combined, Transpilled and Minified
With more and more logic moving to JavaScript.Developers started facing their biggest nightmare,
Global Scope Pollution
Let me explain .
When our file gets executed by JavaScript engine
They start creating new property to window object. Wait …Why this would be problem ?Imagine, we have 10 files, and each file has 4 function,
Developers had to make sure, they have to create unique-Names for each variables
And if it’s a distributed team of developers, a team might end up overriding a function with the same name
This will be a nightmare to debug and track the problem.HmmmDo we have solution ? Yes of course,
IIFE … Pattern,
Wow
what is that ?
let say we have two files file1 – has getdate Definition
File2 – has displayDateTime definition
I want everyone to closely follow from here,
Hope everyone could see this faded abbreviation of IIFE, in the topI am going to reveal each word in this abbreviation with its respective step in code,
Let start with the first step
Wrap our code with an anonymous function .
Hmm
Why Anonymous function ?
When our javaScript engine execute this function definition , they don’t create new property in our window object., why ? because this function don’t have a name,
Wait wait, when there is no function name how are we going to invoke it,
hmm, you will get your answer in next step
Wrap our anonymous function with an expression.
Why ?
Remember we had to invoke this Anonymous function,What we had was function declaration ,
if we invoke them JS engine will throw you an syntax error,
So we have to convert the function Declaration to an Function Expression
There are several tricks to achieve that,
But we will go with brackets
From now on I am going to address each file as Function ExpressionNow that we understood the purpose of expression, lets utitlize it,
Let invoke in next step
There you go, you got the purpose and meaning of each word in IIFE Pattern;
Immediately invoked function ExpressionOk what does this achieve ? How does this solve our global pollution issue ?
When JS Engine execute: invoking Function Expression, your code inside the expression get called
So variable in our code will be defined inside the inner scope of this Anonymous function expression.
Not at the global scopeSo what we achieved here is separate inner scope with IIFE pattern;
Means Our JavaScript programs are split into separate scope on its own.
This is called module. … When we invoke a function expression we end up creating module.
This might make you think, When there are not defined in global Scope, how these Function Expressions are going to communicate ?
That’s what we are going to solve next
In our Example , Function Expression 2, calls the function getDate(), which will throw an error .Why ? 1. getDate is not defined in diaplyDateAndTime
2. If we check In its outer scope – which is an anonmynous function, - not defined there either
3. If we check in outer scope of anonmynous function which is the global scope, window object, not defined there too, so its throws an error
By the way This is called scope chain .
Function with scope chain is called Closure …Now we can say, getDate is called inside the closure of displayDateAndTime.
Now what we see here is a dependency issue,How do we solve this?
What we need here is a way to export variable or functions or constant outside a Module
And Import in the Consumer when needed.
In this Example
We need a way to export getDate out of function Expression 1
And import getDate function inside function expression 2.
In general What we propose here is , every function Expression should have the ability to export and importlet resolve that next.
let pass two arguments to each Function Expression,
exports to export its variable
Require to import variables
Let make exports to be an object which hold the variables , functions or constant that needs to be exposed
Since Export being an Object, inorder to consume this Object, we need a getter. Yes your guess is Right … require will be our getter function,
Which will give us the exports object
Here we go
Hmmm, What will be the basic requirement for a getter function
KEY ---- in our case, since file path are unique , we will make file Path as our key
Now Where does this require gets defined ?
Let call that as module Manger,Before Implementing Module Manger, let see the purpose of module manger
Say we have an Object
Whose key is file path
And its value being an Function Expression
Code inside that function expression will be you file
As minified version
Now for file 2
In file 3 , we Have three functions that are exported
createButton - which adds button to your page
createPara – which add Para Element to your page
addListener – which attaches an eventListener for Button Click Event
Now index.jsWhich exprts three function from file 3
And Invokes them
Our Purpose here is to convert this Object of Function Expressions into an Object of Exports
Now Implementation Lets start by creating an Anonymous FunctionWhy ? We don’t want to be part of global Scope This Function takes in Function Expression Object
And now we need ModuleExports Object to stored the exports returned by invoking respective Func Exp
Now we get to the meat, Definition of require
First step let check in module exports Object,
If it not there
Create an empty Object named exportsNow we need to populate the exports oBject for that fiepath
How?
Get the function Expression for that filePath
And Invoke them
This will populate the Exports Object
Now return that exports ObjectNow where and How do we invoke this require function, to populate ModuleExports Object
We need to call inside our Module MangerWe need a FilePath that needs to be the Starting point of our function Expressions Object
Lets name that as entryNow we need to Invoke this module ManagerHow ? …. Yes your guess is right
Wrap them with Expression
And We need to invoke them with our Function-Expression Object
And next we need an entry point
In our case, index.js fileLet's do a code walk through to understand better
In our case, index.js fileLet's do a code walk through to understand better
In our case, index.js fileLet's do a code walk through to understand better
In our case, index.js fileLet's do a code walk through to understand better
var moduleManger = (function (funcExpressions, entry) {
var moduleExports = {};
function require(id) {
var exports = moduleExports[id];
if(exports){
return exports;
}
exports = moduleExports[id] = {};
var funcExp = funcExpressions[id];
funcExp(exports, require);
return exports;
}
require(entry);
})
var fileAsFunExpressions = {
"./file1.js": (function(exports, require){
function f(){
return Date();
};
exports['getDate'] = f;
}),
"./file2.js": (function(exports, require){
var g = require("./file1.js").getDate;
function d(p){
p.innerHTML = g()
};
exports['displayDateTime'] = d;
}),
"./file3.js": (function(exports, require){
var d = require("./file2.js").displayDateTime;
var b = document.body;
var bt, t, p;
function cb(){
bt = document.createElement("button");
t = document.createTextNode("Display Time");
bt.appendChild(t);
b.appendChild(bt);
};
function cp(){
p = document.createElement("p");
b.appendChild(p);
};
function a(){
bt.addEventListener("click", ()=>d(p));
};
exports['createButton'] = cb;
exports['createPara'] = cp;
exports['addListener'] = a;
}),
"./index.js": (function(exports, require){
var me = require("./file3.js");
me.createButton();
me.createPara();
me.addListener();
})
}
moduleManger(fileAsFunExpressions, './index.js')