4  Javascript

learning goals
  • Understand JavaScript as a programming language that adds interactivity to web pages
  • Learn fundamental JavaScript syntax including variables, data types, and operators
  • Master control flow using conditional statements and loops
  • Write and use functions to create reusable code blocks
  • Work with arrays and objects to store and manipulate collections of data
  • Apply JavaScript concepts to common psychology experiment scenarios
  • Use the browser console for testing and debugging JavaScript code

4.1 Introduction

Now we move from structure (HTML) and styling (CSS) to behavior and interactivity. Using our house-building analogy, if HTML is the foundation and framing, and CSS is the paint and interior design, then JavaScript is the electrical system, plumbing, and smart home features that make everything actually work and respond.

JavaScript is a true programming language, unlike HTML and CSS. This means it can perform calculations, make decisions, respond to user input, manipulate content on the page, communicate with servers, and much more. JavaScript brings your psychology experiments to life by handling participant responses, randomizing stimuli, calculating reaction times, saving data, and controlling the flow of your experiment from one trial to the next.

In psychology experiments, JavaScript handles all the dynamic behavior: presenting stimuli at precise times, recording when participants press keys, calculating accuracy and reaction times, randomizing trial orders, adapting the experiment based on participant performance, and saving all the data you need for analysis.

This is a tough section to teach because the needs of the student vary quite widely here. Some of you are programmers from other languages who are coming here to learn front end coding. Some of you are totally new to coding and JavaScript will be your first programming language. Just like if you know Spanish already, learning Italian becomes a whole lot easier since you take the same concepts and express them with minor variances. This workshop is optimizing for those totally new to coding and thus we assume no prior knowledge. If you do know another programming language, I invite you instead to skim this section to familiarize with how JavaScript looks. The next sections will be aimed also at you.

4.2 What is Code?

A dumb question but its answer may surprise you. Code is for humans first and computers second. You can think of writing code as essentially writing notes on how to solve a particular problem that just happens to be in a way that computer can understand it.

Wait, why? The why is because you or someone else will have to go back and re-read that code some time in the future, maybe tomorrow or maybe in ten years (I’ve worked on code older than 10 years old, it’s not fun.) As such, it’s important to write code in such a way that it can understood as quickly as is reasonable. Think of it like a textbook: you don’t want to read the entire textbook everytime you want to review a concept, you want to jump right to the page and learn just the thing you’re looking for. Same with code: you want to jump right to the bit of code in question and understand it at a glance without re-reading the whole codebase.

You will spend far longer maintaining this code than you will writing it the first time. Be explicit. Be deliberate. The point here is not to be clever but to be simple and to communicate clearly. Code is communication.

4.3 How JavaScript Executes

Okay, given this, let’s frame how this code works then. When you write code, the computer breaks it down into smaller pieces it can understand and then executes those one bit at a time. With JavaScript, only one thing is ever happening at a time (this is called being single threaded but that is not a term you need to know.) In general, this means it executes line 1, then line 2, then line 3, etc. Let’s see that in action:

const monthlyRent = 500;
const yearlyRent = monthlyRent * 12;
console.log(yearlyRent);
6000

We’ll discuss this example in more detail a bit later, but for now it’s enough to understand that the code will run the code on each line, one at a time, from top to bottom. In this case it takes the number 500, multiplies it by 12 and prints the result for us. You’ll notice how this differs from HTML and CSS in that we can perform calculations and store and manipulate data. Whereas the content of the HTML and CSS were static, and did not change, JavaScript is dynamic.

4.4 The JavaScript Console

Before we dive into writing JavaScript code, let’s discover the powerful tool that’s built right into your web browser. Every modern browser has a JavaScript console where you can type and execute JavaScript code immediately. Think of it as a playground where you can experiment with code and see results instantly.

4.4.1 Opening the Console

To open the JavaScript console:

  • Chrome/Edge: Press F12 or right-click anywhere on a webpage and select “Inspect”, then click the “Console” tab
  • Firefox: Press F12 or right-click and select “Inspect Element”, then click “Console”
  • Safari: First enable developer tools in Safari > Preferences > Advanced > “Show Develop menu”, then press Option+Cmd+C
  • Any browser: Right-click on a webpage, select “Inspect” or “Inspect Element”, then look for the “Console” tab

4.4.2 Try It Yourself!

Open your browser’s console right now and try typing these commands. Press Enter after each one:

Ex. 1

2+3

Ex. 2

"Hello, world!"

Ex. 3

Math.random()

