Skip to content

Practice 1 · 2026-09-07 · 12 min read · Source code →

JavaScript Fundamentals

ContentsShow
  1. Introduction
  2. Prerequisites: the Live Server extension
  3. Project setup
  4. 1. Printing and comments
  5. 2. Variables and data types
  6. 3. Template literals
  7. 4. Types and typeof
  8. 5. Converting between types
  9. 6. Type coercion
  10. 7. Equality: === vs ==
  11. 8. Arrays and loops
  12. 9. Functions
  13. 10. map()
  14. 11. Beyond the class: filter, chaining, spread, objects
  15. Summary
  16. Tasks
  17. Next steps

Introduction

This is the first JavaScript practice. You already know C, so a lot of what follows will look familiar: variables, if, loops, functions, arrays. The interesting parts are the places where JavaScript deliberately behaves differently, and there are more of those than you would expect.

Everything today happens in the browser Console. We do not touch the page itself yet, we only print values. Changing the page is Practice 2.

What we cover:

  1. Running JavaScript from an HTML page, and reading the Console
  2. Variables: let, const, var, and block scope
  3. Dynamic typing, typeof, and converting between types
  4. Type coercion, == vs ===
  5. Arrays, for and for...of, conditions
  6. The four ways to write a function
  7. map(), and why it replaces a loop you already know how to write
ℹ️

The two groups saw the same material with slightly different variable names and a few different examples on the projector. This article contains everything both groups saw, so if a snippet looks unfamiliar, it is not because you missed it: it is probably the other group’s variant of the same idea. The last section also covers filter, chaining and objects, which we did not get to in class but which the in-class task package needs.

Prerequisites: the Live Server extension

You need the Live Server extension for VS Code. It serves your folder over http:// and reloads the browser every time you save.

  1. Open VS Code
  2. Open the Extensions panel (Ctrl+Shift+X / Cmd+Shift+X)
  3. Search for “Live Server” by Ritwick Dey
  4. Click Install

Direct link: Live Server Extension

To use it: right-click index.html in the file explorer and pick “Open with Live Server”, or click “Go Live” in the bottom-right status bar.

💡

Opening the file by double-clicking it in your file manager also “works”, but the address bar then says file:///... instead of http://127.0.0.1:5500/.... Several things we do later in the semester (modules, fetch, PHP) refuse to run from file://. Get into the habit of using Live Server from day one.

Opening the Console

  1. Open the page in the browser
  2. Press F12 (Windows/Linux) or Cmd+Option+I (Mac)
  3. Switch to the Console tab

Everything console.log() prints shows up there, together with any error messages. Keep VS Code and the browser side by side if your screen allows it.

Project setup

The folder we used in class had three files:

plaintext
practice-01/
├── index.html    # the page, with a <script> tag
├── script.js     # our JavaScript code
└── style.css     # a stylesheet (barely used today)
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
  <script src="script.js"></script>
</head>
<body>

  <h1>Hello, Krisztián</h1>
  <h2>MF5M7S</h2>

</body>
</html>
css
/* style.css */
h1 {
  color: red;
}
⚠️

<script src="script.js"></script> needs a separate closing tag. <script src="script.js" /> is not valid HTML and the file will silently never load. Whether you write the closing tag right after the opening one or a few lines below it makes no difference.

ℹ️

Right now the <script> sits in the <head> and everything works, because we never touch the page from JavaScript. In Practice 2 we start reading elements out of the page, and this exact line will break: the script runs before the <body> exists. We will fix it then, with defer or by moving the tag. Just remember that it is here.

1. Printing and comments

javascript
console.log("Hello world");
console.log('Hello world with single quotes');

Double quotes and single quotes are identical in JavaScript. Pick one and stay consistent (this course uses double quotes). There is a third kind of quote, the backtick, and it does something extra - see template literals below.

Comments are the same two forms as in C:

javascript
// single-line comment

/*
Multiple
lines
of comments
*/

2. Variables and data types

In C you declare a type: int x. JavaScript never asks you for one. A variable is just a name, and it can hold anything.

