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
THANK YOU!
@jamesqquick
Defense Against the JavaScript Dark Arts
By James Quick
Defense Against the JavaScript Dark Arts
In the magical world of JavaScript, you have the power to do almost anything, but with great power comes great responsibility. Do you have the magical prowess to protect yourself from the darkest depths of JavaScript? In this talk, we discuss 5 spells that you can use protect yourself from the most common JavaScript pitfalls. Some of these are as easy as a swish and flick of the old magic wand thanks to modern JavaScript tooling, but the rest comes from establishing best practices and a new mindset. After this talk, you will walk away head high and wand at the ready!
- 1,175