Ex. 4

new Date()

You should see the results appear immediately below each command you type. This is JavaScript executing your code in real-time! Note that Math.random() prints a random number between 0 and 1, and new Date() prints the current date.

The console is incredibly useful for:

  • Testing small pieces of code quickly
  • Debugging when something isn’t working
  • Exploring JavaScript features and functions
  • Checking the values of variables in your programs

Throughout this chapter, you can copy any of the JavaScript examples into your console to see them work immediately. This hands-on experimentation is one of the best ways to learn programming.

4.4.3 The console.log() Command

One of the most important commands you’ll use is console.log(). This command prints whatever you put inside the parentheses to the console. It’s like JavaScript’s way of “showing your work” - it lets you see what’s happening inside your code. Here are some examples you can type into your browser console to try yourself:

Ex. 1

console.log("Hello from JavaScript!")

Ex. 2

console.log(42)

Ex. 3

console.log(2 + 3)

Ex. 4

console.log(2 / 3)

Ex. 5

console.log("The answer is:", 42)

console.log() is incredibly helpful because it acts as your primary tool for understanding what your code is doing. When your program isn’t working as expected, you can use console.log() to check what values your variables contain at any point in your code. You can add console.log() statements throughout your program to trace exactly where problems occur, following the flow of your code step by step. When learning new concepts, you can log values to confirm your code is doing what you think it’s doing, which helps build confidence and understanding. It’s also the easiest way to display the results of calculations or operations, making it perfect for seeing the output of your work.

Think of console.log() as your window into what JavaScript is thinking. It’s like being able to peek inside the computer’s brain to see the values it’s working with.

4.5 Variables and Basic Syntax

Now that you know how to execute JavaScript code, let’s start learning the language itself. Let’s return to our first example:

const monthlyRent = 500;
const yearlyRent = monthlyRent * 12;
console.log(yearlyRent);
6000

4.5.1 What is a Variable?

A variable is like a labeled box where you can store information. Just like you might have a box labeled “Winter Clothes” in your closet that contains sweaters and coats, a variable has a name and contains some data that you want to use later.

In programming, variables let you store values (like numbers, text, or more complex data) and give them meaningful names so you can refer to them throughout your code. Instead of writing the number 500 everywhere you need it, you can store it in a variable called monthlyRent and use that name instead.

Think of variables as creating shortcuts with descriptive names. Rather than remembering “that number 500 from line 3,” you can just refer to monthlyRent and immediately understand what that value represents.

4.5.2 Declaring Variables

The first thing that happens in our example is that we declare a variable, monthlyRent. We use the const keyword to let JavaScript know we’re declaring a variable. There are actually two main ways to declare variables in JavaScript: const and let.

const is used for values that won’t change after you create them. Once you set const monthlyRent = 500, you can’t change monthlyRent to a different value later in your code. This is perfect for values that should stay the same throughout your program.

let is used for values that might change. If you declared let currentScore = 0 at the beginning of a game, you could update it later with currentScore = 10 or currentScore = 25as the player earns points.

In psychology experiments, you might use const for things like the number of trials or instruction text that never change, and let for things like the current trial number or participant responses that update throughout the experiment.

4.5.3 Variable Naming Rules

Variables can be called almost anything. You can’t use keywords. An example would be const (e.g., const = 15). const is a keyword so it can’t be used as a variable name. You do want to give your variables good names, even if they end up being long sometimes. Imagine we have a huge file and 200 lines below we see the variable named monthlyRent: we’ll know instantly what this variable does and we won’t have to go try to read other parts of the code to figure it out. Always, always, always use good variable names. Seriously. Put time into it. Naming things is hard and it’s a big part of your job.

4.5.4 Why Use Variables?

Okay, so after line one, I have a variable named monthlyRent that I can use as much as I want. In this case, it represents the number 500 but it also semantically represents monthly rent. Imagine if I had 1000 lines between where monthlyRent is declared and where yearlyRent is calculated. I could have just put 500 directly in yearlyRent but I don’t because I now understand how that’s calculated just by reading the code. Use variables. Use them everywhere. It makes your code way easier to read. Also, later, if my monthly rent changes, I can change it one place and everywhere I reference monthlyRent and yearlyRent get updated automatically. Powerful stuff.

4.6 Working with Different Types of Data

So far we’ve mostly dealt with numbers. Let’s go further and start working with words and characters. In programming, we refer to these things as strings, as in a string of one-letter characters.

4.6.1 Strings

let myName = "Nick Brosowsky"