javascript
let name = "Krisztián";     // can be reassigned
const neptun = "MF5M7S";    // cannot be reassigned
var oldStyle = "avoid me";  // legacy, do not use

let - reassignable

javascript
let name = "Krisztián";
console.log(name);   // Krisztián

name = "Someone";    // fine
console.log(name);   // Someone

You may declare a variable without giving it a value:

javascript
let age;
console.log(age);    // undefined

In C an uninitialised local variable contains garbage. In JavaScript it contains undefined, which is a real value meaning “nothing has been put here yet”.

const - not reassignable

javascript
const university = "ELTE";
console.log(university);   // ELTE

university = "BME";        // TypeError: Assignment to constant variable.

Rules:

  • must be given a value at declaration
  • cannot be reassigned afterwards
  • block-scoped, like let

Use const by default. Switch to let only when you actually intend to reassign the variable. It is not about safety theatre: a const tells the next reader “this name means the same thing everywhere below”, which is genuinely useful information.

💡

Run the university = "BME" line on purpose once and read the red error in the Console: “Assignment to constant variable.” JavaScript error messages are usually this direct. Read them before asking anyone what went wrong.

var - legacy, and why we skip it

javascript
{
  let y = 1;
  var z = 2;
}

console.log(z);   // 2   - var escaped the block
console.log(y);   // ReferenceError: y is not defined

var is function-scoped: it ignores { } blocks entirely and leaks out of them. let and const are block-scoped: they only exist between the braces they were declared in, exactly like C.

You will meet var in older code and in older tutorials online. Do not write new code with it.

⚠️

One group used name for the variable, the other used name2. Both are fine with let. The reason to be slightly careful is that browsers already have a global window.name (the name of the browser tab), and it is always a string. With var name = 5 you would be writing into that global and silently get "5" back. With let name = 5 you create your own separate variable and nothing strange happens. Another small reason to leave var alone.

Comparison with C

CJavaScript
int age = 23;let age = 23; or const age = 23;
type declared explicitlyno type in the declaration
type cannot changetype can change at any time
int x; holds garbagelet x; holds undefined
{ } limits scope{ } limits let / const, but not var

3. Template literals

Gluing strings together with + works, but it gets unreadable fast:

javascript
const name = "Krisztián";

console.log("Hello, " + name);      // Hello, Krisztián

The modern way uses backticks and ${ }:

javascript
console.log(`Hello, ${name}`);      // Hello, Krisztián
console.log(`Hello, ${name} ...`);  // Hello, Krisztián ...

If you know C#, ${ } is the same idea as $"hello {name}". In C the closest thing is printf("Hello, %s\n", name), except here the expression sits inside the string itself.

Anything can go inside ${ }, not just a variable name:

javascript
const a = 1;

console.log(`a is ${a}.`);
console.log(`a doubled is ${a * 2}.`);

Backticks also allow real multi-line strings:

javascript
const message = `This is line 1
This is line 2
The value is ${a}`;
⚠️

${ } only works inside backticks. "${a}" with normal quotes prints the literal text ${a}. This is the single most common first-day mistake, and it does not produce an error: it just prints the wrong thing.

4. Types and typeof

The typeof operator tells you what kind of value you are holding:

javascript
console.log(typeof "ELTE");   // "string"
console.log(typeof 23);       // "number"
console.log(typeof 3.14);     // "number"
console.log(typeof true);     // "boolean"

let age;
console.log(typeof age);      // "undefined"

The primitive types you need today are string, number, boolean and undefined.

Note what is missing compared to C: there is no int / float / double split, and no char. 23 and 3.14 are both number. A single character is just a string of length 1.

ℹ️

Both typeof university and typeof(university) appear in the class recordings, and both are correct. typeof is an operator, not a function, so the parentheses are optional and are simply grouping the expression. Do not be surprised by either form.

5. Converting between types

A very common situation: you have a string that contains digits and you need an actual number. (Later in the semester, everything you read out of an input field arrives as a string, so this comes back.)

