Setup
Before starting this prelab, ensure that you have:
- Accept this Github Classroom assignment and clone this repo that contains stencil code for this prelab.
- We have 2 tasks listed at the bottom of this page that you'll have to complete and handin.
Introduction
The Language of the Internet
JavaScript, also known as ECMAScript, is one of the world's most popular and misunderstood programming languages. This prelab introduces you to some of the tools you'll need to begin programming in JavaScript!
Warning: This prelab is a bit on the longer side, but incredibly important to complete especially if you haven’t used JavaScript before. We're going to be giving you a lot of sample code in this lab, but we recommend typing it in instead of copy/pasting; it'll help you learn the syntax.
What you'll get to know
- JavaScript and its ~fun quirks~
- The Document Object Model (DOM)
- How to dynamically alter a page through a hands on experience
What is JavaScript?
From the Mozilla Developer Network (a great site for help with JS):
JavaScript (JS) is a lightweight interpreted or JIT-compiled programming language with first-class functions. While it is most well-known as the scripting language for Web pages, many non-browser environments also use it...
JavaScript is basically the only language you can use in the browser to control the web page (e.g. make pop-ups, create an image slider, build infinite scroll). To control the web page, you use the DOM API, which is a platform and language-neutral interface that will allow programs and scripts to dynamically access and update the content, structure and style of documents.
Using the console
We're going to learn about JavaScript syntax straight from the browser. Modern browsers have built in developer tools that allow us to look at the code of any website. To do this:
-
Create an
index.html
file (with a general HTML template) and drag the file into your browser to open it, or useindex.html
file provided in the stencil for this prelab. We recommend using Google Chrome, but you can also use FireFox or Safari if you prefer. -
In order to include JavaScript in your HTML page you need to use a script tag,
<script>...</script>
. You can type any Javascript code inside the tag and it will be executed while the page is loading. Alternatively, you can write JavaScript in a separate file and include it externally with<script src="path/to/index.js"></script>
. In most cases, just as you should avoid using inline CSS styling, it is also highly advised to use external imports if possible by writing all your JavaScript in a separate file. For the purposes of this prelab it’s okay to follow along by including all your JavaScript in a script tag, but we also encourage you to play around with using imports. - Right click anywhere on the page, and click on "inspect". This should pull up the developer tools. You should see all of the HTML code of this page. Moving your mouse over the different tags will show you its corresponding elements on the page. Right now, we don't have anything in our webpage, but once elements get added, they will start to show up.
-
Notice in the developer tools there is another tab called "console."
- In Chrome, this is the second tab of the DevTools. (How to open Chrome Developer Console)
- In Safari, this is the last tab of the developer tools. (How to open Safari Developer Console)
- In FireFox, this is the tab after Inspector. (How to open Firefox Developer Console)
- The console is like a JavaScript terminal and is a great debugging tool for your later projects. You can type in any JavaScript code, and it will evaluate it line by line, just like an interpreter will. (You can read Google's documentation on Chrome Console)
-
Type in
console.log("i love noodles");
into the console and hit enter (the semicolon at the end is optional and can be skipped). You'll notice that the message is printed onto the console. This is the equivalent of "printing" to the terminal. (By the way, semicolons are completely optional in JavaScript, you can choose whether to use them or not, but consistency is recommended) -
The console can also evaluate arithmetic expressions. Try typing in
3 + 4
and hitting enter.
IMPORTANT: We highly recommend getting familiar with the various developer tools in the console, such as the Chrome Debugger for debugging JavaScript. You can use the following brief guide to learn how to set breakpoints and step through JavaScript code to inspect values: https://developers.google.com/web/tools/chrome-devtools/javascript/.
Printing to the console
The Console is also a very helpful debugging tool. You can make use of this by printing out to the
console directly in your JavaScript code, using the function console.log()
. If you have
a line in your JavaScript that says console.log("Hello World!")
, then when that line is
evaluated "Hello World!" will be written to the console. You can console.log()
any
value in JavaScript, for instance an object or result of a function call, to see if the value is as
you expect it to be. You can think of it as similar to printf()
or print()
in other languages.
JavaScript syntax
JavaScript is a pretty wild language, at least because it supports procedural, object-oriented, and functional programming. Here's a little cheat-sheet.
Variables
There are three ways to declare a variable with the advent of ES6 (the JavaScript version we are
using): var
, let
, and const
. It's a good practice to avoid
using var
. The current most popular opinion is to always use const
when
you know the value will not be re-assigned and let
if you expect to re-assign the value
of the variable (e.g. in a for
loop). But be careful! Just because a variable is
declared with const
does NOT mean that JavaScript will enforce it to be immutable, only
that it can not be re-assigned to. For example, a const
array can still be appended to,
and the values of any keys of a const
object can still be modified. You can read more
about variable
declaration in ES6 here.
JavaScript has totally dynamic typing, meaning that you don't need to declare what type a variable is. JavaScript can just figure out the correct type of a variable based on how it's used.
To see this in action, type in the following lines into the JavaScript console (no need to include the comments)
> const lol = "laughing out loud" // strings can be single or double quoted
< undefined
> typeof lol
< "string"
> let numberOfPenguins = 4
< undefined
> typeof numberOfPenguins // should print "number"
< "number"
> numberOfPenguins = 5.0 // we can reassign the value because it is declared using "let"
> typeof numberOfPenguins // all number is JS are type "number" (floating point number)
< "number"
As a side note, if you’re curious about why your console returns undefined when you define variables, see here: Why does JavaScript variable declaration at console results in "undefined" being printed?
Arrays
Arrays can be of mixed type and are declared through a simple literal syntax that's similar to
Python:
let mixedTypeArray = ["noodles", {class: "cs1320"}, true, function() {alert("I'm a function")}];
Working with arrays:
> mixedTypeArray[0] // access an element, JS array is 0-indexed
< "noodles"
> mixedTypeArray[1] = "CSCI1320" // reassign element value
< "CSCI1320"
> myArray.length // use length to get a total count of elements
< 4
> mixedTypeArray[100] = "Remember to fill out Collab Policy" // add an element at given index
< "Remember to fill out Collab Policy"
Since arrays are among the most widely used data structures, it may be a good idea to review the following array methods (we have highlighted the ones that we think will be especially important to know): push, pop, shift, unshift, indexOf, includes, splice, sort, and filter. It will also be useful to know the various expressions and operators that can be used with arrays including the spread syntax introduced in ES6, as well as the various array iteration protocols such as basic for loops, for...of loops, and iteration methods such as forEach, and map. You can find all the documentation for arrays on MDN.
Objects
An object in JavaScript is a collection of properties where a property is a mapping from a key (aka name, index...) to a value. Similar to JS Arrays, Objects can also have mixed types.
let captain = {
name: 'Rockhopper',
nickname: 'RH',
location: { // you can have nested objects!
ship: 'Migrator',
area: 'Beach'
}
};
Write a similar object for yourself in the <script>
tags in your index.html. Now
open up your page in the browser and check out the JavaScript console. Now type the name of the
variable you just made into the console. You should see the name come up with a little arrow to let
you inspect the object. You can see all the properties you just defined
There are two ways to access a property on an object:
captain['name'] // 'Rockhopper'
or
captain.name // 'Rockhopper'
Dot notation is often used for static attribute while bracket notation could accept an attribute
name that is dynamically generated. Note that dot notation only works with object properties that
are valid identifiers (they must start with a letter and not contain spaces, hyphens, or be keywords
like for
).
If you’re interested, you can also check out some other standard built-in objects that you may find useful, including map and set. ES6 also added some useful ways of using objects, including shorthand for object properties and dynamic property names.
Equality (==
vs. ===
)
One of the most common bugs in JavaScript comes from using ==
rather than
===
. They're both equality operators, but with a catch: JavaScript does type coercion.
==
will coerce its two operands to the same type before comparing them, so
1 == "1"
is true
even though they are not of the same type.
===
on the other hand only returns true
if its two operands are equivalent
and of the same type. So 1 === "1"
is false
, since 1
is a
number and "1"
is a string
. It's a best practice to always use
===
so you don't get any surprises, although there might be situations in which you
want to compare two things regardless of type, in which case ==
is the move.
For negative equality, !=
is equivalent to not ==
, and !==
is
equivalent to not ===
.
You can read more about equality comparisons in this article.
Types of false
In JavaScript, variables are "falsy" or "truthy", meaning that you can do
if (myNonBooleanVariable)...
You can look at definition for truthy and falsy on MDN.
Here are the main falsy values.
-
null
: Usually used to indicate a nonexistent object,null
is actually of type object, which leads to some confusing things like this:
What happens if you put that in your tags. Is that what you'd expect?if (typeof null === 'object') { alert("JavaScript is weird!"); }
-
undefined
: When you try to access a property on an object or a variable that isn't defined, you'll get the valueundefined
. Say you have this:
If you're accessing a variable and you're not sure if it'll be there (because, for example, all function arguments are optional in JavaScript), you should check forfunction sayHi(name) { if (name !== undefined) { alert("Hello " + name); } } sayHi() // no output since name is undefined sayHi("CS Student"); // name is "CS Student", you'll get an alert
undefined
.let myObj = {}; // empty object if (myObj.name === undefined) { console.log("no name!"); // this will always be called }
-
false
:false
is useful for values that are definitely known (unlikeundefined
ornull
) and definitely false. For example,false
may be assigned to boolean variables (boolean is a data type that has two possible values: true or false). A trivial example:const pigsFly = false; if (pigsFly) { console.log("Club Penguin will shut down"); // this will never happen :'( }
Surprisingly truthy
Empty arrays and objects always behave like true
when treated as booleans.
let info = {};
if (info) {
console.log("true!");
}
Summary table of truthy and falsy values in JavaScript
TRUTHY (evaluates to true ) |
FALSE (evaluates to false ) |
---|---|
true |
false |
non-zero numbers | 0 |
"strings" |
"" |
objects | undefined |
arrays | null |
functions | NaN (not a number) |
Functions
Functions are objects. You can define properties on them, and they're capable of inheritance. Here's some advanced information about JavaScript functions. The gist is:
Declaring functions
There are two standard ways to declare functions:
function myFunction(arg1, arg2, arg3, ...) { ... };
// Example
function add(a, b) {
return a + b;
};
const myFunction = function(arg1, arg2, arg3, ...) { ... };
// Example
const add = function(a, b) {
return a + b;
};
The first is a normal function or named function, in this case called myFunction
. The
second is an anonymous function, in which you're assigning to a variable called
myFunction
. They can almost be used interchangeably, except that the anonymous function
is created at run time when the variable is declared, whereas the normal function is run before any
other code. In general, it’s a good idea to use anonymous functions when using functions as
arguments (think functional programming). You could also instantiate a function object with
new Function()
, but there aren't many reasons to.
As of ES6 syntax, you can also declare functions with the arrow (=>
) operator:
const myFunction = (arg1, arg2, arg3, ...) => { ... };
const myFunction = (arg1, arg2, arg3, ...) => { ... };
// Example
const add = (a, b) => a + b; // When the function body can be written in just one line, you can omit the braces { } and return statement. This is purely syntactic sugar.
This is now one of the most popular ways to declare functions in JavaScript because of two main
reasons: 1) it tends to be much more concise to write (see above example) and 2) it resolves some
issues relating to the special this
variable (to be discussed further in the next
section). Although we won’t require the use of any specific form of function declaration, we would
recommend using the latest ES6 arrow syntax when possible because it is currently the most popular
choice among web devs. Read more about arrow functions on MDN
or this feature article: ES6 In Depth: Arrow
functions.
Calling functions and this
JavaScript has some cool ways to call functions. Of course, you can call a function by name:
foo();
or, if it's the property of an object:
const obj = {
foo: function(){alert('hello, world!');}
};
// by name
obj.foo();
// dynamically: you could assign fn at runtime
const fn = "foo";
obj[fn]();
Functions have a special variable called this
in their scope which can be used to refer
to the object that called the function. When a function is called plain (e.g. foo()
),
this
is either the window
or the object the function is being called on
(which could be an instance of the function object). If the function is called as the property of
another object (e.g. obj.foo()
) this
refers to the object before the
.
(in this case it would be obj
). this
can be tricky to
understand, and if you find yourself stuck or confused when using it then come to TA hours and/or
try checking out this Stack Overflow answer: How does the
"this" keyword work?
One important takeaway from this is that arrow functions do not have their own this
binding! Instead, the value of this
in an arrow function refers to the same
this
in the context that the arrow function was defined in. The introduction of the
arrow function removes the need for a lot of hacky fixes related to this
when
programming in JavaScript. You can find a lot of examples about this online, for example here.
Expand for further optional reading on instantiating object types ▼
In a special case where the function is called with the new
keyword,
this
is bound to an object, and that object is returned. This is the normal way of
declaring and instantiating an object type ("class") in JavaScript:
function Cyborg(name) {
// assign the name property of the `this` object
this.name = name;
}
const doctor = new Cyborg('Doctor Marbles');
console.log('The name of the doctor is ' + doctor.name); // 'The name of the doctor is Doctor Marbles'
If you are familiar with classes in other object-oriented languages, this syntax might confuse
you. On one hand, it is used very similarly to a traditional class from other languages, but on
the other hand, it's declared as a function. You can think of this like just the constructor of
a class. Here, the Cyborg()
is a function that's essentially just a constructor for
a class called Cyborg. We'll see how to define methods and class methods on this class later on.
If you want, you can call a function with a different context, meaning that this
can be set to whatever object you want. You do that with call
and
apply
:
foo.apply(otherObj, [ arg1, arg2, arg3 ]); // call foo where this === otherObj
foo.call(otherObj, arg1, arg2, arg3); // these two lines are equivalent
The difference is how you supply arguments to the function you're calling. apply
takes an array, call
takes individual arguments.
Prototypes and inheritance (optional reading) ▼
You can define methods on your JavaScript objects using the object's prototype
,
which is another object that defines properties and methods for the object. You can also define
methods inside class function as well.
Put this in your <script>
tag:
// this is the constructor for a Friend object
function Friend (name, age, location) {
this.name = name;
this.age = age;
this.location = location;
this.test = function (){
return this.name + " " + this.age;
};
}
// let's define a method for Friend; the prototype is just an object
Friend.prototype = {
info : function () {
return this.name + "/" + this.age + "/" + this.location;
}
};
const penguin = new Friend("Gary", 5, "Pizza Parlor");
console.log(penguin.info());
JavaScript doesn't do inheritance the same way C/Java/Python/Ruby does (classically). It uses
prototypical inheritance. It looks a little bit confusing (considering there's no
inherits
or extends
keyword. It's actually a really simple concept:
when you call a method on an object, JavaScript goes up that object's prototype chain (its
parents' prototypes) looking for a method with that name. While that sounds a lot like classical
inheritance, it's actually a lot simpler, since it doesn't require any classes or interfaces.
You don't really need multiple inheritance since you can inherit from any list of prototypes in any order you want; they don't have to implement the same methods! Try this out:
So what if we want to create an object that inherits from Friend
, say, a
PenguinFriend
? Since we're working with Functions, calling the super-object is as
easy as calling a function with the context (this
) set to our new object:
function PenguinFriend (name, age, location, hobby) {
Friend.call(this, name, age, location); // call the parent (like super() in Java)
this.hobby = hobby;
}
Now we want to set PenguinFriend
's prototype to an instance of
Friend
's prototype. We don't want to use Friend
's prototype directly
because then we'd be messing with Friend
. So we create an empty function with a
blank constructor so we can instantiate a new Friend
prototype:
function emptyFriend() {}; // blank function
emptyFriend.prototype = Friend.prototype;
PenguinFriend.prototype = new emptyFriend(); // a new instance of the prototype
PenguinFriend.prototype.constructor = PenguinFriend; // that's the function that the new keyword calls
Nice! Now we've got a PenguinFriend
using its constructor but Friend
's
prototype. How do we add new methods? Just define them on the prototype object:
PenguinFriend.prototype.profile = function () {
return this.info() + "/" + this.hobby;
}
Now if you've been following along, you should be able to do this:
const matt = new PenguinFriend("Matt", 11, "Ohio", "Skateboarding");
console.log(matt.profile());
This can get pretty confusing and might be worth reading twice. Evan Wallace also has a section on the same topic in JavaScript for Coders.
Classes (optional reading) ▼
As part of ES6, standard class declarations have been introduced to JavaScript. The class syntax is not introducing a new object-oriented inheritance model to JavaScript, it is syntactic-sugar for prototypical inheritance. JavaScript classes provide a simpler and clearer syntax to create objects. For more information on classes checkout this information from Mozilla.
class TA{
constructor(height, hairColor){
this.height = height;
this.hairColor = hairColor;
}
gradeHomework(hwk) {
// code not shown ...
}
}
Function and class declarations are different because function declarations are hoisted and class declarations are not. You first need to declare your class and then access it; otherwise an error will be thrown.
Scoping
Unlike C/Java/Python and other languages you are probably familiar with, Javascript has function
scoping rather than block scoping. So if
statements, while
loops,
etc. do not create a scope, and variables defined with var
are hoisted to be declared
at the beginning of their scope. This is also a reason why let
and const
are better practice than var
, since they don't exhibit this sort of
behavior. Scoping is the source of many errors when using JavaScript. For more information,
check out this great StackOverflow
answer about Variable Scope in JavaScript.
Closure (optional reading) ▼
JavaScript functions can access variables accessible but not declared within their scope at any time.
function counter(){
let count = 0;
return function(){
// [count] here is the [count] from above
count++;
return count;
}
}
let c = counter();
c() // 1
c() // 2
c() // 3
// ...
This type of setup is useful especially when variables shouldn't be accessible -- there's no way
for external code to change count
(on the other hand, if it were the property of an
object anything could read and write it).
Another place this becomes useful is maintaining a reference to an object in event handlers and the like:
//This defines a single Timer object (like a singleton class,
//only one exists and you can't instantiate it).
let Timer = {
count: 0,
render: function () {
console.log(this.count);
},
initialize: function () {
//this puts a reference to [this] in this scope
//which won't be overridden by the [this] in the scope
//of functions defined later.
let manager = this;
//setInterval calls [argument1] every [argument2] milliseconds.
setInterval(function () {
//this reference to manager is maintained, while [this] now
//references the function as passed to setInterval in this
//scope.
manager.count++;
manager.render();
}, 1000);
}
};
Other syntax
Conditionals
JavaScript conditionals are a lot like C. There is if
, else if
, and
else
. We already talked about truthy and falsy values. You can convert a variable to a
boolean type with !!
(not not), so !!1 === true
. This is a good way to
determine if a value is "truthy" or "falsy" (just do console.log(!!myValue);
. You can
also use the Boolean()
function for the same effect.
JavaScript also has a switch statement:
function legal(age) {
let ableTo = [];
switch (age) {
case 18:
ableTo.push('vote', 'fight', 'get real estate license');
break;
case 21:
ableTo.push('drink', 'gamble');
break;
case 65:
ableTo.push('retire');
break;
default:
ableTo.push('be free');
}
return ableTo;
}
Loops
Similar deal here. It looks just like C:
for (let i=0; i < someArray.length; i++){
console.log(someArray[i]);
}
One difference is that you can also iterate over an object or array:
for (let key in object) {
console.log("key: " + key + " value: " + object[key]);
}
for (let index in array) {
console.log("index: " + index + " value: " + array[index]);
}
Note that when used for the object case, this syntax gives you all the keys defined on the object
including keys of its prototypes. That can lead to some unpleasant surprises, so in this case one
would either use hasOwnProperty
, which returns true
if and only if the
property name is defined for that object directly, or use something like Object.entries
which only gets the object’s own keys. You DO NOT need to use this sort of syntax with arrays,
because key
would just be the index in the array, and this type of loop is slower.
for (let key in object) {
if (!object.hasOwnProperty(key)){continue;}
console.log("key: " + key + " value: " + object[key]);
}
// or using Object.entries() - this tends to be the popular choice
for (let [key, value] of Object.entries(object)) {
console.log("key: " + key + " value: " + value);
}
JavaScript also has while
loops just like C/Java, and you can use
++
/--
unary operators on values. JavaScript also has a break
keyword for getting out of a loop and a continue
statement for going to the next
iteration.
Events
If you haven't already, try clicking the box above. Wow! We were able to add this interactive functionality to this webpage using JavaScript to listen for a click event on the above div. But what are events? The DOM makes a ton of use of the delegate event model, meaning that when something happens in the browser, it emits events, which can be picked up (and modified) by any code that's listening. This makes it easy for programmers to add additional code to a page without worrying about other scripts (e.g. plugins, bookmarklets, frames), and allows for really decoupled design. Let's see another example:
Here is some syntax for adding event listeners to an object. It's nice because you can add and
remove multiple listeners for the same event: Add this HTML between your body tags:
<div id="display">press a key</div>
. We're going to set up event
listeners for keyup
, so you can see when the user presses a key. Add the following code
between the <script>
tags in index.html:
const display = document.getElementById('display');
// Here we're going to make use of an anonymous function.
// Since functions are like any other object (number, string, etc.)
// in JavaScript, we can pass a function as an argument to another function
// without issue!
document.addEventListener('keyup', function (event) {
// the event object can tell us which key is pressed
display.textContent = "you pressed key #" + event.keyCode;
}, false);
Reload the page, and try pressing any key. The text should change and indicate the key that was pressed.
Keyboard keys in javascript are identified by their keys and codes. So to only do something when the user hits "enter":
document.addEventListener('keyup', function (event) {
if (event.key === "Enter") {
console.log("you hit enter!");
}
}, false);
Notice that we're adding the event listener to document
. The document
is
an element like any other, referring to everything inside the <html>
tags. We're
just adding the listener there because we want to call it on a keypress anywhere on the
page. Events bubble up the DOM tree: if you click a button, you can listen to that event on any
parent element of the button (which can sometimes be tricky if you weren't expecting to get that
event there).
As another example, here is the code we used to create the click listener for the "You clicked me!" div above:
<div id="onclick-div" style="background-color: #FEBB14; padding: 15px; border: 4px solid #D64D37; margin-bottom: 20px; width: 300px; user-select: none">Try clicking me!</div>
<script>
let counter = 0;
const onclickDiv = document.getElementById("onclick-div");
onclickDiv.addEventListener("click", function(event) {
counter++;
if (counter < 5) {
onclickDiv.innerHTML = "You clicked me! (" + counter + ")";
} else {
onclickDiv.innerHTML = "Ok, please stop clicking me now ;-; (" + counter + ")";
}
});
</script>
innerHTML
is a property of an element which represents its contents as HTML. Here we
use it just to change the text inside the div, but we could also add more elements inside the div
(so assigning onclickDiv.innerHTML = "<em>content</em>"
will show
a div with the word "content" inside of it italicized).
This is opposed to textContent
which also represents the contents of an element, but as
text. Any tags inside it are ignored, and any tags put into it are represented literally as text.
(So assigning onclickDiv.textContent = "<em>content</em>"
will
show a div with the words "<em>content</em>", not italicized).
preventDefault
and stopPropagation
Events have default behaviors depending on what triggered the event. For example, on a text input,
the default behavior of a key press event is to add the key that was pressed to the value of the
input. However, there are times when you might not always want the default behavior to happen and
that is where preventDefault
becomes useful. For example, if we wanted our text input
to only accept lowercase letters, we could attach the following key press handler to our text input:
<input type="text" id="lowercase-input">
<script>
const lowercaseInput = document.getElementById("lowercase-input");
lowercaseInput.addEventListener("keypress", function(event) {
const key = event.charCode
if (key !== 0 && (key < 97 || key > 122)) {
event.preventDefault();
}
});
</script>
stopPropagation
is useful when you want to prevent further propagation of the current
event in the capturing and bubbling phases. Event bubbling in JavaScript describes the order in
which event handlers are called when one element is nested inside a second element, and both
elements have registered a listener for the same event (such as a click). For example:
<div id="parent">
<button id="child">click me</button>
</div>
<script>
const child = document.getElementById("child");
child.addEventListener("click", function(event) {
console.log("child clicked");
});
const parent = document.getElementById("parent");
parent.addEventListener("click", function(event) {
console.log("parent clicked");
});
</script>
Clicking on the button would result in the console printing “child clicked” then “parent clicked”. However if you don’t want the click event to propagate to the parent and want to prevent the parent from printing, you can change the child’s click listener to the following:
child.addEventListener("click", function(event) {
event.stopPropagation();
console.log("child clicked");
});
What is the DOM?
From the W3C (the people who design web standards):
The Document Object Model (DOM) is an application programming interface (API) for valid HTML and well-formed XML documents. It defines the logical structure of documents and the way a document is accessed and manipulated. In the DOM specification, the term "document" is used in the broad sense.
The DOM represents elements (i.e. <a>
, <div>
,
<p>
) as nodes in a tree. These nodes are available as objects in JavaScript. You
may find that you will often want to manipulate the document (we have already seen this, e.g. when
we used getElementById()
or innerHTML
). If you want, you can read more
about manipulating objects here.
A quick note on asynchronous programming in JavaScript
This is a fairly advanced but essential part of programming in JavaScript that we won’t go into great depth for the purposes of this prelab. After you’ve had a chance to process and digest the other information, we recommend doing a little independent research on this topic. If you don’t already know what asynchronous programming means, we recommend checking out this introduction. In short, although the JavaScript language is synchronous and single threaded by default, many things that JavaScript is used for needs an asynchronous approach. For example, one of JavaScript’s main jobs is to respond to user actions such as onClick, onMouseOver, onChange, onSubmit, which require an asynchronous approach. The JavaScript environment (browser) provides a set of API functionality that can handle this asynchronous functionality. However, this can result in problems when using variables that we expect to have a certain value but end up not being defined because the event (asynchronous) hasn’t completed yet.
The classic fix for this was the use of a callback function, which is passed as an argument to another function and only has its code executed when the event happens. As of ES6, Promises (highly recommended to look into!) were introduced as the new method of asynchronous programming and can be used to avoid a bunch of the problems with using callbacks. Promises can also be used with async functions and the await keyword introduced in 2017, and this is now arguably the best way to program asynchronously in JavaScript. You may find the following tutorials helpful down the road:
Promises in JavaScript with ES6 / ES2015Exploring Async/Await Functions in JavaScript
Looking ahead: Using JSDoc + ESLint:
In addition, there are two widely used JavaScript add-ons that you should be aware of which we would like you to use in your assignments: JSDoc and ESLint. JSDoc allows you to add annotations to your JavaScript code, making it easier for you and other programmers to read your code. ESLint is used to help find bugs and errors in your code by statically analyzing it. It can be installed as a plugin with VS Code or configured as part of your Node.js project using npm.
Tasks:
Open the stencil
- Task 1: Move the EventListener for click-button to the
script.js
file and invoke it in yourindex.html
file - Task 2: Add the following functionality to the form: Write a function that adds the three inputted values together
and then print this value in the
index.html
page Feel free to style it, but it is not required for this lab!
This is the end of the prelab! It is a lot to digest, so if you're confused about anything, feel free to come to TA Hours.
Handin Instructions
- To hand in your code for prelab 3, commit and push your changes to your cloned GitHub Classroom repository's
main
branch. - And submit it on gradescope using this link. Please note that you'll have to submit it to gradescope this time or we'll not be able to grade your submission.