console.log(myName);
"Nick Brosowsky"

You can see I use the ” (double quote) to enclose everything I want to be in the string. In JavaScript you can also use ’ (single quote) and ` (back tick) as well to demarcate strings.

Strings, as you may imagine, are everywhere in programming. We’re constantly keeping track of names, addresses, names of products, cities, etc. and thus constantly need strings.

4.6.2 String Concatenation

Strings let you connect them together through string concatenation. If I want to be able to greet someone based on their name, I might have something like this:

const firstName = "Nick";
const lastName = "Brosowsky";

const sentence = "Hello " + firstName + " " + lastName + "! How are you!?";

const sentenceWithTemplate = `Hello ${firstName} ${lastName}! How are you!?`;

console.log(sentence);
console.log(sentenceWithTemplate);
"Hello Nick Brosowsky! How are you!?"
"Hello Nick Brosowsky! How are you!?"

The first way is the old way. We can use the + to tell JavaScript to connect two strings. Notice how we have to insert the space between firstName and lastName. The computer only does exactly what you tell it to do. If you don’t insert that space, it doesn’t get put there.

The second line is the new way of doing this. JavaScript got a large update in 2015 and it made things a lot easier. Now you can use the back tick (notice the first uses a double quote, you must use back ticks to do template strings) to do template strings. If you do that, anything inside of ${yourVariableHere} gets output in the string (Note that you can typically find the backtick key on the top-left of your keyboard).

When we start programming our experiments in JsPsych, we’re often going to be using backticks and template strings.

4.6.3 Booleans

Sometimes you just need a simple true or false. These are where booleans are useful. Something like a light switch’s state is best represented by a boolean. A light is either on (true) or off (false). You’d have something like const lightIsOn = true;. Useful and you’ll see them everywhere. Notice that true doesn’t need any quotations around it.

let lightIsOn = true;
let doorIsOpen = false;
let experimentIsRunning = true;

console.log("Light is on:", lightIsOn);
console.log("Door is open:", doorIsOpen);
console.log("Experiment running:", experimentIsRunning);
"Light is on: true"
"Door is open: false"
"Experiment running: true"

4.7 Controlling the Flow

Sometimes I want to modify the flow of how my program works. By default, JavaScript executes code line by line from top to bottom, but often you need your program to make decisions and take different paths based on different conditions. Maybe you want to show different feedback depending on whether a participant got an answer right or wrong, or perhaps you want to present different stimuli based on their previous responses. This is where conditional statements become very useful for controlling the flow of your program.

4.7.1 Basic if-else Statements

The most basic way to control program flow is with an if-else statement. This lets you run one piece of code when a condition is true, and a different piece of code when it’s false. Imagine if we tried this:

let skyIsBlue = true;

if (skyIsBlue) {
  console.log("The sky is blue!");
} else {
  console.log("The sky is … not blue?");
}
"The sky is blue!"

In the above example, the condition inside of the parentheses is evaluated and if it’s true, the first block is run and the second is skipped. If it is false, the second block is run and the first block is skipped. Paste that code into your browser console and play with it. You also do not have to have an else block.

Of course, we can go ahead and change the value of skyIsBlue and this would change the output:

let skyIsBlue = true;

// change it!
skyIsBlue = false

if (skyIsBlue) {
  console.log("The sky is blue!");
} else {
  console.log("The sky is … not blue?");
}
"The sky is … not blue?"

4.7.2 Comparison Operators

So far we’ve only used variables that are already true or false, but most of the time you’ll need to compare values to make decisions. JavaScript provides several operators that let you compare numbers, strings, and other values to create true/false conditions.

// if you see three lines, it's just three = in a row, ===. the font just combines them into one big character
if (2 + 2 === 4) {
  console.log(
    "Oh thank god, the fundamental principles of mathematics still hold true."
  );
} else {
  console.log("Uh, panic?");
}
"Oh thank god, the fundamental principles of mathematics still hold true."

You can put any expression (a technical term, means anything you can stick on the right side of an equal sign, we’ll explore it more as we go) inside of the if statement. In this case, we are asking, is two plus two still equal to four. If this is true (I hope so) then again the first block will be run. If not, the second will be.

Let’s talk about === for a second. If you use just one = in JavaScript, it means “is assigned to.” So when we have const isBrianCool = true; you can verbalize that as “The variable isBrianCool is assigned to true.” Thus we can’t use that inside of the if statement because that’s not what we mean. We’re trying to ask a question, not assign something. We’re trying to ask “is two plus two equal to four.” Enter the triple equals. Triple equals is the same as asking “is this equal to that.” We use the triple equals instead of the double equals because double equals does a lot of funny business that usually we don’t want it to do. It does what’s called coercion and we’ll talk about that below. But in an example 2 == “2” but it does not 2 === “2”. String 2 is double equal to number 2 but string 2 is not triple equal to number 2.

There’s also !==. This is asking “is this not equal to that.” Lastly you can ask with numbers >, >=, <, <= as well to ask if numbers less than or greater than too.

4.7.3 Multiple Conditions with else if

Often you’ll need to check more than just two possibilities. When you have multiple conditions to test, you can chain them together using else if statements. This lets you create a series of checks that run in order until one condition is met.

let friendsAtYourParty = 10;

if (friendsAtYourParty === 0) {
  console.log("Cool, now I have a lot of nachos to myself.");
} else if (friendsAtYourParty <= 4) {
  console.log("Perfect amount to play some Mario Kart.");
} else {
  console.log("Wooooo turn on the dance music!");
}
"Wooooo turn on the dance music!"

4.8 Applying Flow Control

Before moving on, let’s apply our knowledge and explore how using variables, comparison operators and flow control can be important tools for coding psychology experiments. Every time you need to evaluate participant performance, provide feedback, or make decisions about what to present next, you’re using these concepts. Here are some common examples:

4.8.0.1 Checking Response Accuracy

We can check whether a participant’s response was the correct one.

let correctAnswer = "red";
let participantResponse = "blue";

if (participantResponse === correctAnswer) {
  console.log("Correct! Well done.");
} else {
  console.log("Incorrect. The correct answer was " + correctAnswer);
}
"Incorrect. The correct answer was red"

4.8.0.2 Evaluating Reaction Times

We can add multiple conditions to check how long it took someone to respond and provide feedback on whether it was too fast or too slow.

let reactionTime = 400; // milliseconds

if (reactionTime < 200) {
  console.log("Too fast! You may have responded before seeing the stimulus.");
} else if (reactionTime > 1000) {
  console.log("Too slow! Try to respond more quickly.");
} else {
  console.log("Good reaction time: " + reactionTime + "ms");
}
"Good reaction time: 400ms"

4.8.1 Combining Multiple Conditions

We can even do both at the same time, though it gets a bit complicated.

let accuracy = true;
let reactionTime = 450;

if (accuracy === true && reactionTime < 500) {
  console.log("Excellent! Correct and fast response.");
} else if (accuracy === true && reactionTime >= 500) {
  console.log("Correct, but try to respond faster next time.");
} else if (accuracy === false && reactionTime < 300) {
  console.log("Incorrect and very fast - you may be guessing.");
} else {
  console.log("Incorrect response. Take your time to think.");
}
"Correct, but try to respond faster next time."

These conditional statements form the backbone of experiment logic, allowing your programs to adapt and respond intelligently to participant behavior in real-time.

4.9 Loops

Okay so now what if I want to do one thing multiple times? I could do something like this:

let friendsAtYourParty = 0;

friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty = friendsAtYourParty + 1;

console.log(friendsAtYourParty);
10

That’s annoying though. I wish there was a better way. Before we explore that, let’s chat about this example a bit more.

4.9.1 let vs const

We used let instead of const. Things that are const cannot be reassigned later. In general I find this to be of minor help but others do not so I leave you to make your own judgement call. In general one should try to follow the “principle of least power.” You should always choose the least powerful “thing” to accomplish whatever you’re trying to do. Things with less power tend to be simpler and simple things are less prone to having or causing bugs. Why don’t you cut your hair with garden shears? You could, it’d work, but it’s way easier to screw it up and has worse consequences. Same general idea here. Right tool for the right job.

We use let here because you can see on the subsequent lines we do reassign friendsAtYourParty to be a different number. If you used const your code would crash because const won’t let you do that. Thus here we use let. There’s another one called var that is the old way of doing JavaScript. There are differences but I don’t see a reason to use var at all anymore. It behaves more similar to let.

4.9.2 Shortcuts for Adding Numbers

Before we get to loops, let’s show you a few shortcuts for adding one to a thing:

let friendsAtYourParty = 0;

friendsAtYourParty = friendsAtYourParty + 1;
friendsAtYourParty += 1;
friendsAtYourParty++;

console.log(friendsAtYourParty);
3

Those three lines are equivalent. They all do the exact same thing: they add one to the existing total. The second one, the plus-equals line, you can put any number there and it’ll add that amount to total, so friendsAtYourParty += 15; would add 15 to the total. It also works with -= (subtraction,) as well as *= (multiplication,) /= (division,) and **= (exponent.) The last one, friendsAtYourParty++;,just signify add one. They more-or-less mean the same thing (there’s a subtle difference of when it adds one that should never matter to you) but suffice to say everyone in the JavaScript community always does the ++ after; I’ve never seen anyone do it before in JavaScript. -- works as well to subtract one.

4.9.3 for Loops

Okay, so now let’s see how to use a loop to achieve the same effect as above without all that repetitive code:

let friendsAtYourParty = 0;

for (let i = 0; i < 10; i++) {
  friendsAtYourParty++;
}

console.log(friendsAtYourParty);
10

This is a for loop which is the most common kind of loop you’ll use. Inside the parentheses are three statements and you need all of them. The let i = 0; is you defining your control variable that will control the loop. For some reason people always use i, not sure why. It’s just tradition. It really could be anything. The second statement i < 10 is just like an if statement condition - as soon as it’s false it breaks the loop. The last statement, i++ is what happens at the end of every loop. In our case, we increment the control variable i so that it creeps closer to the end of the loop each time.

Of course, what happens inside the loop can be any code at all. It will just run each time the loop completes. For example:

for (let i = 0; i < 3; i++) {
  console.log("Print me 3 times!")
}
"Print me 3 times!"
"Print me 3 times!"
"Print me 3 times!"

It’s also important to note that we always have access to the variable i inside the loop. For example, we use our template string with backticks to insert i on each loop:

for (let i = 0; i < 3; i++) {
  console.log(`This is loop ${i}`)
}
"This is loop 0"
"This is loop 1"
"This is loop 2"

An important note: in coding, we start counting from 0. In English, we count 1, 2, 3, 4, 5, etc. but in coding we count 0, 1, 2, 3, 4, etc. So the fifth element of a string is index 4 (where index is how we’d refer to where that item is in the string). Index 0 is the first element. It’s weird but you get used to it and it makes a lot of things easier.

4.9.4 Avoiding Infinite Loops

Sometimes, if you mess up what’s inside the control condition for the loop, you’ll get a runaway loop that’ll never complete. This is called an infinite loop and it’ll lock up and crash your code. Something like this:

// DON'T RUN THIS - it will crash your browser!
let friendsAtYourParty = 1;
for (let i = 0; i > -1; i++) {  // i will never be less than -1
  friendsAtYourParty++;
}

Since i starts at 0 and keeps getting bigger, the condition i > -1 will always be true. Thus it’ll continue going until it crashes your code. Be careful of these. They’re nasty bugs that can lock up your browser. If your browser freezes while running some code, you’ll need to close the browser and re-open it.

4.10 Functions

A function is a bit of re-usable code. Just how we like to re-use CSS classes, we love to re-use code. Functions let you write a piece of code once and then use it over and over again whenever you need it.

Think of a function as a set of instructions that you can name and save for later. When you “call” the function (use its name with parentheses), JavaScript runs all the code inside that function. Let’s start with a simple example:

function sayHello() {
  console.log("Hello there!");
  console.log("How are you today?");
}

sayHello();
sayHello();
"Hello there!"
"How are you today?"
"Hello there!"
"How are you today?"

See how we defined the function once, but then called it twice? Each time we write sayHello(), JavaScript runs all the code inside the curly braces. This saves us from having to write the same console.log statements over and over.

Now let’s make functions more powerful by giving them information to work with:

function addTwo(number) {
  let result = number + 2;
  return result;
}

let answerOne = addTwo(5);
console.log(answerOne);

let answerTwo = addTwo(12);
console.log(answerTwo);
7
14

This isn’t super useful but hopefully it shows you the mechanics of how a function works. We created a function called addTwo. This function takes in one parameter, number and it returns that number with 2 added to it. We can now use that addTwo function as much as we want! Let’s make something a bit more useful.

4.10.1 Functions with Multiple Parameters

The way to call a function is you add parentheses to the end of it, like this: someFunctionName(). If you see parentheses after a variable name, you instantly know that that’s a function. Inside of the parentheses go the parameters. These variables will be passed to the function that is being called in the order that you put them there. These input variables are called parameters.

function greet(firstName, lastName, honorific, greeting) {
  return `${greeting} ${honorific} ${lastName}! I'm extremely pleased you could join us, ${firstName}! I hope you enjoy your stay, ${honorific} ${lastName}.`;
}

