-
Notifications
You must be signed in to change notification settings - Fork 9
Modules
A module encapsulates related code into a single unit of code.
Usually it is imported only once, and exposes an interface to be used.
A module is a function or object that presents an interface but that hides its state and implementation.
IIFEs takes advantage of function scope and closure to create relationships that are binding and private.
Namespacing: all declared functions and variables are enclosed in the closure.
Caution: implicit variable declaration will bind to global scope
;(function() {
a = 1
var b = 2
function logB() {
console.log(b)
}
logB() // 2
})()
console.log(a) // 1
console.log(b) // ReferenceError: b is not defined
logB() // ReferenceError: logB is not definedEven if any closure as access to global scope, it's good practice to import dependencies of global scope:
- Isolates even more the modules (easier to maintain and test)
- Provides an easy way to rename global variables
;(function(win, doc) {
const ELEMENT_ID = 'my-element'
win.addEventListener('load', function() {
doc.body.appendChild(doc.createElement('DIV'))
})
})(window, document)You can expose the interface for the module to be use.
Expose only what it used, keep the rest private.
var weekDay = (function() {
var names = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday'
]
function getName(number) {
return names[number]
}
function getNumber(name) {
return names.indexOf(name)
}
return {
name: getName,
number: getNumber
}
})()
console.log(weekDay.name(0)) // "Sunday"
console.log(weekDay.number('Monday')) // 1A module is a small app on its own.
It can manage its state in privacy, not polluting the global scope.
var counterModule = (function() {
var counter = 0
return {
incrementCounter: function() {
return counter++
},
resetCounter: function() {
console.log('counter value prior to reset: ' + counter)
counter = 0
}
}
})()
counterModule.incrementCounter()
counterModule.incrementCounter()
counterModule.resetCounter() // "counter value prior to reset: 2"
CommonJsis the module loading system ofNode.js.Each module is declared in a separated file and exports its interface
Any other module can "require" modules it depends on.
All modules are obtained by its path, thanks to the
requirefunction that is always available in Node.js environments
var counterModule = require('./modules/counter')
counterModule.incrementCounter()
counterModule.incrementCounter()
counterModule.resetCounter() // "counter value prior to reset: 2"Npm packages installed in the project can be required by package name
Install a package:
npm install angular --save
Import a module from package:
// Import angular to create a module
var angular = require('angular')
var ngModule = angular.module('my-module', [])Install React:
npm install react --save
Import React module:
// Import React to create a component
var React = require('react')
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>
}
}
requireloads js files and executes doing the following:
- It injects the empty object
exportsready to be augmented with new properties and methods.- It injects a
moduleobject with theexportsobject as its property, so the full module can be replaced instead of augmenting the original
function require(name) {
var code = new Function('exports', 'module', readFile(name))
var exports = {},
module = { exports: exports }
code(exports, module)
return module.exports
}Consider:
var counterModule = (function() {
var counter = 0
return {
incrementCounter: function() {
return counter++
},
resetCounter: function() {
console.log('counter value prior to reset: ' + counter)
counter = 0
}
}
})()In CommonJs:
// modules/counter.js
var counter = 0
module.exports = {
incrementCounter: function() {
return counter++
},
resetCounter: function() {
console.log('counter value prior to reset: ' + counter)
counter = 0
}
}// modules/counter.js
var counter = 0
exports.incrementCounter = function() {
return counter++
}
exports.resetCounter = function() {
console.log('counter value prior to reset: ' + counter)
counter = 0
}Node.js caches every loaded instance.
The second time a module is required, the instance obtain at first call is returned.
function require(name) {
if (name in require.cache) return require.cache[name]
var code = new Function('exports, module', readFile(name))
var exports = {},
module = { exports: exports }
code(exports, module)
require.cache[name] = module.exports
return module.exports
}
require.cache = Object.create(null)var counterModule = require('./modules/counter')
counterModule.incrementCounter()
counterModule.incrementCounter()
counterModule.resetCounter() // "counter value prior to reset: 2"var counterModule = require('./modules/counter')
counterModule.incrementCounter()
counterModule.resetCounter() // "counter value prior to reset: 3"Write a simple module similar to the weekDay module that can convert month numbers (zero-based, as in the Date type) to names and can convert names back to numbers.
var month = require(function(module, exports) {
// YOUR CODE GOES HERE
})
console.log(month.name(2)) // March
console.log(month.number('November')) // 10https://stackblitz.com/github/we-learn-js/js-training-code/tree/master/src/Modules/zapuqus?embed
var month = require(function(module, exports) {
const NAMES = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
]
function getName(number) {
return NAMES[number]
}
function getNumber(name) {
return NAMES.indexOf(name)
}
module.exports = {
name: getName,
number: getNumber
}
})The
exportstatement is used to export functions, objects or primitives from a given file (or module).
Local variables can be exported and renamed.
export { name1, name2, …, nameN }; export { variable1 as name1, variable2 as name2, …, nameN };
const NAMES = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
]
function getName(number) {
return NAMES[number]
}
function getNumber(name) {
return NAMES.indexOf(name)
}
export { NAMES, getName as name, getNumber as number }Variables and functions can be export on declaration.
export let name1, name2, …, nameN; // also var export let name1 = …, name2 = …, …, nameN; // also var, const export function name1(…) { … } // also class, function*
export const NAMES = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
]
export function name(number) {
return NAMES[number]
}
export function number(name) {
return NAMES.indexOf(name)
}As
module.exportsin CommonJs, the full module can be exported as default.But unlike CommonJs, the default export doesn't overwrite other exports.
export default expression; export default function (…) { … } // also class, function* export default function name1(…) { … } // also class, function* export { name1 as default, … };
function getName(number) {
return NAMES[number]
}
function getNumber(name) {
return NAMES.indexOf(name)
}
export const NAMES = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
]
export default {
name: getName,
number: getNumber
}The
importstatement is used to import functions, objects or primitives that have been exported from an external module, another script, etc.
Any member exported can be imported as is or renamed.
import { member } from 'module-name' import { member as alias } from 'module-name' import * as name from 'module-name'
<!--slide-->
Consider:
```js
// modules/months.js
export const NAMES = ["January", "February", "March", "April",
"May", "June", "July", "August", "September",
"October", "November", "December"]
export function name (number) { return NAMES[number] }
export function number (name) { return NAMES.indexOf(name) }
Importing members separately
// main.js
import { NAMES, name as getMonthName, number } from './modules/months'
console.log(getMonthName(2)) // MarchImporting all members
// main.js
import * as month from './modules/months'
console.log(month.name(2)) // MarchDefault member can be imported
import defaultMember from "module-name"; import defaultMember, { member [ , [...] ] } from "module-name"; import defaultMember, * as name from "module-name";
<!--slide-->
Consider:
```js
function getName (number) { return NAMES[number] }
function getNumber (name) { return NAMES.indexOf(name) }
export const NAMES = ["January", "February", "March", "April",
"May", "June", "July", "August", "September",
"October", "November", "December"]
export default {
name: getName,
number: getNumber
}
Importing default:
import month from './modules/months'
console.log(month.name(2)) // MarchImporting default and members
import month, { NAMES as monthNames } from './modules/months'
console.log(month.name(2)) // March
console.log(monthNames) // ["January", "February", "March", "April", ...member can be exported from other modules
export * from …; export { name1, name2, …, nameN } from …; export { import1 as name1, import2 as name2, …, nameN } from …;
<!--slide-->
```js
// modules/months.js
export const NAMES = ["January", "February", "March", "April",
"May", "June", "July", "August", "September",
"October", "November", "December"]
export function name (number) { return NAMES[number] }
export function number (name) { return NAMES.indexOf(name) }
export * from './modules/months'export { name as getMonthName } from './modules/months'// 'math.js'
var {PI, round} = Math
var square = function (x) { return x * x; }
var getNumber = function (x) { return Number(x) }
export square as getSquare
export function getCube(x) { return x * x * x; }
export default getNumber
export {round as getRound, PI}- Import all methods as math
- Import square function as square
- Import number function as number
- Import all methods as math and getNumber as number
// 'math.js'
var {PI, round} = Math
var square = function (x) { return x * x; }
var getNumber = function (x) { return Number(x) }
export square as getSquare
export function getCube(x) { return x * x * x; }
export default getNumber
export {round as getRound, PI}import * as math from 'math'// 'math.js'
var {PI, round} = Math
var square = function (x) { return x * x; }
var getNumber = function (x) { return Number(x) }
export square as getSquare
export function getCube(x) { return x * x * x; }
export default getNumber
export {round as getRound, PI}import { getSquare as square } from 'math'// 'math.js'
var {PI, round} = Math
var square = function (x) { return x * x; }
var getNumber = function (x) { return Number(x) }
export square as getSquare
export function getCube(x) { return x * x * x; }
export default getNumber
export {round as getRound, PI}import number from 'math'// 'math.js'
var {PI, round} = Math
var square = function (x) { return x * x; }
var getNumber = function (x) { return Number(x) }
export square as getSquare
export function getCube(x) { return x * x * x; }
export default getNumber
export {round as getRound, PI}import * as math, default as number from 'math'