JavaScriptLA
Slides.com website for JavaScriptLA -- use this place to find our meetup group presentations. Want more? Visit our website at: https://www.javascriptla.net to get on our mailing list.
by Vijay Menon
Head Organizer, JavaScriptLA, HackBuddy.com
Lots of different ways to get started, but we'll use Babel & Webpack as our starter kit.
https://github.com/vijayxtreme/starterES6Webpack
Alternatively, you can use another toolkit like Gulp/Traceur, Rollup, or even just try out ES6 in your browser if it supports the syntax (e.g. Google Chrome)
Unfortunately, tooling can sometimes be the biggest handicap to learning new JavaScript
let
const
var
Use let when you want to have block scope; use const when you want to make sure your variable value can't be overridden.
When would using var still be appropriate?
let x = "Hello"
if(x == "Hello") {
let y = " World"
}
console.log(x + y)
let x = "Hello"
if(x == "Hello") {
let y = " World"
console.log(x + y)
}
What do you expect as the output here?
What do you expect as the output here?
const $ = function jQuery(){ //do jquery stuff }
/*** adding in other libraries into our JavaScript ***/
$ = function angular(){ //do angular stuff }
console.log($)
What do you expect as the output here?
const API_KEY = '123456'
API_KEY = '654321'; //someone tries to change this variable's value
What do you expect as the output here?
Template strings are great for concatenating lots of variables with text. In the old days of ES5, we had to do stuff like this:
Now we can use the backtick symbol ` to start and end a template string, and use ${} syntax to plug in variables like so:
var x = 10, y=20, a=40, b=15;
var result = "Robert has " + x + " apples, " + y + " bananas, " + a + " mangoes, " + b + " ice creams." ;
let x = 10, y = 20, a = 40, b = 15
let result = `Robert has ${x} apples, ${y}
bananas, ${a} mangoes, ${b} ice creams`
Result is cleaner, easier to read, write syntax; and template strings don't care about multiple lines!
let parseTemplate = function(text, variable1, variable2){
console.log(text);
console.log(variable1);
console.log(variable2);
return variable2;
}
let item1 = "Laundry"
let item2 = function(){ return "Groceries" }
let result = parseTemplate`I have to do my ${item1} and ${item2}`
console.log(`Finished with ${result()}`)
export const query = graphql`
query {
allWordpressPost {
nodes {
title
excerpt
link
featured_media {
source_url
}
}
}
}`
Don't freak out if you don't understand this yet, just try to get the general idea :)
Function expressions that define functions can now be replaced with "arrow function" syntax. Here's single parameter and multiple parameters below.
Note: If you have no params, write your function like so: () => { /*My code here */}
let greetName = name => `hello ${name}`
// ES5 syntax
var greetName = function(name) {
return "hello " + name;
}
console.log(greetName('vijay'))
//hello vijay
let z = (x, y) => x + y
//ES5 syntax
var z = function(x, y){
return x + y;
}
let greetAndPrintName = name => {
let str = `hello ${name}`;
console.log(str);
}
greetAndPrintName('vijay')
Example of a multi-line function; Wrapped with {}
let result = (x,y) => (
x + y
)
console.log(result(1,2)) //3
Example of a multi-line return statement; Wrapped with ()
function Mammal(name) {
this.animalName = name || "Henry";
this.sayName = function() {
setTimeout(function() {
console.log("My name is " + this.animalName);
}, 200);
}
}
var dog = new Mammal();
dog.sayName();
//My name is undefined
How would we fix our code in ES5?
function Mammal(name) {
this.animalName = name || "Henry";
this.sayName = function() {
var that = this;
setTimeout(function() {
console.log("My name is " + that.animalName);
}, 200);
}
}
var dog = new Mammal();
dog.sayName();
//My name is Henry
One possible solution, use that.
Er.. um, this and that?
function Mammal(name) {
this.animalName = name || "Henry";
this.sayName = function() {
// var that = this;
setTimeout(function() {
console.log("My name is " + this.animalName);
}.bind(this), 200)
}
}
var dog = new Mammal();
dog.sayName();
//My name is Henry
How about bind instead?
class Mammal {
constructor(name = "Henry") {
this.animalName = name;
}
sayName() {
setTimeout(() => {
console.log(`My name is ${this.animalName}`)
}, 200)
}
}
let dog = new Mammal();
dog.sayName();
Yay, now I don't have to trip over my own code!
Remember, arrow functions remember their scope, they have a lexical scope binding.
import React, { Component } from "react";
import "./styles.css";
export default class App extends Component {
constructor() {
super();
this.clickMe = this.clickMe.bind(this); //headache -- use arrow functions instead!!!
}
clickMe() {
alert("hello you clicked me");
}
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={this.clickMe}>Click Me</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
Don't worry if you don't understand this right now, just the gist is fine.
import React, { Component } from "react";
import "./styles.css";
export default class App extends Component {
constructor() {
super();
}
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button
onClick={() => {
/* Wow less code bloat!! */
alert("hello you clicked me");
}}
>
Click Me
</button>
</div>
);
}
}
Lexical arrow functions to the rescue!
Use them like super powers! Play around with the sandbox at:
(...param) => param
Returns an array of arguments passed to function at run time.
Note, ...arg must always be the last argument, otherwise this will not work.
(...param, param2) //SyntaxError
(param, ...param2) //Correct
let sum = (total = 0, ...numbers) => {
numbers.forEach(num => total += num)
console.log(total);
}
sum(1,2,3,4,5,6,7);
sum(1,2,3,4,5,6);
sum(1,2,3);
Spread operator plugs in values 1,2,3 into a,b,c respectively.
How would you do this in ES5?
The spread operator is used for arguments in a function call, while the rest operator is used as parameters in a function definition
let sum = (a,b,c) => {
console.log(a + b + c)
}
sum(...[1,2,3]) //spread operator
What's the output of myVariable?
How about here?
Why do you think destructuring is valuable?
let { myVariable } = {
myVariable: "green Salad"
}
let [a,b] = [3, (x,y)=>x+y, 3]
How does this compare to prototypes?
Do you remember what a prototype is?
When is it better to use a class vs a function?
class Animal {
constructor(name="", species=""){
this.name = name
this.species = species
}
}
class Dog extends Animal {
constructor(name, species){
super(name,species)
}
bark(){
console.log(`${this.name}, a ${this.species} says woof woof`)
}
}
const fido = new Dog("Fido", "Dog")
fido.bark()
Avoid the common issue of "callback hell" in ES5; aka nested callbacks -- difficult to read and debug.
If you are doing work with RESTful services, use Promises instead
let p = new Promise((resolve, reject) => {
setTimeout(() => {
let rand = Math.floor((Math.random()*10),1), x = false
x = (rand % 2 == 0) ? true : x
console.log(rand)
x ? resolve() : reject()
}, 1000)
})
p.then(() => {
console.log(`Life's great`)
})
.catch(e => console.log(`You can't always get what you want`))
let ajaxP = (http) => {
return new Promise(function(resolve, reject) {
if (!http) {
throw new Error("Must define an http object")
}
let xhr = new XMLHttpRequest();
xhr.open(http.type, http.url);
xhr.onload = function() {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function() {
reject({
status: this.status,
statusText: xhr.statusText
})
}
xhr.send();
});
}
//Promisified version of our http request
ajaxP({
type: "GET",
url: "https://jsonplaceholder.typicode.com/todos/1"
}).then(response => {
return JSON.parse(response);
}).then(response => {
console.log(response)
let user = response.userId
let url = `https://jsonplaceholder.typicode.com/users/${user}`
return ajaxP({
type: "GET",
url: url
})
}).then(res => {
console.log(JSON.parse(res))
}).catch(e => console.log(e))
The basic pattern here is:
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => console.log(json))
No need to write all the complexities of AJAX and error handling with XMLHttpRequest; the community already did it for you!
If you understand promises, fetch works exactly the same way. Check out the API!
You can replace JQuery $.ajax with fetch and avoid callback hell! Take the plunge today~
function promiseMeYoullWait() {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
resolve('You waited!')
}, 4000)
} catch (e) {
reject(`Sorry the program couldn't wait, Error: ${e}`)
}
})
}
//Start here when you read this code...
async function myAsync() {
let result = await promiseMeYoullWait()
console.log(result)
}
myAsync(); //You waited!
async & await allow us to write code that looks more understandable to a human; step by step, even though this code is still asynchronous -- it looks synchronous to us.
Why do you think this is important?
What does it mean to be an iterable object?
What does it mean to iterate something?
let myArr = [1,2,3,4]
let itr = myArr.values() //get our iterator from our array
let i = itr.next(); //call the next method on the iterator
while(!i.done){
console.log(i); //show the value and done for the current object
i = itr.next(); //keep calling the iterator next method
}
An array is an iterable object; we can loop through it (it supports iteration)
An iterator helps us loop through the code, one step at a time. So think of a for loop that stops each time and gives you a result before going to next iteration.
Why might this be useful?
Generators yield.
Think a function that returns new results each time, rather than the same expected result over and over.
Generators return iterable objects, which give us access to new yield values
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
let itr = myGenerator(); //generator function call, creates a new iterator, itr
let res = itr.next(); //we get an object with { value: 1, done: false }
console.log(res.value, res.done); //1, false
// do something else
res = itr.next();
console.log(res.value, res.done); //2, false
// do something else
res = itr.next();
console.log(res.value, res.done); //3, true
A generator returns an iterable object with keys for value and a done flag. In each yield, we get the value and a done (true/false). So long as done is not true, we can keep iterating through the generator for new yield values
function* createNewId() {
let i = 0;
while (true) { //generate infinite ids
yield i;
i++;
}
}
let itr = createNewId() //create iterator
function createNewAccount(email) {
let res = itr.next(); //get next yield value
let id = res.value; //store yield value in id
let user = {
email,
id
}
console.log("new user created", user)
}
createNewAccount("aj@iw.org")
createNewAccount("ma@nn.com")
createNewAccount("pjw@sn.com")
This code can generate us new accounts forever, but we use iterators to control how often we need to yield
function* myGenerator(){
yield 1;
yield 2;
yield 3;
}
for(let i of myGenerator()){
console.log(i)
}
You can use the for...of loop for convenience (syntactic sugar) instead of calling myGenerator.next()
Maps can hold values of any type and they also remember the order in which items were individually stored
Sets hold unique values; when do you think a Set might be useful over an array?
//Map Example
let myMap = new Map();
myMap.set('John', 'Cheeseburger');
myMap.set('Vijay', 'Chicken Tikka Masala')
myMap.set('Danielle', 'Vegan Lasagna')
myMap.set('function doNothing(){}', 'Did nothing')
myMap.set(NaN, 'yup NaN works too')
console.log(myMap.size);
myMap.forEach((val, key) => console.log(`${key}: ${val}`))
The first argument is your "key", the second argument is your "value". Think of Maps like hashes, yet you can store any type you want as a key, and look it up in the same fashion to retrieve a value.
When do you think a Map might be better than a traditional object for item lookup?
let mySet = new Set();
//use the add method to add new values to our set
mySet.add(1)
mySet.add(2)
mySet.add((24/12))
mySet.add("iPhone")
//use the delete method to remove values from our set
mySet.delete(1)
console.log(mySet.has(2)) //true
mySet.forEach(val => console.log(val))
//2
//iPhone
console.log(mySet.size) //2
Sets must always have unique values, unlike arrays which can hold duplicate values. Why might a Set be better than an Array?
//new Q library
var Q = (function(){
return {
log: function(query){
console.log(query)
},
ver: 1.0
};
}())
//version 2 update
(function(lib){
if(lib.ver < 2) {
update(lib);
}else {
console.log("Library at latest version")
return lib;
}
function update(lib){
lib.add = function(num1, num2){
lib.log(num1 + num2);
}
lib.subtract = function(num1, num2) {
lib.log(num1 - num2);
}
lib.ver = 2.0
}
return lib;
})(Q);
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript" src="Q.js"></script>
<script type="text/javascript" src="Q.update.js"></script>
<script type="text/javascript" src="Q.random.js"></script>
…
<script type="text/javascript" src="Q.update100.js"></script>
<script type="text/javascript" src="Q.plugin45.js"></script>
<script type="text/javascript" src="Q.randomN.js"></script>
</body>
</html>
Hmm.. this is getting heavy.. I wonder if our site will be okay with all these requests. Perhaps we could move to CDN? Could we minify all these into one file? Would that be optimal? What if we don't need all these libraries, should we just rewrite?
<html>
<head>
<title>My Sample Project</title>
<!-- data-main attribute tells require.js to load scripts/main.js after require.js loads. -->
<script data-main="scripts/main" src="scripts/require.js"></script>
</head>
The data-main attribute for the script tag tells require.js to load main.js after require.js loads. (The require.js library is downloaded from the RequireJS website).
The main.js file is where you can tell RequireJS to load in any other dependencies you need for your program. Modules are asynchronously loaded by RequireJS, hence Asynchronous Modular Development. Key benefit -- module loading for the browser (no NPM needed)
//Q.js file
define(["jquery"], function($) {
return {
log: function(val) {
$("body").html(val)
},
ver: 1.0
}
});
project folder >
| js
| lib
- Q.js
- jquery.js
- main.js
- require.js
index.html
/***** q.js *****/
function log(query) {
console.log(query)
}
var version = 1.0;
module.exports = {
log: log,
ver: version
};
/***** app.js *****/
var Q = require("Q.js");
console.log(Q.ver) //1.0
project folder >
| js
| q.js
| app.js
index.html
How many times have you seen this ad?
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['Q'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('Q'));
} else {
// Browser globals
root.Q = factory(root.Q);
}
}(this, function(Q) {
Q.add = function(num1, num2) {
Q.log(num1 + num2);
};
return Q;
}));
Yeah, um.. no.. can we just go back to ES6 already?
/*** Car.js our Module file ***/
let car = "Toyota"
export default function driveMyCar() {
return `${car} is driving`
}
/*** index.js ***/
import driveMyCar from "Car"
let myCar = driveMyCar()
console.log(myCar)
Yay, so much simpler.. and we can use things like Webpack to bundle all our modules together, split the code into vendor files, our files, then cacahe redundant files, eliminate unused code, making our overall code easier to track/ship, generate less requests, and work on devices without much reliable internet giving our users a better user experience no matter where they are!!!!!!! (Okay sorry for making this complex again by mentioning Webpack lol).
See our talk on Webpack 4 on YouTube for more info (if interested in that rabbit hole)
Decorators are like functions that augment other functions (sometimes called wrappers).
You see them a lot in Angular syntax.
function log(User) {
return (...args) => {
console.log(`New user created ${args}`);
return new User(...args);
};
}
@log
class User {
constructor(name, age) {
console.log(`New user entered the chat: Name: ${name}, Age: ${age}`)
}
}
const u = new User('Graham', 34);
The idea with decorators is to make it so we can bury "startup" code elsewhere and just use "annotation" style syntax to augment classes with special powers -- in this case log every User
/* These are JavaScript import statements. Angular doesn’t know anything about these. */
import {
BrowserModule
} from '@angular/platform-browser';
import {
NgModule
} from '@angular/core';
import {
AppComponent
} from './app.component';
/* The @NgModule decorator lets Angular know that this is an NgModule. */
@NgModule({
declarations: [
AppComponent
],
imports: [ /* These are NgModule imports. */
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
Can you see how a decorator augments our AppModule class here?
Want more on Angular? Check out Angular presentation here.
TypeScript is a language offered by Microsoft that adds types to JavaScript; much like you have types in other programming languages like Python, C++, Java.
Common types: ints, booleans, floats, decimals, strings (stack variables), chars, and heap variables (objects). You can also use TS with ES6, React, etc by configuring a simple tsjson.config file
Types can be useful, especially when dealing with memory; you could hope that JS does its job, but you never know what monsters lurk under the hood.
Git repo set up for you to play around:
https://github.com/vijayxtreme/starterTSWebpack
Documentation on Config for Webpack and TS at:
https://webpack.js.org/guides/typescript/
Full blog post on TypeScript here:
class Student {
fullName: string;
constructor(public firstName: string, public middleInitial: string, public lastName: string) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
let user = new Student("Jane", "M.", "User");
document.body.textContent = greeter(user);
Note the use of ES6 with types (stricter JS here)
Source: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html
If you were overwhelmed (lol) and want to visit this again with me holding your hand even further, sign up for the video lecture at:
https://hackbuddy.teachable.com
(8 hours of video)
You can also get the book on Amazon.com (if you like reading):
Your purchases help keep the group going and generating more helpful content, lectures, meetups and interviews!
By JavaScriptLA
Slides.com website for JavaScriptLA -- use this place to find our meetup group presentations. Want more? Visit our website at: https://www.javascriptla.net to get on our mailing list.