console.log(greet("Nick", "Brosowsky", "Dr.", "Salutations"));
console.log(greet("Jack", "Sparrow", "Captain", "A-hoy"));
"Salutations Dr. Brosowsky! I'm extremely pleased you could join us, Nick! I hope you enjoy your stay, Dr. Brosowsky."
"A-hoy Captain Sparrow! I'm extremely pleased you could join us, Jack! I hope you enjoy your stay, Captain Sparrow."

Now rather than have to repeat ourselves over-and-over again with that long string, we can just call greet with the appropriate parameters. Here we use four parameters. The order is important that we send in the parameters because this will be the order the function receives these parameters. You can have as many or as few parameters as you like.

4.11 Built-In Functions

Lots of functions already exist for you! Smart people have created this commonly-used functions for things we often need. For example, say you have a string and you want to make everything lowercase, you can do this:

let sentence = "ThIs HaS wEiRd CaSiNg On It";
let lowerCaseSentence = sentence.toLowerCase();
console.log(lowerCaseSentence);
"this has weird casing on it"

Builtins that manipulate your data often follow the format of variableName.function(), where you add a dot to the end of your variable name and write the name of the builtin function after. However, there is also a set of Math and Date builtins that can be useful and follow a different format. There are actually so many builtins there’s no way we could ever cover all of them.