javascript
const aNumber = "10";
console.log(typeof aNumber);              // "string"

Three ways to convert it:

javascript
// 1. Number() - the explicit, readable one
console.log(typeof Number(aNumber));      // "number"

// 2. parseInt() - reads an integer from the front of the string
console.log(parseInt(aNumber));           // 10

// 3. unary + - short, and you will see it in other people's code
const asNumber = +aNumber;
console.log(typeof asNumber);             // "number"

Prefer Number(). It is the one that reads as what it does.

The difference between Number() and parseInt() shows up on messy input:

javascript
console.log(Number("10px"));      // NaN     - refuses the whole string
console.log(parseInt("10px"));    // 10      - takes the leading digits
console.log(parseInt("3.9"));     // 3       - integer only, cuts the rest

NaN

When a conversion or a calculation fails, you get NaN:

javascript
console.log(Number("hello"));     // NaN
console.log(typeof NaN);          // "number"   (!)

NaN stands for “Not a Number”, and its type is number. That is not a typo: it is the value produced by a failed numeric operation, so it lives in the number world.

It has one more oddity worth parking in your head:

javascript
console.log(NaN === NaN);         // false

Every other value in the language equals itself. NaN does not, because it means “a calculation that failed”, and two failures are not the same failure.

6. Type coercion

Because variables have no fixed type, JavaScript converts values automatically when an operator needs it. This is called coercion, and it is where the language’s reputation comes from.

javascript
console.log("2" + 3);        // "23"    - + with a string glues
console.log("5" - 3);        // 2       - - has no string meaning, so it converts
console.log(4 * "0" + 2);    // 2
console.log(4 - "1" + "6");  // "36"    - 4-1=3, then "3"+"6"

The rule underneath is small: + means “add” for numbers and “join” for strings, so as soon as one side is a string you get joining. Every other arithmetic operator (-, *, /, %, **) has no string meaning, so it converts both sides to numbers first.

Two examples from the projector, worth walking through slowly:

javascript
console.log("2" + 3 * 6);

* binds tighter than +, exactly like in C, so 3 * 6 runs first and gives 18. Then "2" + 18 joins, giving "218".

javascript
console.log("2" * 3 + 2 * "abc");

"2" * 3 converts and gives 6. 2 * "abc" cannot convert, so it gives NaN. Then 6 + NaN is NaN, because anything arithmetic touching NaN stays NaN.

💡

You are not expected to memorise the conversion table. You are expected to recognise the shape of the problem, so that when a "218" or a NaN shows up in the Console you know where to look. The practical defence is the next section.

7. Equality: === vs ==

javascript
console.log("1" == 1);    // true    - loose: converts, then compares
console.log("1" === 1);   // false   - strict: compares value AND type
OperatorNameBehaviourExample
==loose equalityconverts types, then compares"1" == 1 is true
===strict equalitycompares value and type"1" === 1 is false
!=loose inequalityconverts types61 != "61" is false
!==strict inequalitycompares value and type78 !== "78" is true

More examples:

javascript
console.log(17 == "17");    // true
console.log(85 === "85");   // false
console.log(false == 0);    // true
console.log(false === 0);   // false

Used in a condition:

javascript
if ("1" === 1) {
  console.log("They are the same");
}
else {
  console.log("They are different.");
}
⚠️

Always use === and !==. If you ever feel you need ==, you do not: convert explicitly with Number() and then compare strictly. The in-class task package requires strict comparison.

Truthy and falsy

A condition does not need a boolean. Any value works, and JavaScript decides whether it counts as true:

javascript
if ("") console.log("empty string is truthy");        // does not print
if (0) console.log("zero is truthy");                 // does not print
if ("hello") console.log("non-empty string is truthy"); // prints

Falsy values, the complete list: false, 0, "", null, undefined, NaN. Everything else is truthy, including "0", [] and {}.

This becomes genuinely useful when we validate forms. For now, just know it exists, because it explains a few accidents later in this article.

8. Arrays and loops

javascript
let data = [1, 2, 3];

console.log(data[0]);       // 1
console.log(data.at(0));    // 1   - same thing, newer syntax
console.log(data.length);   // 3

Compared to C arrays:

  • no size declared up front, and they grow and shrink freely
  • .length is a property, not .size and not length()
  • elements may be of mixed types ([1, "hello", true] is legal, though usually a bad idea)
  • data.at(i) does the same as data[i], but also accepts negative indexes: data.at(-1) is the last element

An empty array to start from is just let data = [];.

Classic for loop

Identical to C:

javascript
let data = [1, 2, 3];

for (let i = 0; i < data.length; i++) {
  console.log("element: " + data[i]);
  console.log(`The ${i}-th element is: ${data[i]}`);
}

for...of

The version you should reach for by default:

javascript
let total = 0;

for (const item of data) {
  console.log(item);
  total = total + item;
}

console.log("Total is " + total);   // Total is 6

What disappeared is the index. Most of the time you do not want the position, you want the value, and i only exists as a source of off-by-one bugs. Use for...of unless you specifically need the index.

ℹ️

Why can item be const inside a loop that runs three times? Because each turn of the loop creates a new item. It is never reassigned, it is re-created, so const is honest here.

Arithmetic operators

+, -, *, /, plus two that matter today:

javascript
console.log(7 % 2);    // 1   - remainder
console.log(6 % 2);    // 0
console.log(2 ** 3);   // 8   - 2 to the power of 3

So n % 2 === 0 means “n is even”. You will type that a lot.

⚠️

** is exponentiation. ^ is not: in JavaScript (as in C) ^ is bitwise XOR, so 2 ^ 3 gives 1, not 8.

+= and friends work as in C:

javascript
total += item;    // same as total = total + item

Task: sum the even elements

javascript
function sumEven(data) {
  let result = 0;

  for (const item of data) {
    if (item % 2 === 0) {
      result += item;
    }
  }

  return result;
}

console.log(sumEven([2, 3, 4]));   // 6
💡

Start your accumulator at 0, not with a bare let result;. undefined + 2 is NaN, and then everything after it is NaN too. This is the most common way NaN shows up in a first-week homework.

Building a new array with push

javascript
let data = [1, 2, 3];
let squaredResult = [];

for (const item of data) {
  squaredResult.push(item ** 2);
}

console.log(squaredResult);   // [1, 4, 9]
console.log(data);            // [1, 2, 3]   - untouched

Three moves: create an empty array, loop over the original, push each new value into the new array.

Look at that second console.log. The input array did not change. We built something new instead of damaging what we had, and that habit matters for the whole semester.

ℹ️

const squaredResult = [] followed by squaredResult.push(...) is perfectly legal, and it is the form you should prefer. const means the variable always points at the same array; it does not mean the array is frozen. You can fill it, you just cannot say squaredResult = somethingElse afterwards.

9. Functions

A function declaration looks almost like C, minus the types:

javascript
function sum(x, y) {
  return x + y;
}

console.log(sum(1, 2));   // 3

No return type, no parameter types, and no forward declarations.

A function with no parameters and no return value:

javascript
function print() {
  console.log("Printing");
}

print();

Functions are values

This is the part that has no C equivalent. A function can be stored in a variable, exactly like a number can:

javascript
// 1. function declaration
function sum(x, y) {
  return x + y;
}

// 2. function expression
const sum2 = function (x, y) {
  return x + y;
};

// 3. arrow function, with a body
const sum3 = (x, y) => {
  return x + y;
};

// 4. arrow function, concise
const sum4 = (x, y) => x + y;

console.log(sum(1, 2));    // 3
console.log(sum2(1, 2));   // 3
console.log(sum3(1, 2));   // 3
console.log(sum4(1, 2));   // 3

All four do the same thing. The differences that matter today:

  • an arrow function with { } needs an explicit return, like any other body
  • an arrow function without braces returns its single expression automatically, because there is nothing else it could mean
  • with exactly one parameter the parentheses are optional: x => x * 2
  • with no parameters you still need them: () => console.log("Hi")
