A module is a normal js file with two differences.
The `export` keyword is how modules express what parts are to be exposed to importing modules.
The `export` keyword identifies locals that are to be exposed to importing modules. The `export` keyword can be followed by an option `default` keyword once per module.
An object declaration can then follow, or an object can be exported by referencing an existing object surrounded by curly braces and optionally re-namespaced with the `as` keyword.
export [default] <object declaration>
export [default] { <object reference> [as <identifier>] }
Any number of named exports may exist for a given module. Named exports can be exported at declaration time simply by adding the `export` keyword.
export const list = [1,2,3];
export function doStuff() { ... }
They can also be exported after declaration time. In this case, the local name(s) must be surrounded by curly braces.
export { list }
export { doStuff, doOtherStuff }
Named exports can also be exported under a different name than their internal one.
export { transformToAwesome as awesomeInator }
Any value can be exported from a module. There are no restrictions on export types.
export function Bruceinator(name) {
name = 'Bruce';
return `Mind if we call you ${name}?`;
}
export const JaredsMotto = "I'm a belieber!";
export let MattsCats = ["Muffin", "Piddles", "Sweetums", "Daddy's Princess"];
export { Bruceinator as deafault };
Only one default export can exist per module. Default exports are identified by the `default` keyword after the `export` keyword.
export default function rad() { ... }
export { rad as default }
Effectively, default exports are really named exports with a special `default` name.
The language preferred approach to exporting is the single export as default, and so is the easiest to construct and to consume.
// module1.js
export default function SRP(config) {
...
}
// module2.js
import mod from 'module1';
The `import` keyword indicates modules to be loaded into the current module context.
The `import` keyword identifies which exposed module parts are to be brought into the existing module context.
The `import` keyword must be followed by a name if only importing the module's default export.
If importing multiple module parts an import list (a comma separated list names surrounded by curly braces) or * is followed by an optional "`as identifier".
The location string completes the `import` statement.
import <name> [as <indentifier>] from "<location>";
import { <name>[ as <identifier>][, <name>[ as <identifier>]...] }> from "<location>";
import * as <identifier> from "<location>";
Default imports can be assigned directly to an arbitrary identifier.
// jQuery.js
export default function jQuery() { ... };
// myApp.js
import $ from 'jQuery';
The import/export keywords have no way to dynamically or conditionally load a module. The location that follows `from` must be a string literal. interpolated strings will throw an exception.
// myApp.js
let loc = 'jQuery'
import $ from loc; // Exception thrown;
The ES module specification comes with a programatic API for dynamic/conditional loading of modules using the import operator: `import()`.
Named exports must be imported under the name they were exported as...
// underscore.js
export function Underscore() { ... };
// myApp.js
import { underscore } from 'underscore';
Named exports can be assigned to an arbitrary identifier with the `as` keyword.
// underscore.js
export function Underscore() { ... };
// myApp.js
import { underscore as _ } from 'underscore';
The import() operator can be used anywhere in the code and accepts a fully qualified resource url as the only argument. It returns a Promise.
import('https://unpkg.com/d3@5.7.0/dist/d3.min.js')
.then((d3) => {
...d3 stuff
})
`let` is essentially `var` with block scoping
`let` declarations are not hoisted and therefore are not direct replacements for `var` since lexically they create a "temporal dead zone" where references are neither declared nor defined. `var` declarations do hoist and therefore are declared at every point in the lexical scope, though they may not be defined yet.
let a = 1;
console.log(a); // 1
console.log(b); // undefined
console.log(c); // Reference Error
var b = 2;
let c = 3;
`const` has all the same rules as `let` with the addition that `const` variables cannot be reassigned (though they can be mutated if the data-structure is itself mutable).
Attempts to reassign result in a silent failure, so there's that.
const a = 1;
a = 'one'; // No Error
const b = [2];
b.push(3);
console.log(a); // 1
console.log(b); // [2,3]
console.log(c); // Reference Error
const c = 4;
In computer programming, an anonymous function (also function literal or lambda abstraction) is a function definition that is not bound to an identifier. Anonymous functions are often:
https://en.wikipedia.org/wiki/Anonymous_function
Arrow functions are quite a bit more terse than standard JavaScript functions.
function (arg) {
return doStuff();
}
(arg) {
return doStuff();
}
(arg) => {
return doStuff();
}
arg => {
return doStuff();
}
arg => return doStuff();
arg => doStuff();
function (arg) {
return doStuff();
}
arg => doStuff();
function (arg) {
return doStuff();
}
arg => doStuff();
(arg1, arg2) => doStuff();
function (arg) {
return doStuff();
}
arg => doStuff();
(arg1, arg2) => doStuff();
(arg1, arg2) => {
let arg = arg1 + arg2;
return doStuff(arg);
}
Arrow functions are particularly well suited -- indeed created for -- lambda functions in javascript. When anonymous functions are passed as arguments or returned as a value from another function, they are excellent candidates for arrow functions.
In 1936, Alonzo Church ...wrote... about functions. His model was called the λ-calculus. (λ is the lowercase Greek letter lambda.) This work was the reason Lisp used the word LAMBDA to denote functions, which is why we call function expressions “lambdas” today.
Church wanted this model in order to prove things about computation in general.
[H]e found that he only needed one thing in his system: functions
Think how extraordinary this claim is. Without objects, without arrays, without numbers, without if statements, while loops, semicolons, assignment, logical operators, or an event loop, it is possible to rebuild every kind of computation JavaScript can do, from scratch, using only functions.
In ES5 to provide default values to arguments, code such as the following is often employed:
function doStuff(val1, val2) {
val1 = val1 || 'default';
val2 = val2 || {};
}
For the most part this has got the job done, but here area several cases where this approach falls apart.
doStuff('', false); //Oh noes!
In ES6 gives us proper default values for arguments
const doStuff = (val1='default', val2={}) => { ... }
No worries about false negatives or other falsey gotchas
doStuff('', false); //success
Destructuring allows us to extract individual values out of arrays...
let a = [1,2,3,4];
let [b, c] = a;
b === a[0]; // true
c === a[1]; // true
...or objects.
let o = {
a: 1,
b: 2
};
let {a, b} = o;
a === o.a; // true
b === o.b; // true
We can even assign the extracted values to new names!
let o = {
a: 1,
b: 2
};
let {a: x, b: y} = o;
x === o.a; // true
y === o.b; // true
Though to my eyes, this syntax looks backwards. :)
function rockStar({ name='Claudio Sanchez',
bandPosition= 'lead singer',
BFF= 'Cory'} = {}) {
return `Hello! My name is ${name}. I'm a ${bandPosition}. ${BFF} is my BFF!!`;
}
rockStar({
name: 'Jack White',
bandPosition: 'everything kind of guy'
}); // "Hello! My name is Jack White. I'm a everything kind of guy. Cory is my BFF!!"
rockStar({
name: 'Stephen Christian'
}); // "Hello! My name is Stephen Christian. I'm a lead singer. Cory is my BFF!!"
rockStar({}); // "Hello! My name is Claudio Sanchez. I'm a lead singer. Cory is my BFF!!"
rockStar(); // "Hello! My name is Claudio Sanchez. I'm a lead singer. Cory is my BFF!!"
Template strings are string literals with special powers. They are denoted by `backticks` rather than single or double quotes.
`I look an awful lot like a plain old string with no apparent special powers.`
One of the primary special powers of a template string is string interpolation. As you would expect, simple string insertion works just dandy.
const lovedOne = 'Ma',
stringConcatOperator = '+';
`Look ${lovedOne}! I don't need to use ${ stringConcatOperator } to concat strings!`;
//"Look Ma! I don't have to use + to concat strings!";
But any arbitrary expression works just fine as well.
`Look ${ getClosestLovedOne() }!
I don't need to use ${ String.fromCharCode(Number('0x2A') + 1) } to concat strings!`;
//"Look Ma! \nI don't have to use + to concat strings!";
const teplateStringLimerick = `
Making multiline strings in JS
Was, basically anyone's guess
But finally we've got
A solution that's not
A hot stinking pile of mess
`;
It's like gummyberry juice for your template strings
const getClosestLovedOne = () => `Ma<script>pwnIt();</script>`
sanitize`Look ${ getClosestLovedOne() }!
I don't need to use ${ String.fromCharCode(Number('0x2A') + 1) } to concat strings!`;
//"Look Ma<script>pwnIt();</script>! \nI don't have to use + to concat strings!";
Tagged templates is really just syntactic sugar for a call to a function you implement that carries a particular signature.
//[strings between replacements], [the interpolated parts]
const sanitize = (templateData, ...replacements) =>
templateData
.map(piece => piece.replace('<', '<').replace('>', '>'))
.reduce((str, piece, i) => `str${piece}${replacements[i] || ''}`));
could be something like this...
//imperial is a style of mustache, get it?
imperial`
<ul>
{#for user in ${users}}
<li>{user}</li>
{/for}
</ul>
`;
//"Cory"
or this...
i18n('de')`Welcome, ${user.name}`;
//"Welcomen, Cory"
or whatever, like literally whatever.
Rest parameters are a way of collecting all the rest of of the arguments passed into a function after the named parameters have been consumed.
const sum(a, b, ...cs) => cs.reduce((sum, c) => sum + c, a+b);
sum(1,2,3,4,5,6); // 21
const sum = (...args) => {
//just like arguments, but better
}
The spread operator operates on an iterable and spreads them in place. This may not sound terribly useful at first, but there are several ways this makes JavaScript development easier.
// Convert and array-like to an array-proper
[...document.querySelectorAll('.things')]
// A more elegant alternative to Function.prototype.apply
fn.call(null, ...someArray);
ES6 offers a convenience for defining methods on objects
// The old and busted
const someObj = {
someMethod: function (arg) {
// do some stuff.
}
}
// The new hotness
const someObj = {
someMethod(arg) {
// do some stuff.
}
}