Instead, I’ve created an appendix for your reference that you can use later. I still haven’t included everything available, but do include the ones that I’ve found most useful.

4.12 Arrays

So far we’ve been working with individual pieces of data such as single numbers, strings, or booleans. But in psychology experiments (and many other coding contexts), you often need to work with collections of data. Maybe you want to store a list of all the stimuli you’ll present, or keep track of all the participant’s responses. This is where arrays and objects become incredibly useful for organizing and storing multiple pieces of related information.

An array is like a list that can hold multiple values. Think of it as a container where you can store many pieces of data in a specific order. Arrays are defined using square brackets with each item in the array separated by a comma. For example:

let myList = ["zero", "one", "two"]

Each item in the array has a position (called an index) starting from 0. Once we’ve defined an array like let myList = ["zero", "one", "two"], we can later access it by referring to different positions in the array like this:

myList[0]
myList[2]

Here are some examples to illustrate this point:

let colors = ["red", "blue", "green", "yellow"];
let reactionTimes = [450, 523, 389, 612, 445];
let responses = [true, false, true, true, false];

console.log("First color:", colors[0]);
console.log("Third reaction time:", reactionTimes[2]);
console.log("Fifth response", responses[4]);
"First color: red"
"Third reaction times: 389"
"Fifth response: false"

