js (1)
js (1)
• A number
• The Boolean values false and true
• The special values null and undefined
• A string
• A symbol
• An object
Let variables
let variables are block scope they can only be accessed inside the block they’ ve been
defined in { }.
const scope
Global variables
they can be accessed from anywhere in the program
If you create a variable with the same name inside a function, that variable's value
will be used whenever you refer to that variable name within the scope of that
particular function. Here you can see an example:
This will output:
The same is also true for parameter names. If you have the same parameter name as
a global variable, the value of the parameter will be used:
if(rain){
console.log("** Taking my umbrella when I need to go outside **");
} else {
console.log("** I can leave my umbrella at home **");
}
else if statements
Ternary Operator
Switch Statement
LOOPS
While Loop
Do while loop
for loops
for of loop
The for of loop iterates over the elements of an iterable object, most commonly
an array or string.
There is another loop we can use to iterate over the elements of an array: the for of
loop. It cannot be used to change the value associated with the index as we can do
with the regular loop, but for processing values it is a very nice and readable loop.
Here is what the syntax looks like:
Loops and objects
for in loop
Manipulating objects with loops can also be done with another variation of the for
loop, the for in loop. The for in loop is somewhat similar to the for of loop. Again
here, we need to specify a temporary name, also referred to as a key, to store each
property name in. We can see it in action here:
You can use any loop on objects, as soon as you convert the object to an array. This
can be done in three ways:
If we want to loop over the keys of the object, we can use the for in loop, as we saw
in the previous section, but we can also use the for of loop if we convert it to an
array first. We do so by using the Object.keys(nameOfObject) built-in function. This
takes an object and grabs all the properties of this object and converts them to an
array.
To demonstrate how this works:
We can loop over the properties of this array like this using the for of loop:
And this is what it will output:
Similarly, we can use the for of loop to loop over the values of the object by
converting the values to an array. The main difference here is that we use Object.
values(nameOfObject):
You can loop over these arrays in the same way you loop over any array. You can
use the length and index strategy like this in a regular for loop:
More interesting is how to loop over both arrays at the same time using the for of
loop. In order to do so, we will have to use Object.entries(). Let's demonstrate
what it does:
Break
We can use break to stop looping through the array of cars when we have
found a car that matches our demands.
continue
break can be used to quit the loop, and continue can be used to move on to the next
iteration of the loop. It quits the current iteration and moves back up to check the
condition and start a new iteration.
Writing functions
calling functions
Parameters
A parameter is defined as the variable listed inside the parentheses of the function
definition, which defines the scope of the function. They are declared like so:
Default parameters
Instead, we could tell JavaScript to take different default parameters. And that can
be done like this:
If you call the function with no arguments now, it will automatically assign 2 to x
and 3 to y, unless you override them by calling the function with arguments.
If you call a function with more arguments than parameters, nothing will happen.
JavaScript will just execute the function using the first arguments that can be
mapped to parameters. Like this:
Arrow functions are a special way of writing functions that can be confusing at first.
Their use looks like this:
Or for no parameters:
Spread operator
this becomes :
This will output 27 to the console, calling the function like this:
Rest parameter
SCOPE
They are out of scope outside the function and in scope inside the function. Let's
have a look at a variable defined inside a function:
So, the returned value I'll return that was assigned to local variable y gets
returned and stored in variable z.
The output of this code snippet is as follows:
Recursive Functions
This function is going to call itself until the value of the parameter is no longer
bigger than 0. And then it stops.
What happens when we call a function recursively is that it goes one function deeper
every time. The first function call is done last. For this function it goes like this:
• getRecursive(3)
• getRecursive(2)
• getRecursive(1)
• getRecursive(0)
• done with getRecursive(0) execution
Remember we said that classes are just some special function beneath the surface.
We could create the object with a special function like this:
JavaScript objects are very different from those in class-based languages such
as Java and C++. A JavaScript object is simply a set of name/value pairs or
“properties,” like this:
Once you have such a variable, you can access the object properties with the usual dot
notation:
The harry variable was declared as const, but as you just saw,
you can mutate the object to which it refers. However, you cannot assign a different value to
a const variable.
In other words, const is like final in Java and not at all like
const in C++.
delete harry.salary
A property name can be computed. Then, use array brackets to access the property value:
let harry = {
name: 'Harry Smith',
age: 42, // Add more properties below
}
Quite often, when declaring an object literal, property values are stored in
variables whose names are equal to the property names. For example,
let age = 43
let harry = { name: 'Harry Smith', age: age }
// The 'age' property is set to the value of the age variable
let harry = { name: 'Harry Smith', age } // The age property is now
43
Use brackets for the computed property names in object literals:
A property name is always a string. If the name doesn’t follow the rules of
an identifier, quote it in an object literal:
To access such a property, you cannot use the dot notation. Use brackets
instead:
Note that the element types in an array don’t need to match. The numbers array
contains three numbers and a string.
As with any object, a nonexistent property has the value undefined. For example,
Note that, as with all objects, you can change the properties of an array that
is referenced by a const variable.
numbers.lucky = true
The typeof operator returns 'object' for an array. To test whether an object is
an array, call Array.isArray(obj).
The JSON.stringify method turns a JavaScript object into a JSON string, and
JSON.parse parses a JSON string, yielding a JavaScript object. These methods
are commonly used when communicating with a server via HTTP.
Note
console.log(`harry=${harry}`)
harry=[object Object]
console.log(`harry=${JSON.stringify(harry)}`)
Note that this problem only occurs with strings that contain objects. If you log an object by
itself, the console displays it nicely. An easy alternative is to log the names and values
separately:
Destructuring
Let’s look at arrays first. Suppose you have an array pair with two elements.Of course, you
can get the elements like this:
let first = pair[0]
let second = pair[1]
This statement declares variables first and second and initializes them with pair[0] and
pair[1].
Consider this more complex case and observe how the variables are matched
with the array elements:
Let’s look at arrays first. Suppose you have an array pair with two elements.
Of course, you can get the elements like this:
This statement declares variables first and second and initializes them with
If the variables first and second are already declared, you can use destructuring to set them
to new values:
Object destructuring
If you use destructuring for an assignment, the left-hand side doesn’t have
to consist of variables. You can use any lvalues—expressions that can be on
the left-hand side of an assignment. For example, this is valid destructuring:
[numbers[0], harry.age] = [13, 42] // Same as numbers[0] = 13;
harry.age = 42
This code snippet declares two variables harrysName and harrysAge and initializes
them with the name and age property values of the right-hand side object.
Keep in mind that the left-hand side is not an object literal. It is a pattern to
show how the variables are matched with the right-hand side.
Destructuring with objects is most compelling when the property has the
same name as the variable. In that case, you can omit the property name
and colon. This statement declares two variables name and age and initializes
them with the identically named properties of the object on the
right-hand side:
or, of course,
Note
Advanced Destructuring
let pat = { name: 'Pat', birthday: { day: 14, month: 3, year: 2000 }
}
Rest Declarations
When destructuring an array, you can capture any remaining elements into an array. Add a
prefix ... before the variable name.
numbers = [1, 7, 2, 9]
let [first, second, ...others] = numbers
// first is 1, second is 7, and others is [2, 9]
If the array on the right-hand side doesn’t have sufficient elements, then the
rest variable becomes an empty array:
let [first, second, ...others] = [42]
// first is 42, second is undefined, and others is []
The allButName variable is set to an object containing all properties other than
the one with key name.
Constructors
The constructor method is a special method that we use to initialize objects with
our class blueprint.
That object gives you a ready-made place for adding methods, like this:
Employee.prototype.raiseSalary = function(percent) {
this.salary *= 1 + percent / 100
}
As you can see, there is a lot going on. Let us have another look at the call:
2. The [[Prototype]] internal slot of that object is set to the Employee.prototype object.
3. The new operator calls the constructor function with three parameters: this
(pointing to the newly created object), name, and salary.
4. The body of the Employee function sets the object properties by using the
this parameter.
5. The constructor returns, and the value of the new operator is the now
fully initialized object.
6. The variable Harry is initialized with the object reference. Figure 4-3 shows
the result.
Here is how you can create a new object from the Person class:
Methods
Properties
Properties, sometimes also called fields, hold the data of the class. We have seen one
kind of property already, when we created them in our constructors:
private properties
Right now, the firstname and lastname properties cannot be accessed from outside
the class. This is done by adding # in front of the property. If we try it:
Inheritance/subclasses
Behind the scenes, a prototype chain is established—see Figure 4-4. The pro-
totype of Manager.prototype is set to Employee.prototype. That way, any method that
is not declared in the subclass is looked up in the superclass.
classes can have child classes that inherit the properties and methods from the parent class.
Here we have two methods in our Vehicle class: move and accelerate. And this
could be a Motorcyle class inheriting from this class using the extends keyword:
With the extends keyword we specify that a certain class is the child of another class.
In this case, Motorcycle is a child class of Vehicle. This means that we'll have access
to properties and methods from Vehicle in our Motorcycle class. We have added a
special doWheelie() method. This is not something that makes sense to add to the
Vehicle class, because this is an action that is specific to certain vehicles.
The super word in the constructor is calling the constructor from the parent, the
Vehicle constructor in this case. This makes sure that the fields from the parent
are set as well and that the methods are available without having to do anything
else: they are automatically inherited. Calling super() is not optional, you must do
it when you are in a class that is inheriting from another class, else you will get a
ReferenceError.
Because we have access to the fields of Vehicle in Motorcycle, this will work:
And this is what it will output:
We cannot access any Motorcycle specific properties or methods in our Vehicle class.
This is because not all vehicles are motorcycles, so we cannot be sure that we would
have the properties or methods from a child.
Right now, we don't use any getters and setters here, but we clearly could. If there
are getters and setters in the parent class, they are inherited by the child class as well.
This way we could influence which properties could be fetched and changed (and
how) outside our class. This is generally a good practice.
Prototypes
Suppose you have many employee objects similar to the one in the preceding
section. Then you need to make a raiseSalary property for each of them. You
can write a factory function to automate that task:
Still, each employee object has its own raiseSalary property, even though the
property value is the same function for all employees (see Figure 4-1). It would
be better if all employees could share one function.
That is where prototypes come in. A prototype collects properties that are
common to multiple objects. Here is a prototype object that holds the shared
methods:
const employeePrototype = {
raiseSalary: function(percent) {
this.salary *= 1 + percent / 100
}
}
harry.raiseSalary(5)
As you will see later in this chapter, prototypes can be chained. If the proto-
type doesn’t have a property, its prototype is searched, until the prototype
chain ends.
The prototype lookup mechanism is completely general. Here, we used it to
look up a method, but it works for any property. If a property isn’t found in
an object, then the prototype chain is searched, and the first match is the
property value.
NOTE: Lookup in the prototype chain is only used for reading property
values. If you write to a property, the value is always updated in the
object itself.
For example, suppose you change the harry.raiseSalary method:
This adds a new property directly to the harry object. It does not modify
the prototype. All other employees retain the original raiseSalary property.
When nothing is specified when creating a class, the objects inherit from the
Object.prototype prototype. This is a rather complex built-in JavaScript class that
we can use. We don't need to look at how this is implemented in JavaScript, as
we can consider it the base object that is always on top of the inheritance tree and
therefore always present in our objects.
There is a prototype property available on all classes, and it is always named
"prototype." We can access it like this:
Let's give an example of how to add a function to a class using the prototype
property. In order to do so, we'll be using this Person class:
It will output:
And it will be as if you had defined the class with a favorite color holding a
default value, and a function, introduce. They have been added to the class and
are available for all instances and future instances.
So the methods and properties defined via prototype are really as if they were
defined in the class. This means that overwriting them for a certain instance doesn't
overwrite them for all instances. For example, if we were to have a second Person
object, this person could overwrite the favoriteColor value and this wouldn't
change the value for our object with firstname as Maria.
This is something you should not be using when you have control over the class
code and you want to change it permanently. In that case, just change the class.
However, you can expand existing objects like this and even expand existing objects
conditionally. It is also important to know that the JavaScript built-in objects have
prototypes and inherit from Object.prototype. However, be sure not to modify this
prototype since it will affect how our JavaScript works.
METHODS
Global methods
The global JavaScript methods can be used without referring to the built-in object
they are part of. This means that we can just use the method name as if it is a
function that has been defined inside the scope we are in, without the "object" in
front of it. For example, instead of writing:
Parsing numbers
parseInt()
output :
Array methods
Here is how to construct an empty array with ten thousand elements, all
initially undefined:
const bigEmptyArray = []
bigEmptyArray.length = 10000
The length property is one more than the highest index, converted to a number.
For example,
The calls
arr.length--
arr[arr.length] = x
The push and unshift methods can add any number of elements at once:
arr = [9]
arr.push(16, 25) // 16, 25 are appended; arr is now [9, 16, 25]
arr.unshift(0, 1, 4) // 0, 1, 4 are prepended; arr is now [0, 1, 4,
9, 16, 25]
First, deleteCount elements are removed, starting at offset start. Then the
provided elements are inserted at start.
If start is negative, it is counted from the end of the array (that is, it is adjusted by adding
arr.length).
It takes the function that needs to be executed for every element as input. Here you can see
an example:
The filter method takes a function as an argument, and this function should
return a Boolean. If the Boolean has the value true, the element will end up in the
filtered array. If the Boolean has the value false, the element will be left out. You can
see how it works here:
The copyWithin() method can be used to replace a part of the array with another
part of the array. In the first example we specify 3 arguments. The first one is the
target position, to which the values get copied. The second one is the start of what
to copy to the target position and the last one is the end of the sequence that will be
copied to the target position; this last index is not included.
arr becomes:
If we specify a range with length 2, the first two elements after the starting position
get overridden:
We can also not specify an end at all; it will take the range to the end of the string:
It is important to keep in mind that this function changes the content of the original array, but
will never change the length of the original array.
This method will return a new array with all the new
values.It is going to execute the function for every element in the
array, so for example:
This is what the console output with the new mapped array looks like:
Using the arrow function, the map() method has created a new array, in which each
of the original array values has been increased by 1.
It will return the index of the last element with that value, if it can find it at all:
This will log 2, because the index 2 holds the last bye variable. What do you think
you'll get when you ask for the last index of something that's not there?
Template literals are strings that can contain expressions and span multiple
lines. These strings are delimited by backticks (`. . .`). For example,
Any newlines inside the template literal are included in the string. For
example,
greeting = `<div>Hello</div>
<div>${destination}</div>
`
sets greeting to the string '<div>Hello</div>\n<div>World</div>\n' with a
newline after each line.
join() method
With the method you can convert an array to a string. Here is a basic
example:
This will use the – instead of the comma. This is the result:
String methods
concat() method.
This does not change the original string(s); it returns the combined result as a string. You will
have to capture the result in a new variable, else it will get lost:
split()
As you can see, it creates an array of all the elements separated by a space. We can
split by any character, for example a comma:
indexOf()
The indexOf() method returns the index, a single number, of the first character of the
substring:
This is logging 7 to the console, because the first occurrence of re is in are, and the re
begins at index 7. When it can't find an index, it will return -1.
search()
An alternative way of searching for a particular substring within a string is to use the
search() method:
This will log 17, because that is the index of lo in fellow. Much like indexOf(), if it
cannot find it, it will return -1.
lastIndexOf() method.
It returns the index where the argument string occurs last. If it cannot find it, it returns -1.
Here is an example:
This returns 24; this is the last time re appears in our poem. It is the second are.
charAt(index)
Creating substrings
slice(start, end)
This does not alter the original string, but returns a new string with the substring.
It takes two parameters,the first is the index at which it starts and the second is the end
index. If you leave out the second index it will just continue until the end of the string from
the start. The end index is not included in the substring. Here is an example:
The first one only has one argument, so it starts at index 5 (which holds an e) and
grabs the rest of the string from there. The second one has two arguments, 0 and 3. C
is at index 0 and a is at index 3. Since the last index is not included in the substring,
it will only return Cre.
It takes two arguments, one string to look for in the string and one new
value to replace the old value with. Here is an example:
This will log to the console Hi Pascal. If you don't capture the result, it is gone,
because the original string will not get changed.
changed. If the string you are targeting doesn't appear in the original string, the replacement
doesn't take place and the original string will be returned:
the replaceAll() method.
We can change the letters of a string with the toUpperCase() and toLowerCase()
built-in methods on string. Again, this is not changing the original string, so we'll
have to capture the result:
This logs:
It converts all the letters to uppercase. We can do the opposite with toLowerCase():
Let's make it a bit more complicated and say that we'd like the first letter of the
sentence to be capitalized. We can do this by combining some of the methods we
have seen already right now:
We are chaining the methods here; we first grab the first character of fixed_caps
with charAt(0) and then make it uppercase by calling toUpperCase() on it. We then
need the rest of the string and we get it by concatenating slice(1).
startsWith()
This will log true to the console, because the sentence starts with You. Careful here,
because it is case sensitive.
endswith( )
we can do the same thing for checking whether a string ends with a certain string. You can
see it in action here:
Number methods
isNaN() method
This will only leave two decimals, so the value of newX will be 1.23.
There is also a method to specify precision. Again this is different from the rounding
methods in the Math class, since we can specify the total number of numbers to look
at. This comes down to JavaScript looking at the total number of numbers. It is also
counting the ones before the dot:
So the value of newX will be 1.2 here. And also here, it is rounding the numbers:
This will log 1.235.
Math methods
to find the highest number among the arguments. It logs 233, because that's the highest
number.
Math.min ( )
In a similar way, we can find the lowest number:
The method sqrt() is used to calculate the square root of a certain number.
result of 5*5*5.
ceil() method
The floor() method is doing the exact opposite of the ceil() method. It rounds
down to the nearest integer number, as you can see here:
And then one last method, trunc(). This gives the exact same result as floor() for
positive numbers, but it gets to these results differently. It is not rounding down, it is
simply only returning the integer part:
When we use negative numbers for trunc() we can see the difference:
Creating dates
There are different ways to create a date. One way to create dates is by using the
different constructors. You can see some examples here:
This will log the current date and time, in this case:
But, this way we are not using the built-in method, but the constructor. There is a
built-in method, now(), that returns the current date and time, similar to what the no
argument constructor does:
We can also convert dates back to strings. For example with these methods:
PROMISES
We first create a Promise. When creating a Promise, we don't know what the value
of the Promise is going to be. This value is whatever is sent as an argument to the
resolve function. It is a sort of placeholder.
So when we call then on the Promise, we basically say: figure out what the value
of the Promise is, and when you know, execute one function if the Promise was
resolved or a different function if it was rejected. When a Promise is neither resolved
nor rejected, we say that the Promise is pending.
then() is a Promise itself, so when it returns we can use the result for the next then()
instance. This means we can chain the then() instances, which can look like this:
The resolve functions are implemented with an arrow function. The return statement
is the value input for the next function. You can see that the last block is a catch()
function. If any of the functions were to result in a rejection and the Promise were
therefore rejected, this catch() block would be executed and print whatever the
reject() function sent to the catch() method. For example:
This will just log oops... because the first Promise was rejected instead of resolved.
This is great for creating asynchronous processes that need to wait till another
process is complete. We can try to do a certain set of actions
You have just seen how to build pipelines of promises with the then and catch
methods, and how to execute a sequence of promises concurrently with
Promise.all and Promise.any. However, this programming style is not very conve-
nient. Instead of using familiar statement sequences and control flow, you
need to set up a pipeline with method calls.
The await/async syntax makes working with promises much more natural.
The expression
The compiler transforms the code of an async function so that any steps that
occur after an await operator are executed when the promise resolves. For
example, the putImage function is equivalent to:
As you can see from these examples, the rewriting that the compiler does
behind the scenes is not trivial.
If you forget the await keyword when calling an async function,
the function is called and returns a promise, but the promise just sits
there and does nothing. Consider this scenario, adapted from one of
many confused blogs:
• Arrow functions:
async url => { . . . }
async (url, params) => { . . . }
• Methods:
class ImageLoader {
async load(url) { . . . }
}
obj = {
async loadImage(url) { . . . },
. . .
}
We can access them using the item() method to access them by index, like this:
We can also access them by name, using the namedItem() method, like this:
Since h1 doesn't have an ID or class, it is only h1. And since it doesn't have an ID, it
is not a namedItem and is only in there once.
Using querySelector()
This first option will select the first element that matches the query. So, enter the
following in the console, still using the HTML snippet introduced at the start of the
section:
It should return:
It only returns the first div, because that's the first one it encounters. We could also
ask for an element with the class .something. If you recall, we select classes using dot
notation like this:
This returns:
Using querySelectorAll()
Sometimes it is not enough to return only the first instance, but you want to select all
the elements that match the query. For example when you need to get all the input
boxes and empty them. This can be done with querySelectorAll():
This returns:
As you can see, it is of object type NodeList. It contains all the nodes that match the
CSS selector. With the item() method we can get them by index, just as we did for
the HTMLCollection.
innerHTML property.
to modify the content of an HTML element
To achieve this we have to add an event listener that fires when a user causes any event
e.g. clicks a button
You can also remove event handlers that have been attached with the addEventListener()
method:
This might sound a bit vague, so let's have a look at an example where we are going
to add a class to an element, which in this case will add a layout and make the
element disappear.
classList.add
classList.remove()
Toggling classes
In some cases, you would want to add a class when it doesn't already have that
particular class, but remove it when it does. This is called toggling. There is a special
method to toggle classes. Let's change our first example to toggle the hide class so the
class will appear when we press the button the second time, disappear the third time,
and so on. The blue class was removed to make it shorter; it's not doing anything in
this example other than making the square blue.
Manipulating attributes
The attributes in this example are id, class, and href. Other common attributes are
src and style, but there are many others out there.
You can do this from the console and
see the result easily, or write another HTML file with this built in as a function. In
this HTML snippet, you will see it in action: