Written by Hamilton Cline
Last updated on January 18, 2019, 5:40 pm

Variable Instantiation

Drop Var

This is more opinion than fact, but it is shared opinion on the webs. Var has been effectively replaced by the two new instantiation words: let and const. Although Var does something different, and therefore might still be considered, the thing that it does should be considered 'not cool', and therefore all future code can effectively drop the Var command.

Let

Let is the all around replacement for Var. It does everything the Var command would have been used to do, with one major difference. It can be scoped to any block statement. Where as Var could only be scoped to a function block, Let can be scoped to any block, including if, while, for, and switch. When using this for the first time, this can lead to issues if you're not prepared for this new mindset.

let a = 5; for(let b=0;b<10;b++){ a = b; } console.log(a,b); // Uncaught ReferenceError: b is not defined

Ultimately this should lead to cleaner code, and better mindsets. Define your variables properly before using them, and keep them defined to their appropriate scope.

Const

Const is essentially the same as Let, except that it's immediate value becomes immutable. This means a const value once defined cannot be redefined, however it's properties and iterables can still be modified.

const a = 5; a++; // Uncaught TypeError: Assignment to constant variable. const b = {}; b.name = 'George'; console.log(b.name); // George

Arrows and This

ES5 Functions

Here are some familiar old ways of writing a function. Making a function with the function name() syntax will hoist the function to the top of the stack, and make it available to anything anywhere. Using the foo = function() syntax will anonymize the function as a variable value, and only be available to future code, similarly a function created as an object method is only available after its creation.

function foo(){ return 'bar'; } var foo = function(){ return 'bar'; } var some = { foo : function(){ return 'bar' } }

Arrow Functions

Arrow functions are a new way of writing functions. It does not outright replace the old function syntax, but it can be used more effectively in a number of conditions. Arrow functions are never hoisted. They are only ever used as inline values on variable names, and as such, should probably always be set up as const values to make them immutable.

const foo = () => 'bar'; const some = { foo : () => 'bar' }

Arrows can be setup with all manner of parameters, but they can be written in slightly different ways, depending on how many are present. With zero arguments, you must represent the parameters of an arrow function using parenthesese. When using only one argument, you can drop the parens, but then you must bring them back for more than 1 argument.

Understand that if you just copied the next example into a browser, it would fail, since a const can't be defined more than once.

const foo = () => 'bar'; // No arguments const foo = a => 'bar'+a; // Single Argument const foo = (a,b) => a+'bar'+b; // Multiple Arguments

Expression Body

As an expression body, an arrow function should always be one thing, and it will return that one thing as a value. With a statement body, the arrow function can have multiple statements, and then return a value like normal.

const foo = a => 'bar'+a; // Single expression const foo = a => { // Multiple statements console.log(a); return a; }

Lexical This

Arrow functions do not have their own this value. They share their this with their parent. This makes them wonderful for certain method callback functions, but actually not as great for simple event callbacks. An event callback might want to know what element just called it, but an arrow callback would not know that information. Of course you can use an arrow function there as well, just use e.target to get the same value as the anonymous function's lexical this.

This next example jumps ahead a bit to show something useful.