Remember that arrays start counting from 0, so the first item is at index 0, the second item is at index 1, and so on. You can access any item by putting its index number in square brackets after the array name. The length property tells you how many items are in the array.

4.12.1 Array Built-In Functions

Arrays come with many built-in functions that are extremely useful for manipulating data inside an array (see Section A.4).

For example, it is quite simple to add something to the end of an array, like adding a new response time to the end of an array of response times using push. It’s also quite simple to join to arrays together using concat.

let responseTimes = [321, 444, 1023]

responseTimes.push(657)

console.log(responseTimes)

let newResponseTimes = [1120, 990]

let allResponseTimes = responseTimes.concat(newResponseTimes)

console.log(allResponseTimes)
[ 321, 444, 1023, 657 ]
[ 321, 444, 1023, 657, 1120, 990 ]

Make sure you have a look through the built-in function appendix to get an idea of what is possible.

4.12.2 Looping Through an Array

One of the most common things you’ll do with arrays is go through each item one by one. This is called “looping” or “iterating” through the array. Here’s how you can do it:


const colors = ["red", "blue", "green", "yellow"];

// Loop through each color and print it
for (let i = 0; i < colors.length; i++) {
  console.log(colors[i]);
}
"red"
"blue"
"green"
"yellow"

Let’s break down what’s happening here:

  • i is our counter variable that keeps track of which position we’re at in the array
  • colors.length gives us the total number of items in the array (4 in this case)
  • colors[i] uses the current value of i to access the item at that position in the array
  • The loop starts with i = 0 (first position), and continues as long as i < colors.length
  • Each time through the loop, i++ increases i by 1, moving us to the next position

So when i is 0, colors[i] gives us colors[0] which is “red”. When i is 1, colors[i] gives us colors[1] which is “blue”, and so on.

4.13 Objects

