Learn JavaScript
Learn JavaScript
Learn JavaScript
tom.a.scott@hotmail.co.uk
JavaScript
Notes
Lydia Hallie
JavaScript Fundamentals 4
1.1 Primitive Data Types 4
1.2 Variables 5
1.2.1 Var, const and let 6
1.2.2 Coercion 6
1.3 Operators 6
1.3.1 Assignment operators 7
1.3.2 Comparison operator 7
1.3.3 Arithmetic operators 8
1.3.4. Logical operators 9
1.3.5 Operator precedence and associativity 10
1.4. Conditionals 12
1.4.1 If-else statements 12
1.4.2 Conditional operator 12
1.4.3 Switch statements 13
1.5 Arrays 14
1.6 Objects 15
1.6.1 Dot Notation 15
1.6.2 Bracket Notation 15
1.6.3 Dot Notation vs. Bracket Notation 16
1.6.4 Object methods 17
1.7 Functions 18
1.7.1 Function expressions 19
1.7.2 Function statements 19
1.7.3 Arrow functions 20
1.7.4 IIFE 21
1.7.5 Callback Functions 21
1.7.6 Promises 22
1.7.7 Async/await 24
1.8 Loops 25
1.8.1 For loops 25
1.8.2. For/in loops 26
1.8.4 Do/while loops 27
1.8.5 Break and continue 28
1.9 Common Methods 28
1.9.1 String methods and properties 28
1.9.2 Array methods and properties 32
1.9.3 Math Object 38
1.10 DOM Manipulation 40
1.10.1. DOM Methods and Properties 41
1.10.2. Bubbling and Capturing 44
1.10.3. Prevent default 46
1
Understanding JavaScript
2.1 Syntax Parser 46
2.2 Creation phase 46
2.2.1 The this keyword 47
2.3 Lexical Environment 47
2.4 Creation and hoisting 48
2.5 Execution stack 49
2.5.1 Event Loop 51
2.6 Variable scope 52
2.7 The scope chain 53
2.8 Closures 54
2.9 Closures and callbacks 57
2.10 bind(), call(), and apply() 58
2.11 Functional programming 59
2.11.1 Higher-order functions 59
2.12 Objects 60
2.12.1 Computed member access operator 60
2.13 JSON and Object Literals 61
2.14 Functions are objects 61
2.15 Function statement vs. function expression 62
2.16 By value vs. by reference 62
2.17 The keyword arguments and the spread operator 63
2.18 Inheritance 64
2.18.1 Understanding the prototype 64
2.19 Primitive or object 66
2.20 Function constructors and the keyword new 66
2.21 Prototype 67
2.22 Built-in function constructors 68
2.23 Classes 70
2.24 Static methods 71
2.25 Polyfill 72
2.26 Strict mode 72
2
This is a summarized collection of personal notes that were taken in order
to learn JavaScript, and combines both basic ES5 and ES6 features. This
book is not meant to replace any courses or other study methods, and
may contain information that is outdated or incomplete.
3
JavaScript Fundamentals
4
1.2 Variables
Variables are containers that you can store values in. In the first
example, we declare a variable, then give it a value.
Normally, you wouldn’t take these two steps. You’d simply type:
Instead of the let keyword, you could in some cases also use var or
const, however they are not the same.
let myNumber = 5;
myNumber = "five";
First, we gave the variable myNumber the value 5, which type is a
number. Later, we redeclare the variable myNumber and give it the value
of "five", which type is a string. This phenomenon is called coercion.
var: If it’s not declared inside a function, that variable is accessible
anywhere in your code. If it’s declared inside a function, it’s accessible
anywhere in that function.
let: Is only accessible in the block you’ve declared it in.
const: Works the same as var, but its value can’t be reassigned.
5
1.2.2 Coercion
Converting a value from one type to another. This is possible because
JavaScript is dynamically typed. Example:
1.3 Operators
Assignment x = y x = y
Addition x += y x = x + y
Subtraction x -= y x = x - y
Multiplication x *= y x = x * y
Division x /= y x = x / y
Remainder x %= y x = x % y
Exponentiation x **= y x = x ** y
6
1.3.2 Comparison operator
Compares its operands and returns a boolean based on whether the
comparison is true or false.
Name Notation
Often, if the two operands are not of the same type, JavaScript attempts
to convert one or both operands to an appropriate type in order to
compare the two. For example, myNum == 0 also returns true if
myNum = '0', or even if myNum = false. JavaScript converts the string and
the boolean to a number in order to be able to make the comparison,
without you really knowing.
If you use strict equal or strict not equal, it will really only return true if
the type of the value equals the type of the value in the comparison. So
myNum === 0 would return false if myNum = '0', because the type of the
operand in the comparison is a number, and not a string. It’s advised to
use strict equal and strict not equal in almost all cases.
7
1.3.3 Arithmetic operators
Takes numerical values as their operands and returns a single numerical
value.
Unary operation: an operation with only one operator.
Binary operator: an operation that operates on two operands and
manipulates them to return a result.
x = 3, +
+x sets x to 4 and returns 4
x = 3, x ++ returns 3, then sets it to 4
+true // 1
+"3" // 3
8
1.3.4. Logical operators
Logical operators often return a boolean value.
Falsy: Values that evaluate to false. There are six falsy values:
undefined, null, NaN, 0, "", and false.
These values return false if we converted them into a boolean.
Truthy: Values that evaluate to true. The easiest way of knowing
whether it would be truthy, is by checking if it wouldn’t be falsy.
And expr1 && expr2 Returns true if both expressions return true.
With non-boolean values, it will return the
right operand if both values are truthy.
9
new (with arguments) n/a new … (...)
17 Postfix n/a … ++
Increment/Decrement … --
Unary Plus +…
Unary Negation -…
Division …/…
Remainder …%…
Subtraction …-…
in … in …
instanceof … instanceof …
10 Equality left-to-right … == …
Inequality … != …
10
9 Bitwise AND left-to-right …&…
5 Logical OR left-to-right … || …
3 Assignment right-to-left … =…
… += …
… -= …
… **= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
yield* yield* …
1.4. Conditionals
In your code, you sometimes have to make decisions. The simplest way
of doing so is with if-else statements.
11
let myAge = 19;
if (myAge >= 18) {
console.log("I am older than 18!");
} else if (myAge > 5 && myAge < 10) {
console.log("I am older than 5 and younger than 10!");
} else {
console.log("I am older than 5 and younger than 10!");
}
This if-statement will log "I am older than 18!" in the console, as
myAge >= 18 returns true.
If condition returns true, the variable has the value of val. Otherwise, it
has the value of val2. You can read the ? as the question “Did the
condition just return true?” If that question gets answered with “yes”, the
first value will be returned, which is val1 in this case. The : can be read
as the “else”, so if the condition returned false. If that’s the case, then
val2 will be returned. Example:
12
1.4.3 Switch statements
If you have many conditions, it might be annoying to always write the
else statement. In that case, you will use switch statements. Example:
switch (expression) {
case choice1:
// Your code here.
break;
case choice2:
// Your code here.
break;
case choice3:
// Your code here.
break;
default:
// Hmm none of the cases turned out to be true, just run this.
}
Note the colon after the case, the semicolon after break and no break
after default. Don’t use switch cases if you need comparisons.
WRONG RIGHT
switch (age) { switch (age) {
case (age < 18): case 18:
console.log("Kiddo."); console.log("You're 18");
break; break;
case (age >= 18): case 19:
console.log("Adult."); console.log("You're 19");
break; break;
} }
13
1.5 Arrays
console.log(myArray[0]) /
/ 4
console.log(myArray[1]) / / 12
console.log(myArray[2]) / / 1
.push is just one of many built-in methods, to see more go to Methods.
1.6 Objects
14
Our person object has multiple members, in our case do the members
have the key of name, age, gender and interest, and the value of [
"Lydia",
"Hallie"], 19, "female" and ["yoga", "programming", "petting dogs"]. Each
pair is called a key/value pair. There are two ways of accessing object
members.
objectName.keyName = value
person.age = 19;
person.interests = ["yoga", "programming", "petting dogs"];
objectName["keyName"] = value
person["age"] = 19;
person["interests"] = ["yoga", "programming", "petting dogs"];
let x = "age"
/* We declared a variable with the value age, which is already
a key on our object. We would expect that person.x is actually
person.age, and person[x] is person["age"]. */
15
person.x returned undefined, because JavaScript started looking for a key
that was called "x" (a string, because object keys are strings), rather
than the value of the variable. We didn’t have a key/value pair with a key
called "x", so person.x returned undefined. Dot notation only lets you
access the explicit key name of a property.
let bird = {
feathers: "colorful",
size: "small",
}
let mouse = {
name: "cute tiny mouse",
small: true,
}
mouse["small"] = true;
console.log(mouse.bird.size)
// TypeError: Cannot read property 'size' of undefined.
console.log(mouse[bird["size"]])
// true
16
1.6.4 Object methods
There are many different methods you can use in order to get,
manipulate, or delete data from objects. These are the most common:
let myObj = {
firstName: "John",
lastName: "Doe",
gender: "male",
}
17
1.7 Functions
function sayMyName() {
console.log("Your name is Lydia");
}
function whatsMyAge(age) {
console.log(`Your age is ${age}`);
}
whatsMyAge(19); // Your age is 19.
whatsMyAge(7); // Your age is 7.
whatsMyAge(31); // Your age is 31.
18
1.7.1 Function expressions
We can also assign a variable. There are anonymous function
expressions and named function expressions. Anonymous functions
don’t have an identifier to refer to the function. You normally use
anonymous functions as a parameter to other functions. That’s important
to remember when we get to Callbacks and Hoisting.
Anonymous Named
var dog = function() { var dog = function bark() {
console.log('Bark!'); console.log('Bark!');
} }
sayHi(); // Hi
sayHi(); // Hi
19
Arrow functions are a lot smaller and can be inline. It saves a lot of space,
compared to the regular functions.
If you write curly brackets, you have to write return. However, by using
the normal parentheses or none at all, you don’t. You can only do this if
you just return one value! With arrow functions, this refers to its current
surrounding scope, unlike regular functions! We will get back to this when
we talk about the keyword this.
20
1.7.4 IIFE
IIFE stands for Immediately-Invoked Function Expression. Right
after creating the function, we invoke it.
(name => {
console.log(`Hey ${name}`)
})('Lydia'); // "Hey Lydia"
We wrap the function within parentheses, and right after closing the
parentheses we invoke it how we usually invoke functions. This is very
useful when we want to invoke anonymous functions!
The callback function is not run until the containing function invokes it, or
“calls it back”. Once the containing function has stopped running, it calls
back to the function that was passed in the parameter, hence the name
callback function. This example is a synchronous callback, the callback
function is being run where we invoke it. However, often callback
functions are being executed after an asynchronous function has
completed.
1.7.6 Promises
Now, imagine that that callback function contained another callback
function, then that callback function had another callback function. This
would lead to very chaotic code! To avoid that, we use promises. With
promises, we can postpone the execution of a code block until an
21
asynchronous operation is completed. This way, other operations can
keep running without interruption.
A promise may complete at some point and produce a value. Once that
value is ready, it allows you to do something with that value.
Promises look very much like promises in real life. You promise to walk
the dog, after you’ve done a certain task. However, promising something
doesn’t guarantee that you actually do it! You write a promise this way:
promiseToWalkDog.then((fromResolve) => {
console.log(`Did you walk the dog? ${fromResolve}`);
}).catch((fromReject) => {
console.log(`Did you walk the dog? ${fromReject}`);
});
Only when the promise is resolved, the .then method will be fired. If the
promise rejected, the .catch method will be fired. Within that method you
can have a callback function. From resolve([argument]), we pass that
argument to the fromResolve([receives that argument here]) function.
The .catch handles the reject function.
22
You can also promise to clean the room after you’ve walked the dog, after
you’ve cooked food.
cleanRoom()
.then(() => walkDog()) // No semicolon!
.then(() => cleanRoom()) // No semicolon!
.then(() => console.log("All three promises were fulfilled!"));
Note that we are only resolving here! This is why the syntax differs from
the first example. You can’t write a semicolon if you have another .then!
You avoid nested code by using promises. If you want all three promises
to run at the same time, and once all of them are done you want to do
something. This is possible with Promise.all!
1.7.7 Async/await
An async function is syntactic sugar over promises, the code looks much
cleaner. Its asynchronous code looks and behaves a little more like
synchronous code. With async/await, you really wait for one promise to
resolve before moving to the next line.
23
const makeRequest = async () => { const makeRequest = () =>
console.log(await getJSON()) getJSON()
return "All done!" .then(data => {
} console.log(data)
return "All done!"
makeRequest() }
makeRequest();
The function has the keyword async before it. Any async function returns
a promise implicitly, and the resolve value of the promise will be whatever
you return from the function ("All done!" in our case).
console.log(await getJSON()) basically means: wait until the getJSON
promise has resolved, then console.log gets called that prints the value.
It pauses the execution of your method until the value from the promise
is available. If you don’t await, you’ll get the promise instead of the value
you expect! Async await and promises are not logically equivalent!
The async/await loads the results in sequence, while the promise example
loads in parallel. Async/await and promises are the same thing under the
hood. That’s the reason why we can do stuff like this:
wait getFoo();
let foo = a
let bar = a wait getBar();
24
1.8 Loops
// 2
// 4
// 6
// 8
Initialization expression
The first part of our for loop starts with i = 0, or basically: index equals
zero. By saying i = 0, you’re just saying: the loop has to start at the first
item in the array. If we said i=1, the loop would’ve started at the second
item in the array. This part is called the initialization expression and
runs only once!
Conditional expression
JavaScript evaluates this expression before each loop! By writing
i < myArray.length, you are basically saying “keep the loop going as long
as there are items in the array”. Before each and every loop, JavaScript
checks whether the conditional expression returns true or false. If it
returns true, if moves on to the third part of the loop. If it returns false,
the loop ends and JavaScript moves on to the next line of code. You can
replace the myArray.length with any value you like, f.e. i < 5.
Final-expression
The third and last part of the loop increases a value each time the code
block in the loop has been executed, so it moves on to the next item in
the array.
25
1.8.2. For/in loops
let myObj = {
a: 15,
b: 3,
c: 14,
};
// 30
// 6
// 28
We want to loop over our object, and return every value multiplied by 2.
for (const item in myObj)
While the number has a value that’s lower than 10, the statement gets
executed. The statement is myNumber++, which increases the value of
myNumber on every execution. The last number where the condition would
return true, is if myNumber = 9. myNumber then gets incremented one more
time, which makes it 10.
26
1.8.4 Do/while loops
Creates a loop that executes the statement until the condition evaluates
to false. The condition is evaluated after executing the statement, which
means that the statement will always run at least once.
First, it increases the value of the variable myNumber with 1. Then, the
condition evaluates to false, as myNumber isn’t smaller than 0, which ends
the do..while loop.
27
1.9 Common Methods
JavaScript has many built-in methods that you can use. You always have
access to them.
.charAt()
.concat()
.endsWith()
The final index it will look for is 11. In that case, the string indeed ends at
there so it returns true!
28
.includes()
Checks whether a string contains the specified string/characters. It is case
sensitive!
.indexOf()
Returns the position of the first found occurrence of a specified value in a
string.
.lastIndexOf()
Returns the index of the last found occurrence of a specified value in a
string.
.repeat()
Returns a new string with a specified number of copies of an existing
string.
.replace()
Searches a string for a specified value, or a regular expression, and
returns a new string where the specified values are replaced.
29
.slice()
Extracts a part of a string and returns a new string. The first argument is
the first index of the new, sliced array, the second argument is the last
index which is excluded.
.split()
Splits a string into an array of substrings.
.startsWith()
Checks whether a string begins with specified characters.
.substr()
Extracts the characters from a string, beginning at a specified start
position, and through the specified number of character.
The 1
is the index to start, the 4 is the total length, not the final index.
The difference between this and .slice() is the answers you’ll get when
you don’t fill in anything, such as NaN or undefined etc.
myString.substr(-1) // "d". Using Internet Explorer: "Hello World"
30
.substring()
Extracts the characters from a string, between two specified indices. The
second parameter is the end index.
.toLowerCase()
Converts a string to lowercase letters.
'WHY AM I SHOUTING?!'.toLowerCase();
// "why am i shouting?!"
.toString()
Returns the value of a string.
'Coding is fun'.toString();
// "Coding is fun"
.toUpperCase()
Converts a string to uppercase letters.
'I am whispering'.toUpperCase();
// "I AM WHISPERING"
.trim()
Removes whitespace from both ends of a string.
31
1.9.2 Array methods and properties
.concat()
Joins two or more arrays, and returns a copy of the joined arrays.
let a = [1, 2
, 3 ];
let b = [4, 5 , 6 ];
a.concat(b) / / [1, 2, 3, 4, 5, 6];
.every()
Checks if every element in an array pass a test.
5, 7
[14, 1 , 90].every(testValue); / / false
[13, 8 0, 3 0, 20].every(testValue); / / true
.fill()
Fill the elements in an array with a static value.
.filter()
Creates a new array with every element in an array that passes a test.
32
.find()
Returns the value of the first element in an array that passes a test.
.findIndex()
Returns the index of the first element in an array that passes a test.
.forEach()
Calls a function for each array element.
// 2
// 4
// 6
.indexOf()
Search the array for an element and returns its position
33
.isArray()
Checks whether the passed parameter to the Array.isArray function is an
array.
.join()
Joins all elements of an array into a string
[1, 2
, 3 ].join(''); // "123"
[1, 2 , 3 ].join(); // "1,2,3"
.lastIndexOf()
Search the array for an element, starting at the end, and returns its
position.
.map()
Creates a new array with the result of calling a function for each array
element
34
.pop()
Removes the last element of an array, and returns that element. It
modifies the original array!
.push()
Adds new elements to the end of an array, and returns the new length. If
you define a variable with the .push method, the length will be returned.
.reduce()
Reduce the values of an array to a single value (going left-to-right).
It takes the first item first, which is 11 in this case. It goes over 11, so now
11 is the current value cur. Then it goes to 50, which is now acc. The
function says cur + acc, so what will happen is it will return 11 + 50,
which is 61. 61 is now the current value cur, so now 39 is acc, and so on.
.reverse()
Reverses the order of the elements in an array.
35
.shift()
Removes the first element of an array, and returns that element.
.slice()
Selects a part of an array, and returns the new array. The original array
doesn’t get modified.
The slice function excludes the item with index of the second
parameter! So when you write .slice(1,3), it actually only “slices” the
second and third item, not the fourth.
.some()
Checks if any of the elements in an array passes a test.
[1, 5
, 7 , 9 ].some(testValue); / / false
[1, 5 , 7 , 1 1].some(testValue); / / true
.sort()
Sorts the elements of an array.
36
.splice()
Adds/Removes elements from an array.
The 2 stands for the position on which you want to start adding items. The
0 stands for how many items you want to have removed, in this case 0.
What if we changed the 2 to a 1 and the 0 to a 1?
.toString()
Converts an array to a string, and returns the result.
.unshift()
Adds new elements to the beginning of an array, and returns the new
length.
.valueOf()
Returns the primitive value of the array.
37
1.9.3 Math Object
The math object allows you to do mathematical calculations on numbers.
Math.round()
Returns the passed value down or up to its nearest integer.
/ 3
Math.round(3.4); /
Math.round(3.5); / / 4
Math.round(3.6); / / 4
Math.pow()
Returns the first passed value to the power of the second passed value.
Math.sqrt()
Returns the square root of the passed value.
Math.sqrt(64); // 8
Math.abs()
Returns the absolute (positive) value of the passed value.
Math.ceil()
Returns the passed value up to its nearest integer.
/ -4
Math.ceil(-4.3); /
Math.ceil(4.01); / / 5
38
Math.floor()
Returns the passed value down to its nearest integer.
Math.floor(4.7); // 4
Math.floor(-4.3); // -5
, 3
Math.min(400, 2 , 1 , 2 0, 3 0); / / 1
Math.max(400, 2 , 3 , 1 , 2 0, 3 0); / / 400
Math.random()
Returns a random value.
39
1.10 DOM Manipulation
JavaScript can:
● change, create and remove HTML elements + attributes.
● change all the CSS styles in the page.
● respond to all existing HTML events in the page.
In order to do so, you manipulate the Document Object Model (DOM). The
DOM is created when the webpage is loaded. The document, which is
accessible in the window object, is the root node (the element that’s on
top of the tree), and has access to all other nodes (HTML Elements).
We have a super simple website that just has a title in the head, and only
a link in the body. All elements are defined as objects.
40
1.10.1. DOM Methods and Properties
By using a m
ethod, you can manipulate the content of the HTML
elements.
By using a p roperty, you can access the property of HTML elements.
document.getElementById("id")
Returns an element object representing the element whose id matches
the string.
<p id="test"></p>
document.getElementsByClassName("class")
Returns a node list representing the element whose class name matches
the string.
lass="info-button">Information 1</button>
<button c
<button c lass="info-button">Information 2</button>
<button c lass="info-button">Information 3</button>
document.createElement("tagname")
Creates the HTML element specified by its tagname. Tag names are for
example div, p, h1, a, etc.
ocument.createElement("div");
let newDiv = d
let newTitle = document.createElement("h1");
41
document.querySelector("#id", ".class" or "element")
Returns the first element within the document that matches the specified
selector, or group of selectors.
parentNode.appendChild(node)
Appends a node as the last child of a node. For example, if we want to create
a div with a paragraph in it.
element.setAttribute(attributeName, value)
attributeName: name of the attribute (href, id, class, etc.)
value: value of the attribute
let p = document.querySelector("p");
p.setAttribute("class", "greeting")
// Now p has the class name "greeting"
42
element.innerHTML (not recommended)
Sets or gets the HTML syntax. Setting the innerHTML has security risks.
Instead, you should always try to use node.textContent if you’re only
adding a text.
<p id="greeting"></p>
--
document.getElementById("greeting").innerHTML = "Hey there!"
element.getAttribute(attributeName)
Get the value of the specified attribute.
Every time you click on the span element, "click"will be logged to the
console. There are many different events that can be used instead of
"click", which are easy to find online.
43
1.10.2 Bubbling and Capturing
Bubbling
Let’s say we have this code:
<div onclick="console.log('div')">
<p onclick="console.log('p')">Click here!!</p>
</div>
A click on the <p> first runs onclick event listener on that <p>, but also on
the <div>. If we click p, we see 2 logs: 1. “p“, 2. “div“. This is called
bubbling, because events “bubble“ up through their parent elements like
a bubble in water. The deepest nested element that caused the event is
the target of the event, the event.target. You can stop bubbling by
adding event.stopPropagation(). This would be useful if you have, for
example, a button inside a button. If you click on the inner button, you
don’t want the parent node (the outer button) to receive the event as
well.
<div onclick="console.log('div')">
<p onclick="event.stopPropagation()">Stop!</p>
</div>
Capturing
The DOM event describes 3 phases of event
propagation:
1. C
apturing phase
2. T arget phase
3. B ubbling phase
44
<form>
<div>
<p>Click</p>
</div>
</form>
----
for (let el of document.querySelectorAll('*')) {
el.addEventListener("click", e => alert(${el.tagName}), true);
el.addEventListener("click", e => alert(${el.tagName});
}
45
Understanding JavaScript
console.log(this.a) // 0
console.log(obj.normalFunc()); // 1
console.log(obj.arrowFunc()); // 0
46
2.3 Lexical Environment
b();
console.log(a);
let a = "Hey";
function b() {
console.log("Hey world!");
}
Notice how we invoke b before creating the function, and want to log a to
the console before declaring the variable. With other languages, you’d get
an error, but not with JavaScript. Instead, you get this result:
"Hey world!"
undefined
47
It runs the function, and shows that the value of variable a is undefined.
Variables and functions are available, even if they’re written later in the
code. This can happen, because during the creation phase of the code,
memory space is set up for variables and functions. This is called
hoisting. Before your code gets executed, the JavaScript engine already
has all the variables and functions in its memory, which is why you can
call them. However, variables are stored with the default value of
undefined, until you actually define them in your code. This is why you
get the value of undefined returned if you use a variable in your code
before defining it. Undefined does not mean not defined!
48
const myFunc = () => console.log("It's a function!");
myFunc();
When this function is called, the call stack looks like this:
When the function has finished, it gets popped off the stack. The functions
run when their stack frame is on top of the stack. A stack frame
represents a function call, and its argument data.
console.log(bar(2));
49
to the call stack, and simply returning their value. However, what would
happen if we had a setTimeout function?
bar();
foo();
baz();
// First
// Third
// Second
We have a setTimeout function and invoked it first. Yet it was logged last.
This is because in browsers, we don’t just have the runtime engine, we
also have something called a WebAPI. The WebAPI gives us the
setTimeout method to start with, and for example the DOM. You can
make calls to this. We can (sort of) do multiple things at the same time
now.
Our setTimeout function is now complete, and will be popped off the
stack. foo gets invoked.
50
The webAPI can’t just add stuff to the stack when it’s ready. Instead, it
pushes the callback function to something called a task queue. This is
where an event loop starts to work.
51
2.6 Variable scope
let x = 1;
if (x === 1) {
let x = 2;
console.log(x);
}
console.log(x);
// 2
// 1
x was only 2 inside that code block, so when logging x outside of the
function, it returned 1.
When you do something with a variable other than declaring it, it doesn’t
just look in the variable environment. It uses the ref to the outer
environment, which in function b’s case is the global execution context.
52
If we had this:
function a() {
function b() {
console.log(myVar);
}
}
The outer environment of function b is now function a. However, the outer
environment of function a is the global environment. To find the variable
myVar, which we want to log in function b, it goes down the scope chain.
b? Can’t find it. a? Can’t find it. global? Found it!
A callback function gets called back after the outer function has already
finished running, and was popped off the stack. Yet, we can still access
the variables created in that function context, even though it’s been
deleted.
Even though the someTimeout function has been popped off the stack, we
still have access to variable a 500 milliseconds after. This is because of
closures.
53
2.8 Closures
54
Normally, the JS engine clears it with a process called garbage
collection. The execution context of the greet function is still somewhere
in memory. We invoke the sayHi function, which creates a new execution
context. When its code is invoked, and it sees the sayHi variable, it goes
up the scope chain, and there is an outer lexical environment
reference to that variable. Although the execution context of sayHi is
gone, the execution context of sayHi still has a reference to the memory
space of its outer environment.
It is basically closing in all the variables that it should have have access
to, hence the name closure. It doesn’t matter when you invoke the
function.
55
Famous example:
We now have an array, and every item has a function. However, the
function hasn’t been invoked yet. If you log this, you get [f, f, f] or 3
functions. When we then call each function by func[0](), 3 gets returned.
During the for loop, we push a function to the array that logs i, but we
don’t invoke it yet. During the for loop, we increment i until i is equal to
3. Then when we actually invoke the functions, but we invoke it when i is
already 3, so they all log 3 as they log i. They can’t find a variable named
i in their function, so they go down the scope.
56
2.9 Closures and callbacks
function sayHiLater() {
var greeting = "Hi";
setTimeout(() => {
console.log(greeting);
}, 3000);
}
sayHiLater();
.call()
let obj = { number: 10 };
let addToThis = function(a, b, c) {
return this.number + a + b + c;
}
// functionName.call(object, functionArguments)
console.log(addToThis.call(obj, 1, 2, 3)); // 16
57
.apply()
The apply method is very similar to the call method.
The only difference between call and apply, is that you can pass the
arguments in an array, which you can’t do with the call method.
.bind()
The this keyword doesn’t always refer to the object you expect it to refer
to.
let person = {
data: [
{ firstName: "Lydia" },
];
clickHandler() {
console.log(this.data[0].firstName)
}
}
$("button").click(person.clickHandler);
// Error (this = button)
$("button").click(person.clickHandler.bind(person));
// Lydia (this = person object)
58
2.11 Functional programming
Because we have first class function in JavaScript, we can implement
functional programming, or in other words: we can think of code in terms
of functions.
Instead of having to write an entire for loop every time we want to create
another array that returns an array with the multiplied values of arr1, we
use the higher-order function map. Map basically includes that logic
already, so we don’t have to write it! This is just one of the many
examples of a higher-order function. The most common ones in ES6 are
map, filter and reduce.
59
2.12 Objects
person["firstname"] = "Lydia"
The object will now get a reference to the address location in memory.
The operator takes the object and the property, and looks for it on the
object.
JSON: JavaScript Object Notation. The most famous format to send data
used to be XML. However, just to send data, you had to send a lot of
unnecessary characters. Luckily, we don’t have to do that anymore with
JSON.
{
"firstname": "Lydia",
"lastname": "Hallie",
}
It looks like an object literal, but there are differences. When we receive a
JSON file from our API, we first parse the data into a variable. We can
then access the data by using the same computed member access
operators as we use for objects, either the dot notation or bracket
notation.
60
The biggest differences between valid JSON and object notation, is that
the keys need to be wrapped in double quotes, an array is also valid
JSON, and it can only contain properties, no methods! You often
receive a JSON file as a string from a web server. In order to get data out
of it, you use the JSON.parse method, which converts the text into a
normal object. Before you send data back to the web server, you have to
convert it back into a string by using JSON.stringify.
bark.animal = "dog";
61
The function statement doesn’t result in a value. However, it does get
hoisted during the creation phase of the execution context, so it’s
available to you in memory. For the function expression, you’re giving the
variable sayWoof the value of the function object. The function itself
doesn’t need to have a name, but you can assign it to a variable. The
function is invocable by typing sayWoof(). It is an expression, because
invoking the variable results in a value, which is the function object.
let a = 4;
let b;
b = a;
;
a = 2
All objects interact by reference when setting them equal to each other
or passing them to a function.
d = c;
62
c.greeting = "Hello";
console.log(d.greeting); // "Hello"
Variable c holds a reference to an object. Later, we assign d with the
same reference that c has to the object. When you change one of them,
you change all of them.
function showNumbers(x, y, x) {
console.log(arguments);
} // [1, 2, 3]
2.18 Inheritance
Inheritance: one object gets access to the property and methods of
another object.
Classical inheritance is, compared to prototypal inheritance, a lot of code
to write. You can end up with huge collections, whereas using prototypal
inheritance is very simple and flexible.
63
2.18.1 Understanding the prototype
We have an object in memory with a property. You can access it with the
dot notation or bracket notation. The JavaScript engine adds hidden
properties and methods to things we don’t interact with directly.
let person = {
firstName: 'Lydia ',
lastName: 'Hallie',
getFullName() {
return `${this.firstName} ${this.lastName}`
}
}
64
let otherPerson = {
firstName: 'John',
lastName: 'Doe',
}
With proto, this refers to the object you’re currently calling it from!
var a = {};
var b = () => { };
var c = [];
65
2.20 Function constructors and the keyword new
function Car(){
this.make = "Toyota";
}
The instanceof operator checks the current object inherits from the
class’s prototype. newCar is an instance of Car, so it returns true.
66
2.21 Prototype
Person.prototype
When you call the new keyword, it sets the prototype to the function’s
prototype that we call.
You can add features to all objects at once, using the prototype
property of the function constructor. It’s very important to remember that
you can’t add properties to the constructor like you can with objects.
Person.language = 'English'
console.log(lydia.language) // undefined
Functions are objects, meaning that they take up memory space. You
could have added the getFullName function just to the function
constructor, but that would cause a lot of repetition, which would take up
more memory space. But if you add it to the prototype, you have it just
once, which takes up almost no memory space.
67
2.22 Built-in function constructors
There are famous built-in constructors, such as new Array(), new String()
or new Number(). The build in function constructor looks like you’re
creating primitives, but you’ve actually creating objects that contain
primitives.
String.prototype.checkLength = function(limit) {
return this.length > limit
}
let a = 3;
let b = new Number(3);
a == b // true
a === b // false. A is a primitive, b is an object constructor.
var person = {
firstName: 'default',
greet() {
return `Hi ${this.firstName}`
}
}
With Object.create, you create an empty object but with the prototype of
the object passed as the argument. You now get all the properties and
68
methods in the prototype, and can overwrite values by writing, for
example:
john.firstName = "John"
With p
ure prototypal inheritance, you build an object that becomes the
base of all others.
2.23 Classes
In ES6, it’s possible to use classes instead of function constructors.
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
greet() {
return "Hi"
}
}
greet() {
console.log(`First name: ${firstName}`)
}
}
69
Super: calls the parent’s constructor, so the constructor of Person in our
case. You always have to use super before using the this keyword! It
avoids duplicating the constructor parts that are common between the
OtherPerson class and the Person class, so we wouldn’t have to type
this.firstName = firstName again. We simply type super(firstName).
Note that you can’t extend regular objects. If you want to do that, you
have to use Object.setPrototypeOf().
var Animal = {
speak() {
console.log(this.name + ' says woof.');
}
};
class Dog {
constructor(name) {
this.name = name;
}
}
Object.setPrototypeOf(Dog.prototype, Animal);
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor;
}
70
let pantherChameleon = new Chameleon({newColor: "purple"});
pantherChameleon.colorChange("orange");
console.log(pantherChameleon.newColor);
You will see that the newColor of the pantherChameleon is still purple, and
that pantherChameleon.colorChange isn’t a function. That’s because the
function isn’t passed down to the pantherChameleon instance! You can get
around this by creating two arguments. Classes provide an easy method
to use prototypal inheritance and create constructors. They’re not hoisted,
and will throw an error if an instance is declared before the class. A
constructor function determines what code will be executed when creating
a new instance of the constructor.
2.25 Polyfill
Methods such as Object.create aren’t always supported by older
browsers. To handle this, you can use a polyfill. A polyfill is code that
adds a feature that the engine may lack.
if (!Object.create) {
Object.create = function(obj) {
function NewFunction() { }
NewFunction.prototype = obj;
return new NewFunction();
}
}
let greeting;
greetign = {};
console.log(greetign) //Object
function sayHi() {
"use strict";
let greeting;
greetign = {};
console.log(greetign) // Error: greetign is not defined
}
72
FREQUENTLY USED RESOURCES
A very big thanks to all the authors of some of the great resources I’ve
used!
YouTube Channels
Fun Fun Function
JSConf
techsith
Courses on Udemy.com
JavaScript: Understanding the weird parts - Anthony Alicea
The Complete JavaScript Course - Rob Percival
Advanced JavaScript - Asim Hussain
73