document.querySelector("a").addEventListener("click",function(e){ console.log(this.href); // `this` will be the element that was clicked [...this.attributes].forEach(o=> console.log(`${this.nodeName} - ${o.nodeName}`) // `this` is still the original event scope ); }); document.querySelector("a").addEventListener("mouseover",e => { console.log(e.target.href); // e.target will be equivalent to the anonymous function's `this` console.log(this); // Window Object });

Template Strings

Template literals

The template literal is a new syntax for writing strings, which uses backticks instead of quotes to indicate a string. Template literals can be single line or multi line.

let foo = `<div>Text</div>`; // single line template let bar = ` <div> <span>Text</span> </div> `; // multi line template

Interpolation

But unlike the single and double quoted strings, backtick templates can interpolate variables and directives without concatenation. This allows for code that is much easier to follow, and the creation of complex template structures.

const foo = 'bar'; console.log(`'foo' equals ${foo}`); // 'foo' equals bar // A simple templater can be created with backtick strings const templater = fn => data => fn(data); // curried function that expects template first, then data object const showName = templater(o=>`My Name is ${o.firstName} ${o.lastName}`); // create the template function showName({firstName:'George',lastName:'Foobar'}); // My Name is George Foobar

Default, Rest, & Spread

Default Values

Defining default values for the arguments of a function can lead to more functionality.

const addFiveOrSomething = (a, b = 5) => a + b; addFiveOrSomething(4); // 9 addFiveOrSomething(4,10); // 14

Rest Operator

The rest and spread operators are ellipses that indicate an array should be exploded to an interface that would normally take multiple individual comma separated values, or imploded from a list to an array. Arrow function don't have an arguments array, and so one of the things that can combat this is the Rest operator. This can also be useful when you make a function that expects one named value, and then want to gather the rest of the values into an array.

const howManyArgs = (...args) => args.length; howManyArgs(0,1,2); // 3; const thisTimesThose = (num, ...nums) => num * nums.length; thisTimesThose(5,0,1,2,3); // 20

Spread Operator

Sometimes it can be useful to pass an array as the arguments for a function. It can also be useful to turn an array-like object into an array, such as a querySelectorAll result.

const showName = (first,last) => `${last}, ${first}`; showName(...['George','Foobar']); // Foobar, George // Spread an array-like inside an array constructor to gain Array methods [ ...document.querySelectorAll('a') ].forEach(o => console.log(o.href) );

Destructuring

Destructuring values can be extremely valuable when converting between arguments and returns. This quickly takes values from objects or arrays and extracts them to variable names.

Arrays

Destructuring arrays can easily pull out and name specific values from within an array. This is especially useful for certain functions that return an array of values.

let [a,b] = [2,8]; a; // 2 // Skip values you don't care about let [c,,d] = [2,4,8]; d; // 8 // Use a spread operator to destructure the rest of the values let [fullmatch, ...matches] = /^(\w+)@(\w+)\.(\w+)$/.exec('george@gmail.com'); fullmatch; // 'george@gmail.com' matches; // ['george', 'gmail', 'com']

Objects

Objects can be destructured automatically, or renamed to something new.

const obj = {name:'George',type:'Teacher'}; let {name,type} = obj; // Shorthand destructure to the same names let {name:subName,type:subType} = obj; // Destructure to new names: subName and subType

For..Of

For

The for loop is a shorthand version of a common while loop. It's usefulness becomes apparent when dealing with arrays and known length iterables. It can be improved by predefining certain constantly checked values, or by iterating in a reverse order.

for(let i=0; i<5; i++) console.log(i); for(let i=0,l=5; i<l; i++) { // With multiple instantiations console.log(i); // As a statement body } for(let i=5; i>=0; i--) console.log(i); // Reverse loop

For In

A For..In loop can be handy for looping through all array items, or even through object properties. With For..In loop, you must define an iterator that will represent each index as the array is looped through.

const arr = [2,4,8]; for(let i in arr) console.log(`${i}=${arr[i]}`); // 0=2, 1=4, 2=8

For Of

A For..Of loop, on the other hand, loops just like the For..In but its iterator represents the current value. This is especially useful, when using some newer iterables, but also when you just don't care about the index of the current element.

const arr = [2,4,8]; for(let o of arr) console.log(o); // 2, 4, 8

Modules

Module Attribute

If you want to work with React, Angular, or higher level VueJS, you'll encounter import pretty quickly. You can import without using those kinds of node status frameworks. Add a type attribute of module.

<script type="module" src="script.js"></script>

Export

The core of modules is exporting data from one file for access by another. This allows a developer to import what's necessary without polluting the scope with unwanted variable names.

// helpers.js export rand = (n,x) => Math.round(Math.random()*(x-n)+n); export rebounce = (c,f,a,t=100) => !c?!setTimeout(()=>f.apply(c,a),t):true;

You can also set up an export at the end of a document.

// otherhelpers.js const rand = (n,x) => Math.round(Math.random()*(x-n)+n); const rebounce = (c,f,a,t=100) => !c?!setTimeout(()=>f.apply(c,a),t):true; export {rand,rebounce};

Import

Once you've exported some things, you can import those into another script.

// script.js import * as h from "helpers"; h.rand(0,5); // a random number from 0 to 5;

You can also import specific values if needed.

// otherscript.js import {rand} from "helpers"; rand(20,40); // a random number from 20 to 40;

Array Methods

These array methods aren't all new. But many people still haven't worked them into their workflow yet, so let's get a handle on these concepts. Most of these involve some sort of loop, and most of them have a mostly similar structure for use.

ForEach & Map

The forEach method loops through an array and returns nothing when it completes. It's like running a for loop on an array. The map method loops through an array and returns a new array with its values potentially altered from the original. It is most useful when wanting to edit the values of every single element in an array, but still retain the original.

Both of these function and many of these other array methods require a callback function which will be called upon each element in the array, and this callback will get passed three values: the current object, the current index, and the original array.

let arr = [1,2,3,4]; arr.forEach((o,i,a) => o * 2); // 2, 4, 6, 8 arr; // [2,4,6,8] // Map will return a new array arr.map((o,i) => o * i); // [0, 4, 12, 24] arr; // [2,4,6,8]

Reduce

The reduce method is a lot like map, except that instead of ending up with one new array, you will end up with one new... anything. Often times, it can be used to reduce an array of numbers to a single number, but it can just as easily be used to reduce an array of objects to one string or template. It gets used slightly differently than most of the other array iteratives. The predicate gets passed the same three values: object, index, array, and an extra reducer value. The reduce method itself can be passed an optional starting value.

let arr = [{name:'Ginny',age:16}, {name:'Ron',age:17}, {name:'George',age:19}, {name:'Fred',age:19}]; // Templating arr.reduce((r,o,i,a) => r + `${o.name}<br>`, ''); // Ginny<br>Ron<br>George<br>Fred<br> // Templater const templater = fn => arr => arr.reduce((r,o,i,a) => r + fn(o,i,a),''); // Curried Function const showFamily = templater(o => `<p>${o.name}<p>`); showFamily(arr); // <p>Ginny</p><p>Ron</p><p>George</p><p>Fred</p> // Mean age Math.round( arr.reduce((r,o) => r + o.age, 0) / arr.length ); // 18

Filter & Find

The filter method reduces an array by returning only elements that pass a boolean test. It doesn't affect the original array, returning instead a new array. The find method works the exact same way, except where Filter will return an array even if it only finds one item, Find will only ever return one object or undefined. Both of these functions' callbacks will get passed the object, index, and array values.

let arr = [1,2,3,4]; // Filter returns an array based on a predicate boolean arr.filter(o => o % 2); // [2,4] arr.filter(o => o == 2); // [2] arr.filter(o => o == 5); // [] // Find returns one object or a boolean arr.find(o => o > 2); // 3 arr.find(o => o == 5); // undefined

Some & Every

The some method will test to see if any of the elements inside an array pass a test, and it will return a boolean. The every method does the same kind of test, but it only wants to see if ALL the elements pass the test.

let arr = [1,2,3,4]; arr.some(o => o > 2); // true arr.every(o => o == 2); // false

Pop, Push, Shift, & Unshift

These are not new at all, but just as a refresher, pop and push remove and add items from the end of an array, shift and unshift remove and add from the beginning of an array.

let arr = ['red','green','blue']; // Pop removes from the tail, and returns that value arr.pop(); // blue arr; // ['red','green'] // Push adds to the tail, and returns the new length arr.push('magenta'); // 3 arr; // ['red','green','magenta'] // Shift removes from the head, and returns that value arr.shift(); // red arr; // ['green','magenta'] // Unshift adds to the head, and returns the new length arr.unshift(...['yellow','cyan']); // 3 arr; // ['yellow','cyan','green','magenta']

Splice & Slice

Also not new, splice can add or remove from anywhere in an array. The slice method can return part of an array or all of it as a shallow copy.

let arr = ['red','green','blue']; // Splice can remove and add from anywhere in an array, and returns an array of affected items arr.splice(1,0,...['white','black']); // [] arr; // ['red','white','black','green','blue'] arr.splice(-1,1,'gray'); // ['blue'] arr; // ['red','white','black','green','gray'] // Slice returns a shallow copy of a part of an array arr.slice(); // ['red','white','black','green','gray'] arr.slice(1); // ['white','black','green','gray'] arr.slice(-1); // ['gray']

Object Methods

Object methods contain a lot of really cool things that can help when dealing with certain kinds of data and Object Inheritance.

Assign & Create

The assign method lets you take one object, and stamp it's properties onto another object. It will affect the original object, and return that same object when it completes. The create method lets you Take one object or object primitive and create a new instance of that same kind of object, even without a proper constructor.

// Assign properties to an object let ctx = document.querySelector("canvas").getContext('2d'); Object.assign(ctx, {strokeStyle:'blue', lineWidth:3}); // Instantiate a new object of a similar kind let animal = {type:'animal',age:0}; let dog = Object.create(animal); dog.type = 'dog'; animal.type; // animal

Keys & Values

The keys method returns an array of all the keys of an array, array-like, or object. The values method returns an array of all the values of those kinds of things.

Object.keys({a:2,b:4,c:8}); // ['a','b','c'] Object.values({a:2,b:4,c:8}); // [2,4,8]

Entries

The entries method will take an array, array-like, or object, and turn it into a matrix of arrays with key value pairs. Combine this with For..of and destructuring and you can make a foreach much like php.

let arr = [1,2,4,8]; Object.entries(arr); // [[0,1],[1,2],[2,4],[3,8]] for(let [key,val] of Object.entries(arr)) { console.log(`${key} = ${val}`); // 0 = 1, 1 = 2, 2 = 4, 3 = 8 }

Function Methods

Apply & Call

The apply and call methods for functions work very similarly. Both methods call a function with a supplied this value and an amount of parameters. Use call when you want to call a function with individual parameters, and use apply when you want to call a function with an array of parameters.

[].forEach.call( document.querySelectorAll('a'), o => o.addEventListener('click', e => console.log(e,o)) ); Math.max.apply(null, [1,2,4,8]); // 8

JSON Object

Use the JSON object to convert data between strings and objects. This is especially useful to store and retrieve complex data for localStorage and sessionStorage.

Parse

The parse method will take a genuine JSON string, and turn it into Javascript objects. It will fail if the string does not follow strict JSON guidelines.

JSON.parse(`[{"name":"George"}, {"name":"Fred"}]`); // [{name:"George"},{name:"Fred"}] JSON.parse(`{age:0}`); // Uncaught SyntaxError: Unexpected token

Stringify

The stringify method will take a Javascript object, and turn it into a JSON string.

JSON.stringify([{name:"George"},{name:"Fred"}]); // '[{"name":"George"}, {"name":"Fred"}]'

JSON with Storage

Using the JSON object with sessionStorage and localStorage can give you much better and easier data storage capacity. Of course using Storage means you're limited to JSON file types of data, so no functions, but it still makes it so much easier to store complex values.

const dataGet = (k,s='localStorage') => JSON.parse( window[s].getItem(k) ); const dataSet = (k,v,s='localStorage') => window[s].setItem( k, JSON.stringify(v) );

Promises

Promise & Then

A promise is returned from asynchronous concepts. Promises are something that may happen some time in the future. There are many parts of Javascript that already return a promise.

Instantiate a new Promise and then chain off a then method to define what to do the promise resolves.

new Promise(function(resolve,reject){ // some code resolve('Finished'); }) .then(data => { console.log(data); })

SetTimeout

The setTimeout method works asynchronously, and might be the easiest way of seeing how promises really work.

new Promise(function(resolve,reject){ setTimeout(()=> resolve('Finished'), 3000); }) .then(data => { console.log(data); // Doesn't show up for 3 seconds })

Fetch

The fetch API produces a promise and can be used to up your ajax game. Fetch a document, and then do something with the returned data. There's usually an intermediary point where we return the body of whatever document we fetched, and then parse that into json. This process itself produces another promise.

// Downloading data fetch("http://somesite.com/script.php") .then(response => response.json()) .then(data => { console.log(data); // should be a json object of the url's body }) // Uploading data fetch("http://somesite.com/script.php", { method: 'POST', body: JSON.stringify({name:"George"}), headers:{ 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => console.log(data);
Written by Hamilton Cline
Last updated on January 18, 2019, 5:40 pm