⚠️

The nastiest beginner bug in this area: (x) => { x * 2 }. Braces mean “here comes a body”, and a body without return returns undefined. Either drop the braces or add the return.

The important idea is not the syntax, it is that sum2 is a variable holding a function. And if a function is a value, you can hand it to something else. That is the whole basis of the next section.

10. map()

Visual diagram showing how filter() selects elements and map() transforms elements
Array methods: filter() selects elements that pass a test, map() transforms each element
Visual diagram showing how filter() selects elements and map() transforms elements

Array methods: filter() selects elements that pass a test, map() transforms each element

Look at the loop we wrote a moment ago:

javascript
const squaredResult = [];
for (const item of data) {
  squaredResult.push(item ** 2);
}

Make a new array, walk the old one, put one new element in for each old one. That pattern is so common that arrays have a method for it:

javascript
const data = [1, 2, 3];

const resultMap = data.map((item) => item ** 2);

console.log(resultMap);   // [1, 4, 9]
console.log(data);        // [1, 2, 3]   - still untouched

Five lines against one, same result. [1,2,3] becomes [1,4,9].

ℹ️

map is not a new capability, it is a shorter way to write something you can already do by hand. That is exactly why we wrote the loop first. When you are unsure what a map call does, mentally expand it back into the loop.

The function you pass in

The thing inside the parentheses is a callback: a function that map calls once per element. It can be written any of the ways from the previous section:

javascript
data.map(function (item) { return item ** 2; });   // function expression
data.map((item) => { return item ** 2; });         // arrow with a body
data.map((item) => item ** 2);                     // arrow, concise

Or you can pass a function you already have, by name:

javascript
const squareThis = (item) => item ** 2;

// or, equivalently:
function squareThis2(item) {
  return item ** 2;
}

const resultMap2 = data.map(squareThis);
const resultMap3 = data.map(squareThis2);
⚠️

Note there are no parentheses after squareThis in data.map(squareThis). squareThis is the function itself, which is what map wants. squareThis() would call it right now, with no argument, and hand map the result. Passing versus calling is the distinction to get right here.

The rules of map

  • one element in, one element out: the result always has the same length as the input
  • it always returns a new array, and never modifies the original
  • if your callback returns nothing, you get an array full of undefined
💡

If you called map and nothing seems to happen, you probably forgot to store or return the result. map builds a new array and hands it back; it does not change data in place.

11. Beyond the class: filter, chaining, spread, objects

We ran out of time before these in class, so read this section before doing tasks g to j. Nothing here is harder than map.

filter() - keep some elements

javascript
const numbers = [1, 2, 3, 4, 5, 6];

const evens = numbers.filter((number) => number % 2 === 0);

console.log(evens);   // [2, 4, 6]

The difference from map is entirely in what the callback returns:

  • map wants a new value for each element, and gives back an array of the same length
  • filter wants a yes or no for each element, and gives back a shorter array (possibly empty)

Side by side:

javascript
numbers.map((n) => n % 2 === 0);      // [false, true, false, true, ...]  - useless
numbers.filter((n) => n % 2 === 0);   // [2, 4, 6]                        - what you wanted

You can pass a named function here too:

javascript
function isEven(number) {
  return number % 2 === 0;
}

const evens = numbers.filter(isEven);

Chaining

filter returns an array. Arrays have .map. So you can keep going:

javascript
const numbers = [1, 2, 3, 4, 5, 6];

const result = numbers
  .filter((number) => number % 2 === 0)
  .map((number) => number ** 3);

console.log(result);   // [8, 64, 216]

Read it left to right, out loud: take the numbers, keep the even ones, cube them.

Doing it in two steps with an intermediate variable is equally correct, and sometimes clearer:

javascript
const evens = numbers.filter((number) => number % 2 === 0);
const cubed = evens.map((number) => number ** 3);

Imperative versus functional, one last time

With a loop:

javascript
const result = [];

for (const number of numbers) {
  if (number % 2 === 0) {
    result.push(number ** 3);
  }
}

