Workbook Day 2 - Javascript Core Programming Concepts
Workbook Day 2 - Javascript Core Programming Concepts
Conversational Guide
Estimated reading time: 35 minutes
Introduction
User: Hi there! I've been learning JavaScript basics, but I'm still confused about some of the core
programming concepts. Could you help me understand them better?
Expert: Absolutely! I'd be happy to guide you through some fundamental JavaScript concepts. These are
the building blocks that will help you write more efficient and elegant code.
4. Function expressions
5. Arrow functions
Expert: Let's begin with loops since they're one of the most common programming structures you'll use.
Ready to jump in?
Expert: Loops are programming structures that let you repeat code multiple times. Instead of writing the
same code over and over, loops help you automate repetitive tasks.
Expert: Sure! A while loop executes a block of code as long as a specified condition is true. Here's the basic
syntax:
while (condition) {
// code to be executed
}
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
User: I see! And what if I want the code to run at least once, regardless of the condition?
Expert: Great question! That's exactly what the do...while loop is for. It executes the code block once
before checking the condition:
let i = 0;
do {
console.log(i);
i++;
} while (i < 5);
The key difference is that a do...while loop always runs at least once, even if the condition is initially false.
User: What about the for loop? I've seen it used frequently in code examples.
Expert: The for loop is probably the most commonly used loop in JavaScript. It's more compact and
provides better control over the iteration process. Here's the syntax:
Tip: The for loop is generally preferred when you know exactly how many times you need to loop,
while while loops are better when the number of iterations depends on a condition that might
change during execution.
Here's an example:
User: What about nested loops? Is there an easy way to break out of multiple loops?
Expert: Yes! For nested loops, JavaScript provides labels. A label is an identifier followed by a colon that you
place before a loop:
In this example, when i * j === 4 is true, we break out of both loops completely.
Fun Fact: While JavaScript has labels, they're relatively uncommon in modern code. Many
developers consider them a sign that your code might benefit from being refactored into smaller
functions!
Expert: Great question! The switch statement is ideal when you're comparing a single value against
multiple variants. It's more readable and often more efficient than a long chain of if/else statements.
Expert: Sure! Let's say we want to display a message based on a user's role:
switch (role) {
case 'admin':
console.log('Full access granted');
break;
case 'editor':
console.log('Edit access granted');
break;
case 'subscriber':
console.log('View access granted');
break;
default:
console.log('No access');
}
Expert: Without a break statement, the code will "fall through" to the next case! This behavior can be
either a bug or a feature, depending on how you use it.
switch (fruit) {
case 'apple':
console.log('This is an apple');
// No break here!
case 'fruit':
console.log('This is a fruit');
break;
default:
console.log('Unknown item');
}
// Outputs:
// "This is an apple"
// "This is a fruit"
User: Oh! So the execution continues to the next case? Can that be useful?
Expert: Absolutely! This "fall-through" behavior lets you handle multiple cases with the same code. For
example:
let day = 2;
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log('Weekday');
break;
case 6:
case 0:
console.log('Weekend');
break;
}
Tip: Remember that switch uses strict comparison ( === ), so types matter! switch(1) will not
match case '1' because one is a number and one is a string.
Functions
User: What exactly are functions and why are they so important?
Expert: Functions are one of the fundamental building blocks in JavaScript. Think of a function as a
reusable block of code designed to perform a particular task. They help us:
4. Create abstractions
function sayHello(name) {
return "Hello, " + name + "!";
}
Parameters are the variables listed in the function definition (in our example, name is a parameter)
Arguments are the values passed to the function when it's called (in our example, "Alex" is an
argument)
An easy way to remember: parameters are placeholders, arguments are actual values.
Expert: Not necessarily. If you don't include a return statement, the function will automatically return
undefined .
Functions that don't return a value explicitly are sometimes called "procedures" or "void functions." These
functions are typically used for their side effects (like modifying DOM elements or logging information)
rather than for their return values.
function greet(name) {
console.log("Hello, " + name + "!");
// No return statement
}
Tip: Always be explicit about your function's return values. If a function is meant to return a value,
make sure it has a clear return statement. If it's not meant to return anything useful, consider
making that clear in the function's name or documentation.
User: What about function naming? Are there any conventions I should follow?
Expert: Absolutely! Function naming is crucial for code readability. Here are some guidelines:
2. Start with a verb that describes the action (e.g., getUser , not just user )
3. Be specific about what the function does (e.g., validateEmailAddress is better than just validate )
Expert: Great observation! In JavaScript, there are multiple ways to define functions. A function
expression is when you assign a function to a variable:
The key difference is that you're treating the function as a value that can be assigned to a variable.
1. Hoisting: Function declarations are hoisted (moved to the top of their scope), but function expressions
are not. This means you can call a function declaration before it appears in your code, but you can't do
the same with a function expression.
// This works
console.log(greeting("Jordan")); // "Hello, Jordan!"
function greeting(name) {
return "Hello, " + name + "!";
}
2. Anonymous functions: Function expressions are often anonymous (don't have a name), though they
can be named too.
Fun Fact: Named function expressions have a special property: the name is only accessible within
the function itself. This is useful for recursive functions or for better stack traces during debugging!
2. Immediate execution: Creating functions that run once and don't need a name
3. Conditionally defining functions: Creating different function implementations based on conditions
User: I've heard about arrow functions too. How do they relate to these concepts?
Arrow Functions
Expert: Arrow functions were introduced in ES6 (2015) and provide a more concise syntax for writing
function expressions. They're especially useful for short, single-purpose functions:
// Arrow function
let multiplyArrow = (a, b) => a * b;
console.log(multiply(5, 3)); // 15
console.log(multiplyArrow(5, 3)); // 15
User: Wow, that's much shorter! Are there any rules for the syntax?
Expert: Yes! The syntax varies slightly depending on the parameters and body:
User: Are there any important differences between arrow functions and regular functions?
1. No this binding: Arrow functions don't have their own this value. Instead, they inherit this from
the surrounding code.
2. No arguments object: Arrow functions don't have an arguments object.
3. Can't be used as constructors: You can't use new with arrow functions.
Tip: Don't use arrow functions for methods that need to access this from their containing object.
Use regular function expressions instead.
When you want to preserve the this value from the surrounding context
Constructor functions
Functions with complex logic where the body clarity is more important than concise syntax
User: Let's move on to JavaScript specifics now. What makes JavaScript unique compared to other
languages?
JavaScript Specials
JavaScript Specials
Expert: JavaScript has several unique characteristics that set it apart from other programming languages.
Let's go through some key "JavaScript specials"!
Expert: JavaScript's syntax is quite flexible, which can be both good and bad!
1. Semicolons are technically optional in many cases due to automatic semicolon insertion, but most
style guides recommend using them:
if (condition) {
// Code block
}
'use strict';
// Now your code runs in strict mode
4. boolean - true/false
Fun Fact: typeof null returns "object" , which is actually a historical bug in JavaScript! Null isn't
really an object, but the language maintains this quirk for backward compatibility.
User: And what about operators in JavaScript? Are there any special ones?
Expert: JavaScript has all the standard arithmetic, comparison, and logical operators, plus a few special
ones:
1. Nullish coalescing operator ( ?? ): Returns the right-hand operand when the left one is null or
undefined
User: I'd like to know more about debugging and writing clean code. Can you help?
Code Quality
Expert: Absolutely! Writing clean, debuggable code is a crucial skill.
Expert: Modern browsers come with powerful developer tools. Here's a quick guide to debugging with
Chrome DevTools:
2. Set breakpoints:
Step Over (F10): Execute the current line and move to the next one
4. Examine variables: View local and global variables in the Scope panel
5. Use the console: Output values with console.log() or evaluate expressions directly
Tip: console.log() is great, but also check out other console methods like console.table() for
displaying tabular data, console.time() and console.timeEnd() for performance measurements,
and console.group() for organizing related logs.
User: What about writing clean, maintainable code? Any best practices?
Expert: Absolutely! Here are some key principles for writing clean JavaScript code:
Coding Style
1. Consistent indentation: Use 2 or 4 spaces (or tabs, if your team prefers)
2. Meaningful names: Variables and functions should have clear, descriptive names
// Bad
let x = 5;
// Good
let userAge = 5;
4. Comments: Write comments for complex logic, but aim to make most code self-explanatory
5. Consistent formatting: Use a style guide or a linter like ESLint to enforce consistency
// Decide on a style and stick with it
if (condition) {
// Braces on same line
}
if (condition)
{
// Braces on next line
}
User: Those are great guidelines! Let's practice some of these concepts with exercises.
Practice Exercises
Expert: Perfect! Let's work through a few exercises to reinforce what we've learned.
Exercise 1: Loops
User: What's a good exercise for practicing loops?
Expert: Try writing a function that counts down from a given number to zero:
function countdown(n) {
for (let i = n; i >= 0; i--) {
console.log(i);
}
}
Expert: Perfect! That's a great solution using a for loop. You could also solve it with a while loop:
function countdown(n) {
while (n >= 0) {
console.log(n);
n--;
}
}
Expert: Try writing a function that converts a numerical grade to a letter grade:
// Write a function that converts a numerical grade (0-100) to a letter grade (A-F)
function getLetterGrade(score) {
// Your code here
}
function getLetterGrade(score) {
switch(true) {
case score >= 90:
return "A";
case score >= 80:
return "B";
case score >= 70:
return "C";
case score >= 60:
return "D";
default:
return "F";
}
}
Expert: Excellent! Using switch(true) and case statements with conditions is a clever approach. This
shows you understand how to use switch statements creatively.
// 1. Function declaration
function calculateArea(length, width) {
return length * width;
}
// 2. Function expression
// Your code here
// 3. Arrow function
// Your code here
// 2. Function expression
const calculateAreaExp = function(length, width) {
return length * width;
};
// 3. Arrow function
const calculateAreaArrow = (length, width) => length * width;
Expert: Sure! Let's create a function that filters an array of numbers based on a condition, using arrow
functions and loops:
// Write a function filterNumbers that takes an array of numbers and a function condition
// It should return a new array with only the numbers that pass the condition
function filterNumbers(numbers, condition) {
// Your code here
}
// Example usage:
// const evenNumbers = filterNumbers([1, 2, 3, 4, 5], num => num % 2 === 0);
// console.log(evenNumbers); // [2, 4]
return result;
}
// Testing:
const evenNumbers = filterNumbers([1, 2, 3, 4, 5], num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]
Expert: Excellent! Your solution correctly implements the function that filters numbers based on the
provided condition. This exercise combines loops, function parameters, and arrow functions.
Conclusion
User: Thanks so much for explaining all these concepts! I feel like I understand JavaScript much better now.
Any final tips for continuing to improve my skills?
Expert: You've made great progress! Here are some final tips for your JavaScript journey:
1. Practice regularly: The more you code, the better you'll get. Try to solve small problems daily.
2. Read good code: Look at well-maintained open-source projects to learn coding patterns and styles.
3. Build projects: Apply what you've learned by building small applications that interest you.
4. Use developer tools: Get comfortable with browser dev tools for debugging.
5. Stay updated: JavaScript is evolving, so follow blogs, podcasts, or YouTube channels that cover the
latest features.
6. Code review: If possible, have experienced developers review your code and provide feedback.
7. Be patient: Becoming proficient takes time. Don't get discouraged by complex concepts.
User: That's really helpful advice. Where should I go next with my JavaScript learning?
1. Strings
3. Functions
Remember, learning to program is a marathon, not a sprint. Take your time to fully understand each
concept before moving on. You're doing great!
User: Thank you so much for all your help! I'm excited to keep learning and practicing!
Expert: You're welcome! Your enthusiasm will take you far. Happy coding!
JavaScript in Action: Building a Temperature
Converter
Problem Introduction
User: Hey there! I'm trying to build a simple temperature converter using JavaScript. I need it to convert
between Celsius, Fahrenheit, and Kelvin. I'm still new to JavaScript, so I'm not sure where to start.
Expert: That's a great practical project! A temperature converter will help you practice several JavaScript
concepts like functions, switch statements, and conditionals.
User: Exactly! I want users to input a temperature value, specify what unit it's in, and then choose what unit
they want to convert to. Then the program should output the converted value.
User: Yes, that's exactly it! And if possible, I'd like to add some validation to make sure the inputs are valid.
For example, Kelvin can't be negative.
Expert: Great addition! Validation is definitely important. Let's start planning how we'll approach this.
Planning Phase
Expert: Before we dive into code, what approach are you thinking of taking? Any ideas on how to structure
this solution?
User: Well, I think I need some functions to handle the different conversions. Maybe I could have separate
functions like celsiusToFahrenheit() , celsiusToKelvin() , and so on? And I guess I'll need to use if
statements or maybe a switch to determine which conversion to perform.
Expert: That's a good starting point! Having separate conversion functions is a clean approach.
What if we took a slightly different approach? What if we had functions to convert any unit to a "standard"
unit (let's say Celsius), and then from that standard unit to any other unit?
User: Hmm, that makes sense! So we'd have functions like toCelsius() and fromCelsius() ? That would
be more efficient.
Expert: Exactly! This approach means we only need two main functions instead of six. Then we can use a
switch statement to determine which specific conversion path to take.
Let's start implementing this step by step.
Implementation Phase
Expert: First, let's define the variables and functions we'll need. We'll start with the conversion functions.
User: Yes, that seems logical! For the toCelsius function, we need to check what unit we're converting
from and then apply the correct formula. And for fromCelsius , we check what unit we're converting to.
Then the main function ties it all together.
I'm using toLowerCase() to handle inputs like "Celsius", "CELSIUS", etc. Now, can you try to implement the
fromCelsius function using the same pattern?
Is that correct?
Expert: That's perfect! Now let's implement the main convertTemperature function that will use both of
these functions:
User: That looks good! But what happens if someone enters an invalid unit like "Miles" or something? Will it
be caught?
Expert: Great catch! Our functions return "Invalid unit" but we should make sure this gets properly handled
and displayed to the user. Let's modify the main function slightly:
Oh wait, I accidentally used includes() which works on arrays. Since we're not supposed to use arrays yet,
let's rewrite that part using multiple conditionals:
if (fromUnitLower !== 'celsius' && fromUnitLower !== 'fahrenheit' && fromUnitLower !==
'kelvin') {
return "Please enter a valid source unit (Celsius, Fahrenheit, or Kelvin)";
}
if (toUnitLower !== 'celsius' && toUnitLower !== 'fahrenheit' && toUnitLower !==
'kelvin') {
return "Please enter a valid target unit (Celsius, Fahrenheit, or Kelvin)";
}
User: That's much better! Let's test it with some examples. If I convert 0°C to Fahrenheit, I should get 32°F,
right?
User: Great! And if I try to convert -300°C to Kelvin, it should give me an error because that would be a
negative Kelvin value, right?
Yes, exactly! You'd get an error message because absolute zero is approximately -273.15°C, so you can't go
lower than that.
User: Perfect! Now let's create a simple interface to use our converter:
function processConversion() {
const value = parseFloat(prompt("Enter temperature value:"));
const fromUnit = prompt("Enter source unit (Celsius, Fahrenheit, or Kelvin):");
const toUnit = prompt("Enter target unit (Celsius, Fahrenheit, or Kelvin):");
Expert: That looks good, but let's make a small adjustment. When we display the result, we should format it
to a reasonable number of decimal places, and we're already doing that with toFixed(2) . However, if the
result is an error message (string), toFixed would cause an error.
Let's also add a loop so users can perform multiple conversions without reloading the page:
function processConversion() {
let continueConverting = true;
while (continueConverting) {
const value = parseFloat(prompt("Enter temperature value:"));
const fromUnit = prompt("Enter source unit (Celsius, Fahrenheit, or Kelvin):");
const toUnit = prompt("Enter target unit (Celsius, Fahrenheit, or Kelvin):");
User: Hmm, I'm not sure if typeof result === 'number' will work correctly because both our functions
return either a number or a string. I'm thinking we should modify our functions to make this clearer.
Expert: You've got a sharp eye! You're absolutely right - we need to ensure our type checking works
correctly. Let's modify our approach.
One way to handle this is to check if the result is a string that starts with an error message. But a cleaner
approach is to add error checking directly in the convertTemperature function and ensure it always returns
a clear result object.
while (continueConverting) {
const value = parseFloat(prompt("Enter temperature value:"));
const fromUnit = prompt("Enter source unit (Celsius, Fahrenheit, or Kelvin):");
const toUnit = prompt("Enter target unit (Celsius, Fahrenheit, or Kelvin):");
This should work as long as our convertTemperature function returns a string for errors and a number for
valid conversions.
Enhancements
User: This works great! But I was thinking, what if we added a feature to round the result to a specific
number of decimal places? The user could specify how precise they want the result to be.
function processConversion() {
let continueConverting = true;
while (continueConverting) {
const value = parseFloat(prompt("Enter temperature value:"));
const fromUnit = prompt("Enter source unit (Celsius, Fahrenheit, or Kelvin):");
const toUnit = prompt("Enter target unit (Celsius, Fahrenheit, or Kelvin):");
// Validate precision
if (isNaN(precision) || precision < 1 || precision > 10) {
alert("Invalid precision. Using default precision of 2 decimal places.");
precision = 2;
}
const result = convertTemperature(value, fromUnit, toUnit);
User: That's perfect! Another idea: what if we allowed users to enter temperatures with the unit symbol?
Like "32F" or "100C" instead of having to select the unit separately?
Expert: That's an excellent usability enhancement! Let's implement a function to parse input that might
contain unit symbols:
function parseTemperatureInput(input) {
// Remove any spaces
input = input.trim();
function processConversion() {
let continueConverting = true;
while (continueConverting) {
// Ask for temperature with optional unit
const input = prompt("Enter temperature (e.g., 32F, 100C, 300K):");
// Validate precision
if (isNaN(precision) || precision < 1 || precision > 10) {
alert("Invalid precision. Using default precision of 2 decimal places.");
precision = 2;
}
However, there's an issue with this approach - parseFloat("32F") will return 32 but we need to handle
cases like "100°C" where the degree symbol is in the middle. Let's improve our parsing logic:
function processConversion() {
let continueConverting = true;
while (continueConverting) {
// Ask for temperature with optional unit
const input = prompt("Enter temperature (e.g., 32F, 100C, 300K):");
value = parseFloat(numericPart);
// Determine unit
if (unitPart.startsWith('C') || unitPart.startsWith('c') ||
unitPart.includes('°C' ) || unitPart.includes('°c' )) {
fromUnit = 'celsius';
} else if (unitPart.startsWith('F') || unitPart.startsWith('f') ||
unitPart.includes('°F' ) || unitPart.includes('°f' )) {
fromUnit = 'fahrenheit';
} else if (unitPart.startsWith('K') || unitPart.startsWith('k') ||
unitPart.includes('°K' ) || unitPart.includes('°k' )) {
fromUnit = 'kelvin';
} else {
// No unit specified, ask separately
fromUnit = prompt("Enter source unit (Celsius, Fahrenheit, or Kelvin):");
}
// Validate precision
if (isNaN(precision) || precision < 1 || precision > 10) {
alert("Invalid precision. Using default precision of 2 decimal places.");
precision = 2;
}
Hmm, this is getting complex. Let's simplify by using a regular expression instead:
function processConversion() {
let continueConverting = true;
while (continueConverting) {
// Ask for temperature with optional unit
const input = prompt("Enter temperature (e.g., 32F, 100C, 300K):");
// Basic parsing: assume the input starts with a number and ends with a unit
let i = 0;
let numericPart = '';
value = parseFloat(numericPart);
// Validate precision
if (isNaN(precision) || precision < 1 || precision > 10) {
alert("Invalid precision. Using default precision of 2 decimal places.");
precision = 2;
}
Expert: Your idea of parsing input with unit symbols is excellent! I've implemented a simplified version that
should work well.
Let's also add one more optimization: using arrow functions for our temperature conversion formulas to
make the code more concise:
Conclusion
Expert: We've built a comprehensive temperature converter that:
Tip: When designing any converter, think about using a "standard" unit as an intermediary. This
reduces the number of conversion functions you need to write and makes your code more
maintainable.
User: Thank you so much! I learned a lot about JavaScript functions, switch statements, loops, and
conditional logic. I especially liked how we structured the code to be modular and reusable.
Expert: You're welcome! The modular approach we took is a great practice in software development.
Breaking down problems into smaller, manageable parts not only makes the code easier to write but also
easier to understand and maintain.
Keep practicing these concepts, and you'll soon find yourself tackling even more complex problems with
confidence!