Page 95
Javascript has around 40 reserved keywords that cannot be used as names for variables or functions such as else for if swtich
There are some that are only reserved if in strict mode or in other specific circumstances
Full list of reserved keywords
Old way is to use var. Don't use this anymore
use const whenever it makes sense, otherwise let
One reason to avoid var is that it is scoped to a function, or is global if not in a function.
let / const are block scoped, which is now the norm in most languages.
const is a little bit unintuitive as for simple variables types of string or integer, a const cannot be changed once defined, but for arrays and objects, while the array/object can't be re-declared, the contents of it can be changed.
undefined and "undeclared" are different
You can create an undefined variable. The variable exists, but it has no value.
If you never create the variable, it is undeclared, and trying to use it will throw an error.
The stupid part is the error you get from having an undeclared variable uses the term "undefined"
let myvar; //undefined
console.log typeof myvar);
// "undefined"
console.log(someothervar); // ReferenceError someothervar is not defined
undefined and null
Both sort of mean nothing is assigned.
If you declare a variable but don't assign any value, it is undefined
For a variable to be null, null has to explicitly assigned to it.
let myName;
console.log(myName); // undefined
let myName = null;
console.log(myName);// null
//another strange js quirk
console.log(typeof null); // object
assign multiple variables on 1 line.
let km = 3023;
let car = "holden";
let km = 8474, car = "holden"
There are 7 primitive data types
all numbers in JS are stored as a 64-bit float.
5e-324 to 1.79+308
The largest integer that can be stored as a number type is Number.MAX_SAFE_INTEGER; which is 9,007,199,254,740,991 (~ 9 quadrillion)
If bigger integers are needed, use the bigint data type
If an operation is attempted that is impossbile to calculate, the result will be NaN (not a number). NaN is not it's own type, but a number value.
Use Number.isNAN(variable) to check for this value
A non-template string can be enclosed in single or double quotes.
Generally pick on that minimises the esacpe character
If you need an apostrophe in a string, use double quotes to enclose it, and vice versa if you want your string to contain double quotes, such as html text
Try to be consistent within a code base.
Best to just standardise on one and stick to it
You can check if text is contained in a string with the .includes() method
(.include()) can also be used on arrays.
const str = "Hello, World!";
console.log(str.includes("world"));
// false
console.log(str.includes("World"));
// true
used to interpolate dynamic values into strings.
Often variables, but can be js code
Must use a ` (backtick) as a delimeter and variables are enclosed in ${}
const name = "Craigus";
console.log(`Your name of ${name} is a great name.`);
const price = 2.5;
const quantity = 3;
const item = "coffee";
console.log(`I need ${item.toUpperCase()}`); // I need COFFEE
console.log(`Total: $${price * quantity}`); // Total: $7.5
boolean variables have two possible value literals: true | false
The terms truthy and falsy are used in javascript.
This means that various things are evaluated as either true or false when used in expressions.
A Truthy value is something considered True and a Falsy is considered False.
Not all of them are intuitive, so here is the list
[ ] or ["gagf","cuntos" ] an array, empty or notBasically, if not a falsy value, it's going to be Truthy
The boolean function will return if a value is true or false.
console.log(Boolean("gagf")) // true
console.log(Boolean("")) //false
console.log(Boolean(1) // true
console.log(Boolean(0) // false
console.log(Boolean("0") // true
declared, but not assigned a value
let something;
console.log(something); // undefined
intentional absence of any value
let emptyvar = null;
null -> how the developer signals emptiness
undefined -> how javascript signals emptiness
ES6+
A unique and immutable primitive
let id1 = Symbol('id');
let id2 = Symbol('id');
console.log(id1 === id2); // false
ES2020+
For integers greater than what the number type can handle.
let bigNumber = 9007199254740991n;
let anotherBig = BigInt(1234567890);
let result = bigNumber + 1n; // works
let result2 = bigNumber + 1; // will not work. Cant mix bitint and numbers
Unlike most languages, array elements can contain different datatypes.
let myarray = ["craig", 55, true]
push()
pop()
unshift()
shift()
These are stored and passed by reference, not by value
(primitive types are passed by value)
let person = {
name: "Craigus",
age: 55
};
// The following are all objects
let myarray = [1,2,3];
let regex = /abc/;
let date = new Date();
function hi() {
return "hello"
}
const myArrow = () => { console.log("gagf");}
= assignment
=== strictly equal to (compares both value and type)
!== strictly not equal to
== equal to (only compares value, so 5 == "5" is true)
!= not equal to
< less then
<= less then or equal to
> greater than
>= greater than or equal to
&& and
|| or
! not
& and
| or
^ xor
~ not
<< shift left. fills 0 from the right
shift right. preserves sign bit
unsigned shift right. fills with 0 from the left
= assignment operators can be chained
a = b = c = 5; a, b & c all equal 5
condition ? expr If True : expr If False
const price = isMember ? "$2.00" : "$10.00";
Evaluates two operands and returns the value of the second one.
Often used for multiple assignments of variables
let a = 10, b = 5
If you aren't sure if an objects property or method exists, you can use the optional chaining operator ?. so that if it doesn't exist, you get undefined rather than an error.
Both of the below console.logs should show undefined rather than get an error.
You should only optionally chain when you expect an object, or nested object may not exist.
Don't use it if the object should exist.
const adventurer = {
name: "Alice",
cat: {
name: "Dinah",
},
};
const dogName = adventurer.dog?.name;
console.log(dogName);
console.log(adventurer.someNonExistentMethod?.());
This operator is a way to handle cases where a value may be null or undefined
If the value on the left is null or undefined, the value on the right is returned.
let myName = null;
console.log(myName ?? "Anonymous"); // "Anonymous"
myName = "Craigus";
console.log(myName ?? "Anonymous"); // "Craigus"
javascript will fall through by default, so a break/return statement will be required at the end of each case if this isn't desired.
const os = "mac";
lst creator;
switch (os) {
case "linux":
creator = "Linus";
break;
case "windows":
creator = "Bill";
break;
case "mac":
creator = "Steve";
break;
default:
creator = "Unknown";
break;
}
You need to define the parameters accepted by a function, but not what it returns
You also can't return multiple values. If you attempt to return multiple values, there will be no error. It will just return the last value.
function getUser() {
return "name@domain.com", 21, "active";
}
// only "active" is returned
To return multiple values, return an Object.
otherwise known as a self-executing anonymous functions.
// standard IIFE
(function () {
// statements...
})();
// arrow function version
(() => {
// statements..
})();
// async iife
(async () => {
// statements..
})();
It can be useful to define and then immediately execute a function.
// Normal function
function result(a, b) {
return a+b;
}
console.log(result(6,9));
15
// IIFE
const result = (function (a, b) {
return a + b;
})(6,9);
console.log(result);
15
There are 3 ways to declare a function
The normal way to declare a function
function add(x, y) {
return x + y;
}
this bindingarguments objectconst add = function(x, y) {
return x + y;
}
this bindingarguments objectCan be:
const add = (x, y) => {
return x + y;
}
// or even shorter
const add = (x, y) => x + y;
this binding is from surrounding scope (solves context issues for some situations)this, so can't be used at methodsnew keyword)// Traditional simple anonymous function
(function (a) {
return a + 100;
});
1. Remove the word 'function' and place arrow between the arguemtn and opening body brace
(a) => {
return a + 100'
};
2. Remove the body braces and 'return' (return is implied)
(a) => a + 100;
3. Remove the parameter parentheses. (only if 1 parameter)
a => a + 100;
// Traditional anonymous function with 2 parameters
(function (a, b) {
return a + b + 100;
});
// Arrow function
(a, b) => a + b + 100;
// Traditional anonymous with no parameters)
(function () {
return a + b + 100;
});
//Arrow
() => a + b + 100;
The braces around an arrow function can only be omitted if the function directly returns an expression. If the body has statements, the braces are required.
// Traditional anonymous
(function (a, b) {
const chuck = 42;
retirm a + b + chuck;
});
// Arrow
(a, b) => {
const chuck = 42;
retirm a + b + chuck;
};
// Arrow to a variable
const add = (x, y) => {
return x + y;
}
Javascript objects are quite versatile.
Object literals are often used to store data in key-value pairs (often a map or dictionary in other languages), but they can also be used for classes and prototypes.
const car = {
model: "Commodore",
engine: 6.6,
colour: "black",
};
console.log(car.model);
//Commodore
The key: value syntax is the normal way, but if you want the key to have the same name as the existing variable, the syntax can be shortened.
const model = "Commodore";
const engine = 6.6;
const car = {
model: model, // normal syntax
engine, // shortened syntax. same as engine: engine
};
The keys can be hardcoded with dot notation, or use strings, and therefore variables as keys.
All 3 console.logs work, but the last one can be changed at runtime.
const car = {
model: "Commodore",
};
const item = "model";
console.log(car.model);
console.log(car["model"]);
console.log(car[item]);
Objects can contain functions called methods. These functions can use this to access and change properties of the object.
.this apparently doesn't work in arrow functions??
const campaign = {
getRemainingMessages() {
return this.maxMessages - this.sentMessages;
},
maxMessages: 100,
sentMessages: 30,
name: "Welcome Campaign",
};
Methods are not bound to the objects by default
const user = {
name: "Craigus",
sayHi() {
console.log(`Hi, my name is ${this.name}`);
},
sayFuck() {
console.log("Get fucked");
},
};
// This works.
user.sayHi();
const sayHi = user.sayHi;
// The function still runs, but not correctly if using `this`.
// This doesn't work now that it's being run as a standalone function and not as a methof of an object. `this` is now in a global context (or undefined in strict mode)
sayHi();
// Bind it to make it work.
const sayHello = user.sayHi.bind(user);
sayHello();
const logindetails = {
"craigus": "password1",
"hammondus": "gagf7777",
"user3": "stupidpassword1"
}
// return an array of object keys
console.log(Ibject.keys(logindetails));
// ['craigus','hammondus','user3']
// return an array of object values
console.log(Object.values(logindetails))
// ['password1','gagf777','stupidpassword1']
// return an array that contains an array for each key/value pair
console.log(Object.entries(logindetails))
// [['craigus','password1'], ['hammondus','gagf7777']]
You can use the spread syntax to shallow copy object properties.
const engineering_dept = {
craig: "biggus dickus",
mark: "smallus pensis",
};
const software_dept = {
bill: "bill the bull",
sum: "the days highlight",
}
// Create new object that contains both previous objects.
const all_employees = { ...engineering_dept, ...software_dept }
Lets you destructure, or unpack, the elements that make up an object into distinct variables.
const user = {
name: "Craig",
id: 42,
isVerified: true,
};
const { name, id, isVerified } = user;
console.log(name);
console.log(id);
// Destructure to different variable names.
const { name: firstname, id: personId } = user;
console.log(firstname);
Destructing also works if you pass an object in as a function parameter so within the function, you can have seperate variables.
// Instead of doing the following:
function showApple(apple) {
console.log(`the apple is ${apple.colour} and has a radius of ${apple.radius}`};
}
// You can destructure in the function signature
function showApple({ radius, colour }) {
console.log(`the apple is ${colour} and has a radius of ${radius}`};
}
Creating an array.
While you can use new Array(), using array literals is considered a better way
let catNames = new Array("Larry", "Fuzzball","Squeeky"); // not recommended
let dogNames = ["Shaggy", "Spot", "Woofy"]; // the recommended way
// Add to an existing array
dognames[5] = "Knobby";
console.log(dognames)
console.log(dognames.length)
// ["Shaggy", "Spot", Woofy", undefined, undefined, "Knobby"]
// 6
arrays can also be destructured.
const nums = [1, 2, 3];
function double([a, b, c]) {
return [a * 2, b * 2, c * 2];
}
const [x, y, z] = double(nums);
console.log(x, y, z);
// 2 4 6
If you're only interested in first element, you can destructure just that element
const [x] = double(numbs);
If you're not sure how many elements there are, you can use the rest operator ... to capture the rest of the elements. These ... elements will always be destructured into an array.
const [x, ..theRestofThem] = double(numbs);
console.log(x);
// 2
console.log(theRestofThem);
// [4, 6]
every array has a bunch of around 60 methods available to it such as:
concat() a new array made up of current array joined with other arrays / values
pop() remove last element from array
push() add new item to end of array
There are 4 levels of scope for variables and funmctions, and there are differences depending on the environment (browser vs node.js)
Global Scope
Can be accessed amuwjere om the code.
In browsers, global variables are properties of the window object. window.myGlobalVar = "hello";
In node.js, it is the global obejct, so global.myGlobalVar = "hello";
Module Scope
Variables declared at the top level of a modules are scopes to that module, not the global scope.
In a browser, using <script type="module"> creates a module scope for that script
Function Scope
Variables declared with var are limited to the function scope. They are accessible only within that fucntion and any nested functions
Block Scope
Variables declared with let / const is scoped to within the {} block they are defined in
Classes are syntatic sugar for prototypes, which is the real underlying mechanism for inheritance.
Classes in javascript are templates for creating objects, but unlike many other languages, it's easy to create objects without classes.
Classes are still useful in js.
class declaration creates a new class
constructor method is a special method that's called when a new instance of the class is created
new keysword calls the constructor method and creates a new instance of the class
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const user = newUser("Craigus", 69);
By default, all properties of a class are public.
To make it so that the createdAt field is private and not directly accessable, declare it at the start with a #, then include the # when that field is used elsewhere in the class.
Below createdAt is private and only viewable from the showCreated method.
class Message {
#createdAt;
constructor(recipient, sender, body) {
this.recipient = recipient;
this.sender = sender;
this.body = body;
this.#createdAt = new Date();
}
showCreated() {
return this.#createdAt
}
}
// don't touch below this line
const message = new Message("555-1234", "555-5678", "Hi there!");
console.log("Attempting to access the property createdAt...");
console.log("createdAt: " + message.createdAt); // this wont work.
console.log(message.showCreated())
These are bound to the class itself, not to instances (objects) of the class.
Below, there is a static property called numUsers that show the number of users, and a static method to retrive it.
These are accessed by the class name, not via any object from that class.
class User {
static numUsers = 0;
constructor(name, age) {
this.name = name;
this.age = age;
User.numUsers++;
}
static getNumUsers() {
return User.numUsers;
}
}
const lane = new User("Lane", 30);
console.log(User.getNumUsers()); // 1
const allan = new User("Allan", 30);
// The static properties / methods are accessed via the class name.
console.log(User.getNumUsers()); // 2
console.log(User.numUsers);
// This doesn't work because its not a method on the object, just on the class
console.log(lane.getNumUsers());
These are used to define methods to get and set properies. They look like regular methods, but are accessed like properies.
One use case would be to validate data when accessing properties.
_age is used inside the class so not to create a name collision with the name of the getter method called age.
class User {
constructor(name, age) {
this.name = name;
this._age = age;
}
get age() {
return this._age;
}
set age(value) {
if (value < 0) {
throw new Error("Age can't be negative.");
}
this._age = value;
}
}
const craigus = new User("Craigus", 29);
craigus.age = -5; // "Age can't be negative."
console.log(craigus.age); // 29
A class can inherit methods and properties from a parent class using extends
The subclass can add methods, or override ones from the parent class.
Sometimes
class Animal {
constructor(name) {
this.name = name
}
speak(msg) {
throw new Error ("speak method must be implemented by subclasses")
}
walk() {
console.log(`${this.name} is walking........`)
}
}
class Person extends Animal {
speak(msg) {
console.log(`${this.name} bellows: ${msg}`)
}
}
const animal1 = new Animal("Craigus");
const person1 = new Person("Hammondus");
person1.speak("hello");
animal1.walk()
Creating then changing classe properties and medthods without the class syntatic sugar.
const pureTitan = {
name: "Shirty's mum",
speak() {
console.log("*titan noises*");
}
};
const beastTitan = Object.create(pureTitan);
console.log(beastTitan.name);
beastTitan.name = "cocko";
console.log(beastTitan.name);
beastTitan.speak()
// Change the speak method on beastTitan
beastTitan.speak = function() {
console.log("GAGF");
}
beastTitan.speak()
Similar to above, but with classes.
class PureTitan {
constructor() {
this.name = "Shirty's Mum";
}
speak() {
console.log("*titan noises*");
}
}
class BeastTitan extends PureTitan {
constructor() {
super();
}
speak() {
console.log(`${this.name} says. "I'm the Beast Titan"`);
}
}
const beastTitan = new BeastTitan();
beastTitan.speak();
beastTitan.name = "Craigus";
beastTitan.speak();
beastTitan.speak = function() {
console.log("gagf");
}
beastTitan.speak();
You can call a method from the parent class with super
If super is used within a construtor method, the parents constructor is called.
When super is used in a normal method, use super.parentmethod()
class Titan {
constructor(name) {
this.name = name;
}
toString() {
return `Titan - Name: ${this.name}`;
}
}
class BeastTitan extends Titan {
constructor(name, power) {
// call the parent's constructor
super(name);
this.power = power;
}
toString() {
// call the parent's `toString` method
return `${super.toString()}, Power: ${this.power}`;
}
}
const wimp = new Titan("cocko");
const beast = new BeastTitan("Zeke", 9000);
console.log(beast.toString());
// Titan - Name: Zeke, Power: 9000
console.log(wimp.toString())
// Titan - Name: cocko
for (let i = 0; i < 5; i++ ) {
console.log(i);
}
while (condition being true) {
//statements
}
Will always run at least once as condition is evaluated at end
do {
console.log(x);
x++;
} while (x < 5);
Iterates over the enumerable properties of an object.
Typically used for iterating over object keys
const person = {fname:"John", lname:"Doe", age:25};
let text = "";
for (let x in person) {
text += person[x];
}
console.log(text);
// JohnDoe25
iterates through the values of iterable objects such as arrays, strings, maps, sets and nodelists
//Iteratings over a string
const name = "Craigus Hammondus";
for (const x of name) {
console.log(x)
}
// Over an array
const letters = ["a","b","c"];
for (const x of letters) {
console.log(x)
}
const numbers = [2,4,6,8];
for (const x of numbers) {
console.log(x)
}
// Over a map
const fruits = new Map([
["apples", 500],
["bananas", 300],
["oranges", 200]
]);
for (const x of fruits) {
console.log(x)
}
// ["apples", 500]
// ["bananas", 300]
// extract both key band value from map
for (const [key, value] of fruits) {
console.log("value: ", value);
console.log("key: ", key);
}
// value: 500
// key: apples
// value: 300
// key: bananas
Break - breaks out of a loop.
Continue - stops current iteration, and continues with the next one.
for (let i = 0; i < 10; i++) {
if (i == 5 || i == 6) continue;
if (i == 8) break;
console.log(i)
}
// 0, 1, 2, 3, 4, 7
Arrays in javascript are a bit unique in that different elements in an array can be of any data type.
stuff = ["apple", "bananna", 69, true ];
You can easily check if a value exists in an array using the .includes() method in a similar way that .include can be used on strings.
Unlike strings, the whole word has to match when searching arrays, whereas .includes() on a string will also return true for a substring find.
const arraystuff = ["banana", "car", 69, true];
const stringstuff = "banana";
//array
console.log (arraystuff.includes("banana")) // true
console.log (arraystuff.includes("ban")) // false
//string
console.log (stringstuff.includes("banana")) // true
console.log (stringstuff.includes("ban")) // true
const testes = [];
testes.push("gagf");
Sets are objects that let you store unique values of any type.
They are a good way for de-deuplication and checking if a value exists in a collection
There are other types of sets. Ones used more often are intersection, difference and union
A complete list of sets
const set = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(set);
// Set { 1, 2, 3, 4, 5 }
You can add and delete from a set
const set = new Set();
set.add("bertholdt");
set.add("reiner");
set.add("annie");
set.add("bertholdt");
console.log(set);
// Set { 'bertholdt', 'reiner', 'annie' }
set.delete("annie");
console.log(set);
// Set { 'bertholdt', 'reiner' }
returns a new set containing the elements that are in both sets
const heroes = new Set(["eren", "mikasa", "armin", "reiner"]);
const villains = new Set(["eren", "reiner", "bertholdt", "annie"]);
const samesies = heroes.intersection(villains);
console.log(samesies);
// Set { 'eren', 'reiner' }
returns a new set containing the elements that are in the first set but not the second
const heroes = new Set(["eren", "mikasa", "armin", "reiner"]);
const villains = new Set(["eren", "reiner", "bertholdt", "annie"]);
const nonVillains = heroes.difference(villains);
console.log(nonVillains);
// Set { 'mikasa', 'armin' }
returns a new set containing the elements that are in either set
const heroes = new Set(["eren", "mikasa", "armin", "reiner"]);
const villains = new Set(["eren", "reiner", "bertholdt", "annie"]);
const everyone = heroes.union(villains);
console.log(everyone);
// Set { 'eren', 'mikasa', 'armin', 'reiner', 'bertholdt', 'annie' }
maps are collections that support key-balue pairs that are unique, so adding a key that already exists will replace its value.
The set and delete methods are used to change a map
const ogmap = new Map();
ogmap.set("craigus", "biggus dickus");
ogmap.set("knobbly", "bumps");
ogmap.set("craigus", "smallus pene");
ogmap.set("testes","hairy");
ogmap.delete("testes");
console.log(ogmap);
// Map {craigus: "smallus pene", knobbly: "bumps", constructor: Object}
Maps can be constructed from any iterable object. Maps are iterable, so the Map constructor can accept a map. This creates a copy of the original map
If added to the above code, this would create a copy
const anotherMap = new Map(ogmap);
The below function will update the map, but it will mutate the original map
function addToPhonebook(phoneNumber, name, phoneBook) {
phoneBook.set(phoneNumber, name);
return phoneBook;
}
If you want to return a new map, but leave the original unchanged, you need to create a new map and return that
function addToPhonebook(phoneNumber, name, phoneBook) {
const newBook = new Map(phoneBook)
newBook.set(phoneNumber, name);
return newBook;
}
The built-n error object has a message property which should be a human-readable description of the error.
const err = new Error("You are an idiot");
When an error is thrown, either by your or the runtime, the code execution changes from its current contect to the nearest try/catch block. If there isn't one, it crashes the program.
The following code will crash:
const titan = {};
console.log(titan.neck.thickness);
//Uncaught TypeRrror: Cannont read properties of undefined (reading 'thickness')
Place the potentially error-throwing code in a try block. If an error is thrown, execution immediately jumps to the catch block
try {
const titan = {};
console.log(titan.neck.thickness);
console.log("what's a titan");
} catch (err) {
console.log(err.message);
}
console.log("done!!!");
The code in finally will run regardless if any errors are caught in try/catch
try {
const titan = {};
console.log(titan.neck.thickness);
console.log("what's a titan?");
} catch (err) {
console.log(err.message);
} finally {
console.log("This will always run regardless of any errors.");
}
To have code run with a delay, you can use timeouts.
setTimeout() isn't part of javascript, but is a global function provided by the environment, such as the browser or node.js
setTimeout(
() => console.log("This is delayed"),
1000)
console.log("this is not delayed")
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (getRandomBool()) {
resolve("resolved!");
} else {
reject("rejected!");
}
}, 1000);
});
function getRandomBool() {
return Math.random() < 0.5;
}
There is CommonJS modules and ES modules.
Node.JS wants CommonJS by default
main.js
const { moo } = require("./moo.js");
let name = "Craig";
console.log(moo(name));
moo.js
const moo = (name) => `moo, ${name}`;
// or
function moo(name) {
return `moo, ${name}`;
}
module.exports = {moo};
You can change node.js to use ES6 modules
Either change filename of the javascript file from .js to .mjs or
Chnage the type in package.json to module
"type": "commonjs"
"type": "module"
With named export, you can export functions as you define them, so you have multiple export, or alternatively define functions normally and export them all at once.
named exports must be imported with the same name.
Best for utilities / helper functions.
js file exporting from
export function moo(name) {
return `moo, ${name}`;
}
export function cow(name) {
return `moo cow, ${name}`;
}
or
function moo(name) {
return `moo, ${name}`;
}
function cow(name) {
return `moo cow, ${name}`;
}
export {moo, cow};
// OR
// export just moo with alias
export {moo as moomoo};
js file importing to
import {moo, cow} from "./moo.js";
let name = "Craig";
console.log(moo(name));
console.log(cow(name))
With a default export, you can only do it once in a file.
You can rename the functions when importing though.
Best for main feature
function moo(name) {
return `moo, ${name}`;
}
function cow(name) {
return `moo cow, ${name}`;
}
export default {moo, cow};
importing default
import mooModule from "./moo.js";
let name = "Craig";
console.log(mooModule.moo(name));
console.log(mooModule.cow(name))
Generally, it's a good idea to use strict mode, and is the default for ES6 modules
Advantages
this is undefined in global scope.Can be enable for everything, or on a per function basis
At the top of a js file
"use strict";
Just for a single function
function myStrictFunction() {
"use strict"l
// blah balh
}
In browsers, if your html has multiple script tags, they will be executed in sequence
<script src="first.js"></script>
<script src="second.js"></script>
To use ES^ modules, add `type="module"
<script type="module" src="first.js"></script>
<script type="module" src="second.js"></script>
Advantages of modules