With array methods:

javascript
const result = numbers
  .filter((number) => number % 2 === 0)
  .map((number) => number ** 3);

The second version says what you want rather than how to walk the array, it cannot go off by one, and it leaves the input alone. Both are correct code; the second is the one we build on from here.

Spread syntax (...)

Three dots mean “unpack this array here”:

javascript
const myArray = [3, 7, 25];
const newArray = [5, ...myArray, 8];

console.log(newArray);   // [5, 3, 7, 25, 8]

Joining two arrays:

javascript
const first = [1, 2];
const second = [3, 4];
const both = [...first, ...second];

console.log(both);     // [1, 2, 3, 4]
console.log(first);    // [1, 2]   - untouched

Copying an array:

javascript
const original = [1, 2, 3];
const copy = [...original];   // a genuinely new array, not another name for the same one

It also turns array-like things into real arrays, which is how we will use it on the DOM in a few weeks:

javascript
const nodeList = document.querySelectorAll(".item");

const array1 = Array.from(nodeList);
const array2 = [...nodeList];

Objects, the minimum needed for task j

Arrays store values by position. Objects store them by name:

javascript
const person = { name: "Alice", age: 23 };

console.log(person.name);   // "Alice"
console.log(person.age);    // 23

person.age = 24;            // change a value
person.city = "Budapest";   // add a new one

Building one up as a counter:

javascript
const counters = { even: 0, odd: 0 };

counters.even = counters.even + 1;
counters.odd += 1;

console.log(counters);   // { even: 1, odd: 1 }

That is all task j needs. We come back to objects properly later.

Summary

Variables

  • const by default, let when you will reassign, never var
  • let x; gives undefined, not garbage
  • let / const are block-scoped; var leaks out of blocks

Types

  • one numeric type: number (no int / float split), plus string, boolean, undefined
  • typeof value reports the type; the parentheses in typeof(value) are optional
  • convert with Number(), parseInt() or unary +; a failed conversion gives NaN

Operators and equality

  • % remainder, ** power (not ^)
  • + joins when either side is a string, everything else converts to numbers
  • always === / !==, never == / !=
  • falsy: false, 0, "", null, undefined, NaN; everything else is truthy

Arrays and loops

  • [1, 2, 3], .length, data[i] or data.at(i)
  • for (let i = 0; ...) when you need the index, for (const item of data) otherwise
  • build new arrays with an empty array plus push, do not modify the input

Functions

  • function f(a, b) { }, const f = function (a, b) { }, const f = (a, b) => { }, const f = (a, b) => a + b
  • functions are values, so they can be passed to other functions
  • concise arrows return automatically; arrows with braces need return

Array methods

  • map() transforms each element, giving a new array of the same length
  • filter() keeps the elements whose callback returns true, giving a shorter array
  • both return a new array and leave the original alone; they chain

Tasks

In class / at home: Practice 1 - In-class tasks

Ten small functions, a to j, in exactly the order of this article. We solved several of them together in class; finish the rest at home. There are no automatic tests here: you write the function, call it, and compare what the Console prints against the expected line in the task description.

The spine of the package is e and f. Task e cubes an array with a loop and push; task f does the same job in one line with map. Write both, run both, and see that they agree.

Homework, graded: Arcade Scores - array methods

Bigger, ten functions, real messy data with strings and null mixed into the numbers. It is checked automatically, so read its rules carefully: it insists on plain global functions, with no import and no export.

💡

If the Console prints undefined where you expected a value, you almost certainly forgot a return. console.log prints something; return hands a value back to the caller. They are not the same thing, and a function that only logs returns undefined.

You can also try short snippets without creating a project at all, on the site’s playground.

Next steps

Next week we stop printing to the Console and start changing the page itself:

  • DOM basics: finding elements with querySelector, reading and writing their content
  • Events: reacting to clicks and typing
  • and the reason the <script> tag in the <head> is about to become a problem

Keep practising with arrays, functions and map. Practically everything we write for the rest of the semester is built on those three.