Defense Against The
JAVASCRIPT Dark Arts
http://bit.ly/js-dark-arts
James Q Quick
DEVELOPER. SPEAKER. TEACHER.
@jamesqquick
www.learnbuildteach.com
STICKERS!!!
Tweet me @jamesqquick and include hashtag #LearnBuildTeach
Why This Talk??
JavaScript is SCARY!!
On to the spells...
Mastering Equality
1.
"Dobby is a free elf!"
Double Equals
//Double equals compares loose equality using coercion
1 == '1' //true
false == 0 //true
false == '0' //true
0 == "" // true
"" == false //true
null == undefined //true
"Falsy Values"
//false
//0
//"",
//null
//undefined
//NAN
"Falsy Values"
function printName(name){
if(!name){
console.log("YOU AINT GOT NO NAME!!");
}
else {
console.log(name);
}
}
printName() //YOU AINT GOT NO NAME!!
printName("James") //James
Triple Equals
//Triple equals compares strong equality
//by comparing both type and value without coercion
1 === 1 //true
'Dark Arts' === 'Dark Arts' //true
true === true
1 === '1' //false
false === 0 //false
false === '0' //false
0 === "" // false
"" === false //false
Pass by Value
//Primitives are passed by value
//Primitives - undefined, null, boolean, string, number
let name = "James Quick";
const name2 = name;
console.log(name == name2); //true
console.log(name === name2); //true
name = "Jessica Quick";
console.log(name == name2); //false
console.log(name === name2); //false
Pass by Value (cont.)
name =
"James Quick"
name2 =
"James Quick"
let name = "James Quick";
const name2 = name;
console.log(name == name2); //true
console.log(name === name2); //true
name = "Jessica Quick";
console.log(name == name2); //false
console.log(name === name2); //false
Pass by Value (cont.)
name =
"Jessica Quick"
name2 =
"James Quick"
let name = "James Quick";
const name2 = name;
console.log(name == name2); //true
console.log(name === name2); //true
name = "Jessica Quick";
console.log(name == name2); //false
console.log(name === name2); //false
Pass By Reference
//Objects are passed by reference
const p1 = {
first: 'James',
last: 'Quick',
}
let p2 = p1;
console.log(p2 === p1); //true
p1.first = "Jessica";
console.log(p2.first) //"Jessica"
p2 = { ...p1 };
console.log(p2 === p1); //false
Pass by Reference (cont.)
p1 =
p2 =
{
first: 'James',
last: 'Quick'
}
const p1 = {
first: 'James',
last: 'Quick',
}
let p2 = p1;
console.log(p2 === p1); //true
p1.first = "Jessica";
console.log(p2.first) //"Jessica"
p2 = { ...p1 };
console.log(p2 === p1); //false
Pass by Reference (cont.)
p1 =
p2 =
{
first: 'Jessica',
last: 'Quick'
}
const p1 = {
first: 'James',
last: 'Quick',
}
let p2 = p1;
console.log(p2 === p1); //true
p1.first = "Jessica";
console.log(p2.first) //"Jessica"
p2 = { ...p1 };
console.log(p2 === p1); //false
Pass by Reference (cont.)
p1 =
{
first: 'Jessica',
last: 'Quick'
}
const p1 = {
first: 'James',
last: 'Quick',
}
let p2 = p1;
console.log(p2 === p1); //true
p1.first = "Jessica";
console.log(p2.first) //"Jessica"
p2 = { ...p1 };
console.log(p2 === p1); //false
p2 =
{
first: 'Jessica',
last: 'Quick'
}
Use triple equals and make true copies of objects to avoid confusion.
ES6 Features
2.
YOU MUST KNOW ES6!!
Default Parameters
const add = (num1, num2) => {
return num1 + num2;
}
console.log(add()) //NaN
Default Parameters ES5
const add = (num1, num2) => {
if (num1 === undefined) {
num1 = 0;
}
if (num2 === undefined) {
num2 = 0;
}
return num1 + num2;
}
console.log(add()); //0
Default Parameters ES5
const add = (num1, num2) => {
num1 = num1 || 0;
num2 = num2 || 0;
return num1 + num2;
}
console.log(add());
Default Parameters ES6
const add = (num1 = 0, num2 = 0) => {
return num1 + num2;
}
console.log(add()); //0
Default Parameters ES5 Again
const addFromObject = (obj) => {
return obj.num1 + obj.num2;
//cannot read property of undefined
}
console.log(addFromObject());
Default Parameters ES6 Again
const addFromObject = ({ num1 = 0, num2 = 0 } = {}) => {
return num1 + num2;
}
console.log(addFromObject()); //0
Default Parameters Error Function
function requiredArg() {
throw new Error('The argument is required');
}
function add(x = requiredArg(), y = requiredArg()) {
return x + y;
}
add()//Error THE ARGUMENT IS EQUIRED
Template Literal Strings
let markup = "<html>\n\t<head>\n\t</head>\n\t<body>\n\t</body>\n</html>"
markup = "<html>\n\t<head>\n\t" +
"</head>\n\t<body>\n\t</body>\n</html>"
markup =
`<html>
<head>
</head>
<body>
</body>
<html>`;
Template Literal Strings
const printPerson = (person) => {
console.log(person.first + " " + person.last + " lives in " + person.city + "." );
console.log(`${person.first} ${person.last} lives in ${person.city}.`);
}
printPerson({first: "James", last: "Quick", city: "Memphis"});
Spread Operator - Arrays
//Make shallow copies of arrays with the spread operator
const arr1 = [1, 2, 3, 4];
const arr2 = arr1;
arr1[0] = 15;
console.log(arr2[0]); //15
console.log(arr1 == arr2); //true
const arr3 = [...arr1];
console.log(arr3 == arr1); //false
Spread Operator - Objects
//Make shallow copies of objects with the spread operator
const p1 = {
first: 'James',
last: 'Quick',
};
let p2 = p1;
console.log(p2 === p1); //true
p2 = { ...p1 };
console.log(p2 === p1); //false
Deep Copy? Use lodash
OR...
JSON.parse(JSON.stringify(obj))
Other ES6 Features
- Arrow Functions
- Promises
- Enhanced Object Literals
- Rest Operator
- Const and Let variables
YOU MUST KNOW ES6!!
JavaScript 30 by Wes Bos
Asynchronous JavaScript
3.
JavaScript is SINGLE THREADED
Callbacks
//Run code after a delay
setTimeout(() => {
console.log("1 second");
}, 1000);
//Add click event handler for a button
button.addEventListener('click', (e) => {
console.log("Button was clicked");
});
Callback Hell
setTimeout(() => {
console.log("5");
setTimeout(() => {
console.log("4");
setTimeout(() => {
console.log("3");
setTimeout(() => {
console.log("2");
setTimeout(() => {
console.log("1");
}, 1000)
}, 1000)
}, 1000)
}, 1000)
}, 1000)
//5...4...3...2..1
Promises
const axios = require('axios');
const url = "http://api.icndb.com/jokes/random?firstName=James&lastName=Quick"
axios.get(url)
.then( res => {
console.log(res.data.value.joke);
//"Product Owners never ask James Quick for more features. They ask for mercy."
})
.catch( err => {
console.log(err);
});
What if you need to run multiple async actions in a row?
Promise Chaining
const todoURL = "https://jsonplaceholder.typicode.com/todos/1";
const userURL = "https://jsonplaceholder.typicode.com/users/"
axios.get(todoURL)
.then((res) => {
console.log(res.data); //userId = 1
return axios.get(userURL + res.data.userId);
})
.then(res => {
console.log(res.data) //user object
})
.catch(err => {
console.log("ERROR! " + err);
})
Async/Await
const getJokeAsync = async () => {
const url = "http://api.icndb.com/jokes/random?firstName=James&lastName=Quick"
const res = await axios.get(url);
console.log(res.data.value.joke);
}
getJokeAsync();
Async/Await
const getUserFromTodoAsync = async (todoId) => {
const todoURL = "https://jsonplaceholder.typicode.com/todos/" + todoId;
const userURL = "https://jsonplaceholder.typicode.com/users/"
const res = await axios.get(todoUrl);
const userRes = await axios.get(userURL + res.data.userId);
}
Async/Await and Try/Catch
const getTodoAsync = async () => {
const todoURL = "https://jsonplaceholder.typicode.com/todos/1";
const userURL = "https://jsonplaceholder.typicode.com/users/"
try {
const res = await axios.get(todoUrl);
const userRes = await axios.get(userURL + res.data.userId);
} catch (ex) {
console.log("ERROR!" + ex);
}
}
getTodoAsync();
Formatting and Linting
4.
What is LINTING?
Linting Benefits
- Consistent Code Formatting
- NO const reassignment and no undeclared variables
- Better timing/performance with Asynchronous JS
- No console.log()
Formatting Before and After
const name = "James";
const person = { first: name,last:"Quick"}
function printPersonName(p){
console.log(p.first, p.last);
}
...
const name = 'James';
const person = { first: name, last: 'Quick' };
function printPersonName(p) {
console.log(p.first, p.last);
}
No Const Reassignment
No Undeclared References
Better Async Performance
No Console Log
Write BETTER code FASTER
Linting Setup
- Install eslint package to project
- Use `eslint --init` command
- follow prompts to create eslint configuration (creates .eslintrc file)
- Install eslint extension for VS Code
- Go into settings and check "Eslint: Auto Fix on Save"
Linting JavaScript
npm init
npm install --save eslint
eslint --init
ESLint Configuration File
{
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}
Preconfigured with the top front-end frameworks
TypeScript
5.
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
The benefits...
Benefits of TypeScript
- Optional static typings
- Converts to JavaScript
- Easily define object definitions with Interfaces
- Documentation and Intellisense
Static Bindings - 1
function ICanAdd( num1, num2) {
return num1 + num2;
}
ICanAdd(1,2);
ICanAdd("James", "Quick");
ICanAdd({}, []);
Static Bindings - 2
function ICanAdd(num1: number, num2: number) {
return num1 + num2;
}
ICanAdd(1, 2);
ICanAdd("James", "Quick"); //CANT DO THAT!
ICanAdd({}, []); //CANT DO THAT!
Static Bindings - 3
function ICanAdd(num1: number, num2: number):number {
return num1 + num2;
}
const num:number = ICanAdd(1, 2);
Static Bindings - 4
function ICanAdd(num1: number, num2: number):number {
return num1 + num2;
}
const num = ICanAdd(1, 2); //type of number is optional since function returns a number
Static Bindings - 5
function ICanAdd(num1: number, num2: number):number {
return num1 + num2;
}
const num = ICanAdd(1, 2);
num = "James" //YOU CAN"T DO THIS: num is of type number
Compilation -Before/After
const message: string = 'hello world';
const first = 'James';
const person = { first, last: 'Quick', age: 28 };
const { last, age } = person;
console.log(`${first} ${last} is ${age} years old!`);
var message = 'hello world';
var first = 'James';
var person = { first: first, last: 'Quick', age: 28 };
var last = person.last, age = person.age;
console.log(first + " " + last + " is " + age + " years old!");
Try out the TypeScript Playground
Interfaces/Models
interface Person {
first: string,
last: string,
age:number,
children: [],
spouse:Person
}
Documentation
Setup TypeScript
- Install TypeScript via NPMx
- Use tsc command followed by TypeScript file
- Create TypeScript configuration file
- Install TSLint extension VS Code
TypeScript Config
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
}
Configurable with the top front-end frameworks
Debugging
BONUS:
console.log()
Visual Studio Code
Coupon Code - KCDC2019
Wrap Up and Resources
Useful Links
- JavaScript Double Equals vs Triple Equals
- JavaScript Truthy
- JavaScript Objects - Getting Started
- JavaScript Arrays - Getting Started
- JavaScript Promises - Getting Started
- JavaScript Fetch API - Getting Started
- Explaining Value vs. Reference in JavaScript
- JavaScript 30 by Wes Bos
- ESLint
- TypeScript
- Debugging JavaScript in Chrome and VS Code
www.learnbuildteach.com
STICKERS!!!
Tweet me @jamesqquick and include hashtag #LearnBuildTeach