JavaScript Fundamentals
Material based on the lecture by Carlos Scheidegger
When following along this module, my suggestion is that you open the Developer Tools’s JavaScript console on a browser window and run the examples to see what they do. You should also try variants, and just generally play around with the console, to get a feel for the language.
Before we get started, though, a few words of warning: there is a lot of bad JavaScript advice on the internet. For example, although StackOverflow is typically a high-quality Q&A website, I would stay well away from it when it comes to JavaScript – or, at least make sure that the answers given were recent (2016 or more recent). Finally, the introduction below is not meant to give you a comprehensive description of JavaScript but, rather, a foothold.
Once you become proficient in the language, then you can start worrying about best practices and special cases, especially as they related to performance and portability across browsers. It’s easier for you simply not to worry about that kind of stuff right now. This does mean that if you’re a veteran JavaScript programmer, you’ll spot places where what I’m writing is not 100% accurate.
JavaScript Background
Why do we want to use JavaScript? So far, we have only looked at static HTML and SVG code. If we wanted to create a bar chart with 15 bars like that, we’d have to write the code for each bar! If we had a dynamic and general way to load the data, and then create the rectangles, we could use the same code for bar charts with any datasets. I like to think of pure HTML and SVG as analogous to pen and paper – you can create things that look nice once, but you can’t re-use them efficiently and you can’t interact with them.
Computer programming brings interactivity and generalizability (re-use) to the table. We can tell the computer how to do something for certain classes of legal input, and the computer will do that for all possible cases of legal input.
JavaScript is the most important programming language of the web and the only programming language that can be used on most web-browsers without any plugins, although WebAssembly might change things in the future. Alternatives such as Java Applets or Flash were popular in the past but are now practically extinct. JavaScript is mostly used on the client-side of a client-server application. Other languages such as Java and Python are popular on the server, though nowadays JavaScript can also be used on the server (e.g., using Node.js. We will be focusing on client development only in this class.
The most important aspect of JavaScript though, is its tight integration with the DOM. We’ll be talking about this in the next two sections of the class.
JavaScript can be used with imperative/procedural, object-oriented, and functional programming styles.
It is a dynamically typed language, which can be strange for developers who mainly work with strongly typed languages such as C/C++ and Java. I personally prefer to work with TypeScript, a strongly typed language that is transpiled into JavaScript for the browser to run. However, it’s still important to know vanilla JavaScript well.
JavaScript as an object-oriented language uses prototypical inheritance instead of a class-based model. The ECMAScript 6 version of JavaScript has introduced syntactic “sugar” to make object-oriented programming more consistent with other programming languages. That means there now are class definitions, but you can still use the prototypical features for inheritance that can be extended at runtime. If this doesn’t mean much to you now, don’t worry – we’ll go through it slowly.
JavaScript - The Very Basic
If you know any other mainstream programming language, JavaScript will feel sufficiently familiar.
We can write to the console (helpful for debugging by calling the log method of the console object).
Hint: open up your browser’s developer tools to see the output generated by the examples here. Type in a clear()
and re-execute this as we go so that it’s easier to follow along)
It has variables that hold values:
The first thing to notice is that JavaScript’s variables are dynamically typed: you don’t need to declare their types before using them, and they can refer to values of different types at different times in the program execution. (This is convenient but quite error-prone: it’s usually a bad idea to make too much use of this feature.)
You also do not need to declare a variable ahead of time. If you don’t, then JavaScript either assumes you’re referring to an already existing variable, or it creates a new global variable. Again, this is convenient but very error-prone (this is a theme, as you’ll see). One common source of confusion is that typos in variable assignments are not caught: they just become global variables. Syntax highlighting in a good IDE will help you catch this.
Note that all the variables in the previous example are GLOBAL variables. We can define variables of local scope using the let
(and historically the var
) keyword:
To create a local variable, use the keyword let
or const
, with an outdated option: var
. Local here refers to the current execution context. When used within a function or block these variables are private to that function/block, however, when they are declared outside a function, as in the above example, they are still global.
So what is the difference between these?
- NO identifier creates a variable of global scope, which is rarely what you want.
var
creates variables of function scope. That means that the only way to isolate a variable is to wrap it in a function. This is different from most programming languages and rarely has advantages.let
creates a variable of block scope, which is what most other programming languages use. The means that, for example, a variable defined withlet
within anif
clause cannot be accessed outside of thatif
clause.const
creates a variable of block scope (again, if defined within a block, otherwise global) where the variable can’t be reassigned.
As a general rule: always use let
, never use var
, minimize your use of global variables and never define global variables in a function.
You can execute operations on these variables. Below are a couple of important examples, refer to the MDN documentation for the full list:
Arrays
Array literals are declared using square brackets and addressed with square brackets and a reference to the index starting with 0. Arrays in JavaScript are more like Lists in other languages, such as Java. They don’t have to be allocated ahead of time and can be easily extended.
You can do much more with arrays than shown here, again, check out the MDN documentation. Arrays are very important for data visualization, so take the time to go through this!
Objects
Objects are the second type of compound values in JavaScript.
Objects in JavaScript are also dictionaries/hash maps/associative arrays (pick your favorite name).
JSON
Very related to objects is JSON, which stans for JavaScript Object Notation. JSON is a way to serialize javascript objects, so that they can be stored in files or sent via the internet and loaded at another. Here’s a JSON example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"people": [
{
"name": "Alex",
"birthYear": 1981,
"nationality": "Austria",
"countries": [
"Austria",
"Canada",
"USA",
"France"
]
},
{
"name": "Jen",
"birthYear": 1991,
"nationality": "USA",
"countries": [
"USA",
"Scotland"
]
}
]
}
We can then read this file into JavaScript, and it is immediately available as an object. We’re using the standard XMLHttpRequest API here, but libraries such as D3 or JQuery provide simplified wrappers around this.
Control Structures
JavaScript comes with standard conditional control structures – if and switch. Check out the MDN guides on equality tests; JavaScript is a bit odd as it does type-coercion for double equals sings ==
.
Loops
Loops are C-like: for
, do-while
, and while
loops are available. To loop over arrays, use either a regular for
loop, the forEach function of arrays, or the new for-of statement.
Functions
Functions are one of the key features in computer science and are common to almost all programming languages. JavaScript not only uses functions the way we know them from languages such as C or Java but also allows anonymous functions.
None of the calls above cause runtime errors. If you call a function with too many parameters, JavaScript will simply ignore the extra ones. If you call a function with too few parameters, JavaScript gives the local parameters the special value undefined.
There is an alternative way of defining functions:
Pay attention to what’s happening here: this is assigning a function to a variable in the same way that x = "hi"
assigns the string value “hi” to the variable x
. This is important. In JavaScript, functions are values that can be stored in variables. This is your first exposure to the idea that JavaScript is a functional language. In the same way that you can store function values in variables, you can pass them around as parameters, store them in arrays, object fields, and even use them as return values of other functions! This is a powerful idea that we will use a lot.
In particular, we will use a lot of anonymous functions, or “lambda abstractions” when working with D3. We’ve already used one example for sorting an array above. Here is another example:
Here, the map()
function has a callback
parameter, which expects a function that you can map to to the elements of the array.
Since the use of these anonymous functions is ubiquitous, ES6 introduces a shorthand for them: arrow functions. Arrow functions are always anonymous function expressions. There is no arrow function declaration. Here is the example from above, and also some others:
Arrow functions are recommended for short expressions, you probably should use proper function definitions for multi-line functions with explicit return values. Also, note that this
behaves differently for arrow functions.
Object Oriented JavaScript
JavaScript is a prototype-based language. That means that, under the hood, it does not use the concept of classes like in Java or C++. Instead, objects are created and their signature is defined.
ES6 introduces a class
keyword and other related features which we will be using, but that doesn’t mean that JavaScript supports proper classes – instead, JavaScript classes are just syntactic sugar to make creating consistent objects easier.
Let’s first look at the pre-ES6 way. If we create an object with slots that hold functions, this starts to look like methods from Java and Python. If we create a function that returns these objects, this starts to look like class constructors:
Inheritance (or better Prototype Delegation)
Every time you tell JavaScript to access a field from an object, it tries to find the field. If the field exists, then the lookup is performed. If, however, the field doesn’t exist, then JavaScript checks for the presence of a special prototype field in the object. If that field is not null, then the JavaScript runtime performs a recursive access of the field in the prototype object. This is more obvious with an example. Make sure to run these in your JavaScript console:
Classes
ECMA Script 6 adds syntax for classes, but remember that this is only syntactic sugar. JavaScript remains a prototypical language.
Here is an example:
The special variable this
JavaScript has a special variable that is available at every scope called this
. When a function is called with a notation that resembles methods in typical object-oriented languages, say obj.method()
, then this
is bound to the object holding the method (in this case obj
). That way you can make changes to the local object:
So far, so good: we’ve used this
to change the value bound to the x field in the object. That’s pretty convenient.
This convenience, however, comes with a caveat. The way JavaScript decides to associate this
with a given object is simple to explain: it assumes the context of the calling object.
Here’s what can go wrong:
What happens in this latter example is that when t()
is called, this
is bound to the calling object, which is the global window
object in the latter case.
Other noteworthy things in JavaScript
Default Values
Lazy Expressions
uniqueID() is only called when foo() is called (without arguments), not before.
Gather and Spread Operators
Spread
When ...
is used in front of an array (actually, any iterable), it acts to “spread” it out into its individual values.
Gather
The inverse works with gather. The …z in this snippet is saying: gather the rest of the arguments (if any) into an array called z.
Destructuring
Array Destructuring
Object Destructuring
In ES5
In ES6
Template Strings
More ES6
ES6 has many more important features, most notably modules and promises. These can be very useful in production VIS code but go beyond our introduction here.
Versioning and Transpiling
JavaScript has undergone many versions, and is now standardized by the ECMA International. Significant changes were introduced with ECMAScript 6 (ES6) in 2015 (also called ECMAScript 2015). Since then, ECMAScript has transitioned to a more incremental update plan, so that ECMAScript 7, 8, and 9 are only minor updates.
Instead of relying on a particular version, you should check for the availability of features across browsers.
Older browsers, even those that are currently still in use (I’m NOT talking about Internet Explorer 6 here), will unlikely support current and future features. Fortunately, there are various remedies for this, so that you can start using these features now. Note that, for all classwork, we will be using the latest Google Chrome, which will avoid the need to use such remedies.
The most important approach to making your state-of-the-art JavaScript code compatible is transpiling. You take your code, run a transpiler (transformation + compiling) such as Babel, and out comes an ECMAScript 5 compatible code file.
Here is an example of how a ES6 code feature is transpiled into ES5 code:
Again, you won’t need to do this for this class, but in modern web-development this (and other steps) are commonly run to translate, check (lint), and minify your JavaScript code.