Objects are like containers that store related information using names (called keys or properties; I’ll use property) instead of numbered positions. Think of an object as a way to group related data together, like all the information about a single participant or trial.

To define an object, you use curly brackets. Inside the curly brackets, you define both the property and the value that is stored in the property. It follows this format:

let newVariable = {
  key1: "value1",
  key2: "value2",
  key3: "value3"
}

Here’s some examples of storing different kinds of information within each key, accessing and printing out the values of properties.

const participant = {
  id: "P001",
  age: 22,
  condition: "experimental",
  completed: true
};

const trialData = {
  stimulus: "red circle",
  response: "left",
  reactionTime: 450,
  correct: true
};

console.log("Participant ID:", participant.id);
console.log("Trial stimulus:", trialData.stimulus);
console.log("Was response correct?", trialData.correct);
"Participant ID: P001"
"Trial stimulus: red circle"
"Was response correct? true"

You access object properties using dot notation (objectName.propertyName) or bracket notation (objectName["propertyName"]). Objects are perfect for storing related information together in a organized way.

As with any other kind of data we store, we can manipulate objects.

let experimentData = {
  participantCount: 0,
  status: "not started"
};

experimentData.participantCount = 25;  // Change existing property
experimentData.startDate = "2025-08-07";  // Add new property
console.log("Updated experiment data:", experimentData);

// You can also use bracket notation
experimentData["endDate"] = "2025-08-14";
console.log("With end date:", experimentData);
"Updated experiment data: {participantCount: 25, status: 'not started', startDate: '2025-08-07'}"
"With end date: {participantCount: 25, status: 'not started', startDate: '2025-08-07', endDate: '2025-08-14'}"

4.13.1 Nested Arrays and Objects

It’s important to note that we can mix and match how data is stored. We can put arrays into objects, objects into arrays, and so forth. When we get to jsPsych in the next chapter, you’re going to see many examples of nested structures, so it’s good to see a simple version of that right now.

Here’s one example of how we might store some participant data. We can see they have one id, but we need arrays to store their responses and their response times. Arrays are useful here because presumably this comes from a series of trials and we want to maintain their order in the data.

let participantData = {
  id: 100,
  responses: ["blue", "red", "green", "red"],
  responseTimes: [564, 989, 520, 1020]
};

console.log("Participant ID:", participantData.id);
console.log("First response:", participantData.responses[0]);
console.log("Last reaction time:", participantData.responseTimes[3]);
"Participant ID: 100"
"First response: blue"
"Last reaction time: 1020"

And perhaps we have many participants. In which case, we could store each participant’s data in an object, and put all those objects inside an array:

let allData = [
  {
    id: 100,
    responses: ["blue", "red", "green", "red"],
    responseTimes: [564, 989, 520, 1020]
  },
  {
    id: 200,
    responses: ["green", "red", "blue", "red"],
    responseTimes: [332, 123, 500, 558]
  },
  {
    id: 300,
    responses: ["red", "red", "blue", "red"],
    responseTimes: [668, 1200, 2554, 852]
  }
];

console.log("Number of participants:", allData.length);
console.log("Second participant's ID:", allData[1].id);
console.log("Third participant's first response:", allData[2].responses[0]);
"Number of participants: 3"
"Second participant's ID: 200"
"Third participant's first response: red"

When working with nested structures, you chain together the access methods. To get the first response from the second participant, you would use allData[1].responses[0]. Let’s break this down:

  • allData[1] gets the second participant object
  • .responses gets the responses array from that participant
  • [0] gets the first response from that array

Finally, many students get tripped up by the syntax when we mix formats like this. Pay careful attention to:

  • Brackets and braces: Each [ must have a matching ], and each { must have a matching }
  • Commas: Separate items in arrays and properties in objects (but no comma after the last item)
  • Quotes: String values must be in quotes, property names can be with or without quotes

You’ll also notice how I indented the lines to help visualize the structure. This isn’t necessary because JavaScript ignores whitespace, but it’s important for readability. The indentation helps you see where each object and array opens and closes, and how they are nested together.

This nested structure is extremely common in psychology experiments where you need to organize complex data with multiple levels of information.

4.14 Applying Loops, Functions and Data Storage

OK, now let’s try to put some these concepts together and see how we might need to use them.

4.14.1 Loops

Here’s a simple example of how might store three participants data, then calculate the average accuracy across all three of them using a loop:

