Ref/function arguments!

How to Code: The Fundamentals, pt 1

URL copied to clipboard
By AstroMacGuffin dated  last updated 
![Writing code is a lot like having cyber minions](/static/img/mm/cyberspace/onward-cyber-minionsSmall.jpg) As moderator and volunteer on the JavaScript Mastery discord, I've noticed a trend. The people who are in the most distress when they come for help, are the people who jumped head-first into a tutorial that was far too advanced for their skill level. They don't even know the fundamentals of JavaScript and they're trying to build a TikTok clone or a medical-grade messaging app. Some of them don't even want to learn, and are eager to say so. For the rest of you: welcome back to basics. There's a lot of fundamental knowledge you may have missed. If you're not really sure what variables are; if you have no idea how to create a function; and if you've never heard of the terms "control flow" or "scope" before, everything you do will make more sense after you grok this information. If someone (such as myself) sends you this link, congratulations! That person thinks you'll be a better programmer with just a little bit of high-density reading!

People who don't write code might think it's magic, but it's just language, and logic. Anyone can pick it up, because our species excels at both language and logic. So let's do this! If you feel like I skipped some crucial information, maybe try [part 0 of "How to Code: Fundamentals"](https://astromacguffin.com/ref/id/62ee85c9ae620ee833fd88b7). ### This Lesson First you'll learn about control flow and scope. Then you'll learn about variables, a little bit about data types, and then functions. Control flow is a programming concept that means you are in control of which instructions get done in what order. In this lesson we'll mostly be focused on the simplest control flow scheme: line 1 happens, then line 2 happens, then line 3, etc. until the program is complete. I'll use the `if` statement to demonstrate a common control flow feature of programming. Variables are nametags for pieces of data. There are many types of data. Variables are not "typed" in JavaScript, meaning a variable can point to data of one type one moment, and another type of data the next. Functions are nametags for code, but they're also more. Functions take input and send output, and can be any level of complexity, making them entire mini-programs within your program. ### Control Flow "Control flow" refers to things you can do in programming that control which instructions get sent to the CPU in what order. Take your simplest code sample, a traditional Hello World in browser-side JavaScript: ```js const text = 'Hello World'; alert(text); ``` It has two lines of code: 1. The first line of code creates a `const` -- a "constant", which is a type of variable. The `const` named `text` will hold the text, "Hello World". 2. The second line of code calls a function that's built into JavaScript, `alert`. It passes input to that function by placing the variable name `text` between the parentheses. `alert` is a well-known function that pops up a simple message box on the web browser. What should the message say? That's the input you provide when you call the `alert` function. More on functions later. The upshot for now is that, when you run this code, a pop-up, saying "Hello World", should appear. But how does the `alert` function know anything about a variable named `text`? That's thanks to control flow, and scope, a second feature under your control, which will be explained soon. Suffice to say for now: control flow goes from top to bottom, line by line. Since the definition of `text` came before the call to `alert`, `text` existed when `alert` was called. To explain scope better I'll introduce the `if` statement. This code contains a common mistake: ```js let color = 'blue'; if (color === 'blue') { const text = 'Hello World'; } alert(text); ``` What's happening here? First, let me explain *operators*. A single `=` equals sign is known as the "assignment operator". It assigns a value on the right hand side, to a variable on the left. So on the first line of code above, a variable called `color` is being *assigned* the value "blue". But on the next line of code, we have three equals signs `===` back to back. This is one of the many *comparison operators*. The `===` operator means "is exactly equal to". And of course, on that second line of code above, we've met our first `if` statement. If the conditions in the parentheses evaluate to `true`, then the code in the curly braces will be executed. This is a control flow feature. If the code above was run with no modifications, the following line of code would always be executed because `color` will always be exactly equal to "blue" (since we just defined it as such in the line above): ```js const text = 'Hello World'; ``` But since that's all the code in the curly braces after the `if` conditional... what happens next? After any control flow structure in your code has met its end, control flow always resumes its previous state, meaning in this case, we go back to doing things line-by-line, top to bottom... ### Scope ...Which would be great if not for the bug. It's a very common bug: a variable is being used "out of scope". ![robo-mouse-clickSmall.jpg](/static/img/mm/cyberspace/robo-mouse-clickSmall.jpg) Let's look at the whole thing again: ```js let color = 'blue'; if (color === 'blue') { const text = 'Hello World'; } alert(text); ``` If you ran it in a browser, no pop-up box would appear, and you'd be told `Uncaught ReferenceError: text is not defined` in the Developer Tools console. (To open the console, right click almost anywhere in the page, and choose **Inspect** -- e.g. **Inspect element**.) So why isn't `text` defined? It's because it fell out of scope. Basically, each set of curly braces creates a context one level "inner" compared to the context of where you typed those new curly braces. Using our example, the `const text = 'Hello World';` line is in a context one level "inner" vs everything outside those curly braces. **Each of these contexts has its own scope.** Variables from "inner" levels of scope can't be used in "outer" levels of scope. Variables from "outer" levels of scope **can** be used in the "inner" scope code. Just remember, in any given situation, the so-called "outer scope" is often an "inner scope" compared to the overall global scope, that is, the outermost part of the control flow. ***The variable will fall out of scope when the block that created it, ends.*** So long as that doesn't happen before control flow reaches the part of the code where you want to use the variable -- the variable will be available. In the example code, the `if` block ended with the closing curly brace `}` and so the `const text` no longer existed when `alert` was called. That's scope. ### Variables & Data Types Variables (aka vars) are nametags for data. This alone is a powerful feature. Combining variables with other variables, and combining variables with functions, represents almost 100% of programming, so this section is critical. Someone recently asked in the JavaScript Mastery discord why variables were important, so here's a bullet list of reasons off-the-cuff: - Configuration (the easiest config system for any piece of software is a list of config vars at the top) - Software Speed (when the same value is referenced multiple times, a variable is faster) - Function Arguments (without which functions would be far less powerful) - Readability (even if you're the sole maintainer of a program, you'll thank yourself later if your code is readable) - Debugging Steps (easier to find the mistake in your code if that mistake leaves a named imprint you can output) First thing you need to know is the three types of variable, identified by the keyword used to declare them: `const`, `let`, and `var`. - `const` - The variable must be defined. Can't be changed. Normal scope rules apply. - `let ` - The variable need not be defined. Can be changed. Normal scope rules apply. - `var ` - The variable need not be defined. Can be changed. Special scope rules apply. #### Declaring & Defining Variables In JavaScript, as with most languages, you have to *declare* a variable before you can use it. You declare a variable by issuing one of the above three keywords, plus the variable's name. ```js let foo; var bar; ``` You can go one step further in the same line, *defining* the variable by using the assignment operator and some value on the right of that. ```js let foo = 2; var bar = 'The quick brown fox jumped over the lazy dog.'; ``` Variables made with `const` must always be declared and defined at the same time. ```js const foobar = 'Two quick brown foxes jumped over three lazy dogs.'; ``` #### Why `var` is Useless So what are these "special scope rules" attached to `var`? I'll borrow the example from [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var): ```js var x = 1; if (x === 1) { var x = 2; console.log(x); // expected output: 2 } console.log(x); // expected output: 2 ``` The special scope rule is that `var` does nothing special. You still have to declare the variable in the outside scope, and even the second `var` in the `if` block (`var x = 2;`) offers no special features. Compare the above, to the below: ```js let x = 1; if (x === 1) { x = 2; console.log(x); // expected output: 2 } console.log(x); // expected output: 2 ``` They're practically identical (the second example is shorter by one keyword) and they get the same result. For this reason, JavaScript coders tend to drift toward *never* using `var`. Just remember: `const` for constants that can't be changed, `let` for everything else. In all cases, you need to remember to declare your variables in proper scope, so that the variable will be available when it's needed, and released from memory when it's not needed anymore. ### Data Types Variables in JavaScript both do, and don't, have a "type". Every piece of data has a "data type", and yet, variables are not "strongly typed" in JavaScript -- meaning any variable can point to any type of data -- no variable is locked into the type of data it was first assigned. Still, data types are important; eventually, you will find yourself often querying the data type of a variable as part of an `if` or `switch` statement, to make more powerful functions. Long story short, we need a quick word on data types. For a beginner, here's what you need to know about types. There are two categories. Category 1: "primitives" - Data type `boolean` stores either `true` or `false` - Data type `string` stores text of whatever length - Data type `number` will be reported for every number you'll ever use - `null` and `undefined` data types, explained in the next lesson Category 2: "objects" - variables created using the `let obj = new className()` syntax - variables created using the `let obj = { field: 'value', property: 1 }` syntax - arrays, regular expressions, etc Why care? Because category 2, the objects, are always handled "by reference". This means if you assign it to another variable, both variables point to the same object, as shown here: ```js let foo = { field: 'value', property: 1 }; // foo.property is currently 1 const bar = foo; bar.property = 2; console.log(foo.property); // output is 2 ``` Only the object types do this. ![green_computer-gutsSmall.jpg](/static/img/mm/cyberspace/green_computer-gutsSmall.jpg) ### Functions Functions give nametags to code, but also much more. Functions can take input, and can send output, like little mini-programs. Most of the best functions are small, single-task functions. Let's take a look at a simple function that has both input and output: ```js function add(a, b) { return a + b; } ``` The function is called `add` and it adds two numbers, that are given to it when it's called (the inputs). It adds the two numbers, returning (outputting) the result. Let's break this down. Obviously `function add` creates a function called *add*. The `(a, b)` is where things get interesting. This is called the function's "signature". This is how you define the inputs for the function. In this example, we're defining two variables, `a` and `b`, that will be available inside the function. The values of `a` and `b` depend on whoever writes code "calling" this function. (You "call" a function by typing its name with a set of parentheses after it.) For example, if someone writes: ```js let c = add(6, 18); ``` ...then, inside the function, `a` will equal 6 and `b` will equal 18. Notice that the person calling the function used the same number of inputs as the signature specified. And the last thing to understand about the function definition for `add`, is the line starting with `return`. The `return` statement is a function's output. If the function is used thusly: `let c = add(6, 18);` ...you might notice the `let c =` part and wonder, how does it know what `c` should equal? It knows because of the `return` statement inside the function. This example, our `add` function, doesn't even create a variable to return -- it just returns the direct result of a math operation. Outputting values of whatever complexity in this way is fine. ![earth-hexgrid.jpg](/static/img/mm/cyberspace/earth-hexgrid.jpg) Let's look at a slightly more complex example. Let's say you have a simple project for logging and storing log entries. ```js const ERR_LOGSTOR = 'Invalid storage medium'; // an error message const logStorage = []; // an array where we store our logs // function signature with a default value function storeLog(logMessage='Message not specified') { if (Array.isArray(logStorage)) { logStorage.push(logMessage); return 0; } else { return ERR_LOGSTOR; } function logMessage(logMessage=undefined) { if (logMessage === undefined) { console.log('An undefined message was not logged'); } else { console.log(logMessage); const status = storeLog(logMessage); if (status === ERR_LOGSTOR) { console.warn('Log storage error: ' + status); } } } logMessage('System started'); ``` Let's break that down: ```js const ERR_LOGSTOR = 'Invalid storage medium'; // an error message ``` Whenever you see two slashes `//` it means that the rest of that line is a "comment". This is how programmers put documentation into the code itself. Code can also be "commented out" by putting two slashes at the beginning of the line. Code that's been "commented out" is completely ignored when you run the program. ```js const logStorage = []; // an array where we store our logs ``` Two square brackets `[]` means "create a new, empty array". So when we begin this program, `logStorage` will be assigned an empty array. ```js function storeLog(logMessage='Message not specified') { ``` This is the function declaration and the beginning of its definition. The signature is one value this time, but the value has a default. Since the value has a default, you can skip that part of the signature when calling the function: it could be `storeLog('I am a log message')` or it could be `storeLog()` with nothing between the parentheses. If nothing is between the parentheses, the function will proceed as if you had typed `storeLog('Message not specified')`. ```js if (Array.isArray(logStorage)) { logStorage.push(logMessage); return 0; } else { return ERR_LOGSTOR; } ``` In the above snippet, first we do a sanity check -- meaning we check the code's situation to avoid producing an error. In this case, that means making sure the `logStorage` variable is an Array. If it is, we add the `logMessage` to the end of the `logStorage` array and output a 0 as our function's return value. If `logStorage` is not an Array, we do nothing except return the error message defined at the top of the script. A quick additional note about `return`: using `return` also exits control flow from the function, in addition to sending output. Notice you can have multiple `return` statements in a function. Control flow will only arrive at one of them, but you can manage this with features like the `if` statement. ```js } ``` This ends the function. Every code block begins with `{` and ends with `}`. This applies to control flow structures such as the `if` statement, as well as functions, classes, etc. ```js function logMessage(logMessage=undefined) { ``` Once again we have a function declaration with a single parameter as its signature, and once again the parameter has a default value. ```js if (logMessage === undefined) { console.log('An undefined message was not logged'); } ``` Here we're doing some error trapping: we don't want to log nothing, but we do want to log the fact that nothing was logged. (Ah, business logic.) ```js else { console.log(logMessage); ``` Whenever you use `if`, you can use `else` right after the `if` block ends. The `if` block executes if the condition in parentheses is true; the `else` block runs otherwise. ```js const status = storeLog(logMessage); ``` Here we're using the previous function, `storeLog`. Yes, you can use functions within functions. If doing task A means also doing task B, you'll probably find yourself calling one function from within another. ```js if (status === ERR_LOGSTOR) { console.warn('Log storage error: ' + status); } ``` When we ran the `storeLog` function we also grabbed its return value. Now we're checking it against our known error messages, and if there was a match, we're warning the console operator there was a problem. ```js } } ``` Here we're closing the `else` case and closing the function. ```js logMessage('System started'); ``` Finally we set off the whole chain of events by calling `logMessage` to log a message that the system has started. Move on when you're ready to [part 2 of "How to Code: The Fundamentals"](https://astromacguffin.com/ref/id/62ec1c6a209885383e63d857).

Valid HTML!Valid CSS!Powered by Node.js!Powered by Express.js!Powered by MongoDB!