const participants = [
  { id: "P001", age: 22, accuracy: 85, avgRT: 450 },
  { id: "P002", age: 19, accuracy: 92, avgRT: 380 },
  { id: "P003", age: 25, accuracy: 78, avgRT: 520 }
];

// Access some of the data
console.log("First participant:", participants[0].id);
console.log("Second participant's accuracy:", participants[1].accuracy + "%");

// Calculate average accuracy across all participants
let totalAccuracy = 0;
// 1. loop through and add the accuracy scores all together
for (let i = 0; i < participants.length; i++) {
  totalAccuracy += participants[i].accuracy;
}
// 2. divide the total accuracy by the number of participants
const avgAccuracy = totalAccuracy / participants.length;
console.log("Average accuracy across participants:", avgAccuracy + "%");
"First participant: P001"
"Second participant's accuracy: 92%"
"Average accuracy across participants: 85%"

4.14.2 A Data Validation Function

Functions are perfect for tasks you need to repeat many times in an experiment, like checking and validating trial data. Here’s a function that takes a trial object and performs several data quality checks, changing the feedback depending on what the participant did. Note the use of if statements and how we can change what is ‘returned’ inside the if statements.


function validateTrialData(trial) {
  // Check if response time is too slow (over 2 seconds)
  if (trial.responseTime > 2000) {
    return "Too slow";
  }

  // Check if response time is suspiciously fast (under 200ms)
  if (trial.responseTime < 200) {
    return "Too fast";
  }

  // Check accuracy by comparing response to correct answer
  if (trial.response === trial.correctAnswer) {
    return "Correct";
  } else {
    return "Incorrect";
  }
}

// Example trial data
const trial1 = {
  stimulus: "red circle",
  correctAnswer: "left",
  response: "left",
  responseTime: 450
};

const trial2 = {
  stimulus: "blue square", 
  correctAnswer: "right",
  response: "left",
  responseTime: 650
};

const trial3 = {
  stimulus: "green triangle",
  correctAnswer: "right", 
  response: "right",
  responseTime: 2500
};

console.log("Trial 1:", validateTrialData(trial1));
console.log("Trial 2:", validateTrialData(trial2));
console.log("Trial 3:", validateTrialData(trial3));
"Trial 1: Correct"
"Trial 2: Incorrect"
"Trial 3: Too slow"

4.14.3 Looping a Data Validation Function

We can expand on that, to create something a bit more complicated. In the example below, we have four trials all stored in an array. We’re going to loop through them and count how many times they were correct/incorrect/slow/fast. See if you can read through the code below and understand the steps we’ve taken to accomplish that.

function validateTrialData(trial) {
  // Check if response time is too slow (over 2 seconds)
  if (trial.responseTime > 2000) {
    return "Too slow";
  }

  // Check if response time is suspiciously fast (under 200ms)
  if (trial.responseTime < 200) {
    return "Too fast";
  }

  // Check accuracy by comparing response to correct answer
  if (trial.response === trial.correctAnswer) {
    return "Correct";
  } else {
    return "Incorrect";
  }
}

// Array of trial data from an experiment
const experimentData = [
  { stimulus: "red", correctAnswer: "left", response: "left", responseTime: 345 },
  { stimulus: "blue", correctAnswer: "right", response: "right", responseTime: 189 },
  { stimulus: "green", correctAnswer: "left", response: "right", responseTime: 567 },
  { stimulus: "yellow", correctAnswer: "right", response: "right", responseTime: 2100 }
];

// Validate each trial and count results
// start each coutner at 0
let correctCount = 0;
let incorrectCount = 0;
let tooSlowCount = 0;
let tooFastCount = 0;

// loop through the experimentData array
for (let i = 0; i < experimentData.length; i++) {
  const result = validateTrialData(experimentData[i]);
  console.log(`Trial ${i + 1}: ${result}`);

  if (result === "Correct") {
    correctCount++;
  } else if (result === "Incorrect") {
    incorrectCount++;
  } else if (result === "Too slow") {
    tooSlowCount++;
  } else if (result === "Too fast") {
    tooFastCount++;
  }
}

console.log(`\nSummary:`);
console.log(`Correct: ${correctCount}`);
console.log(`Incorrect: ${incorrectCount}`);
console.log(`Too slow: ${tooSlowCount}`);
console.log(`Too fast: ${tooFastCount}`);
"Trial 1: Correct"
"Trial 2: Too fast"
"Trial 3: Incorrect"
"Trial 4: Too slow"

"Summary:"
"Correct: 1"
"Incorrect: 1"
"Too slow: 1"
"Too fast: 1"