/ 

Ref/static function!

How to Code: The Fundamentals, pt 2

URL copied to clipboard
By AstroMacGuffin dated  last updated 
![Blue electronic nodes comprising a field of nodes](/static/img/mm/cyberspace/daffodill-nodesSmall.jpg) If you finished part 1 of this series and thought to yourself, "that wasn't enough for one lesson, I need more immediately!", then you're an odd one, because that was a lot of information. But here we go again! After a quick roundup of smaller topics we're going into objects, classes, and async/await (aka asynchronous code)! ### This Lesson First a round-up of important topics that don't fit elsewhere: `null` and `undefined` as both data types and values; and Garbage Collection aka GC -- JavaScript's technique for memory management. Then I explain everything a beginner needs to know about asynchronous code aka `async`/`await`. Then we jump headfirst into objects and classes. Until recently, programming meant writing lines of code in full confidence that, when you run that code, line 1 would finish before line 2 would begin. And, today, it's still mostly the same. But then a rookie slams headfirst into an asynchronous function. Today we're going to treat `async`/`await` as a fundamental. It's also time to cover objects and classes. Object Oriented Programming is probably the single most powerful tool in rapid programming ever. You'll learn how to treat code like layers in an image, stacking them on top of each other to create an end result that can save a professional programmer hours per task.


### `null` and `undefined` Data Types In programming there are multiple ways to say "nothing". The `null` and `undefined` data types correspond to the built-in `null` and `undefined` values. The value `null` is nothing special... it's just "nothing". Meanwhile `undefined` can mean the variable has been created, but hasn't yet been assigned a value. (Or, it can also mean someone deliberately set a variable to equal `undefined`.) Both `null` and `undefined` evaluate to `false`. As a matter of style, I tend to set my variables to `undefined` when I need to signify they're in a "reset" state; and I tend to return `null` (or just `false` itself) from functions if some criteria wasn't met. You can use the `typeof` operator to see `if (typeof someVariable === 'undefined')` but it's quicker to check the value: `if (someVariable === undefined)` loses both the `typeof` operator, and the quotes on `undefined`. ### Garbage Collection Memory management is hard, and JavaScript is neither the best nor worst at it. Its primary means of keeping a trim footprint is called "garbage collection" or GC. The GC system relies on you, the programmer, to keep a clean house in your code. Data disappears from your program, freeing up memory, when there are no more variables pointing to it. Remember from part 1 of this series: any data type that reports itself as an object (or an Array) gets variables that all point to a single instance of the data. It takes special techniques to ***copy*** an object or Array. If your code says `const whatever = someObject;` you now have one copy of someObject, referenced from two different variable names. This matters because, someday, you'll be building indexes of data. You'll need to be able to clear individual entries from those indexes, to keep your programs memory-efficient. ### Async/await - Asynchronous Functions Our most basic control flow pattern -- top line to bottom line, one line at a time -- is no longer guaranteed, thanks to modern programming features. Programs can now run functions asynchronously, which means functions can activate and do whatever, ***but your outer scope code doesn't wait for the function to finish before continuing with the next line of code.*** As a beginner, you need to understand this fundamental side of the language: the asynchronous side. Because, someday soon, you will do something like: ```js let someVariable = someFunction( options ); ``` ...and everything will seem fine, but you won't receive any data in `someVariable`... or if you do have any data in someVariable, it's some object called a Promise. In this situation you should check the declaration of `someFunction` to see if it is `async`. An `async function`'s declaration looks like one of these: ```js async function someFunction( options ) { // code } // in the following forms, let or var may be used in place of const const someFunction = async ( options ) => { // code } // or const someFunction = async function( options ) { // code } // object classes have methods, which don't use the "function" keyword, but they're totally functions async someMethod( options ) { // code } // another way of declaring an object method someMethod: async function ( options ) { // code } // yet another way to declare an object method someObject.someMethod = async ( options ) => { // code }} // ...or... someObject.someMethod = async function( options ) { // code }} // etc... yes, there are still other ways... ``` #### Solution 1: `await` the `async` If it's `async`, you should adjust the code where you call that function. Instead of ```js let someVariable = someFunction( options ); ``` ...you add `await` before the function call, e.g.... ```js let someVariable = await someFunction( options ); ``` **This won't solve every situation.** But in many cases, a quick `console.log()` will confirm you now have the data you expected, instead of a Promise. If it doesn't solve your situation, then you probably need to know about "Thenables" and Callbacks. #### Solution 2: Callbacks: Functions as Function Input A callback is a function you pass into another function as input. I don't mean like this: ```js y.setHeight( x.getHeight() ); ``` That's passing the function's *output* as the input of another function. I'm talking about passing the function *itself* as input. Remember, everything is a variable, even a function: ```js y.setHeight( x.getHeight ); ``` What's the difference? No parentheses on the function being inputted as a callback. Why do this? Storing the function gives you the ability to re-run the callback, giving you a fresh return value based on up-to-the-picosecond data. There's also the anonymous callback. An anonymous function or anonymous callback is a nameless function made up on-the-fly. So yes, you can actually *write a function in the middle of calling another function.* That is a very common practice actually, and would look like this: ```js aFunctionThatExpectsACallback( () => { // code } // end of callback ); // end of call to aFunctionThatExpectsACallback ``` Many asynchronous functions don't `await` properly and their programmers worked around this by accepting a callback. The callback is run when the job is done. #### Solution 3: "Thenables" & Promises Thenables are objects that have a `.then()` method which takes a callback as an argument. The callback happens when the Thenable finishes its other tasks. The standard JavaScript "Promise" object is a Thenable. So here is another way to force your code to wait until the Promise has turned into a value: ```js // get the promise let foo = await anAsyncFunction(); // now we must wait for the promise to resolve: // method 1: declared function as callback function someHandlerFunctionForWhenItsDone(data) { // do stuff with data } foo.then( someHandlerFunctionForWhenItsDone ); // method 2: anonymous function as callback foo.then( (data) => { // do something with the "data" variable }); ``` As shown in "method 2" just above, the callback will usually have at least one parameter, which is your output data, automatically populated into the callback's input when the task is done. The spec for any given callback will also often have an error object as an input parameter; again, this is auto-filled when your callback starts to run. #### In Conclusion (For Now) regarding Asynchronous Code Asynchronous features are a good thing. They make interfaces faster. But as a beginner, they can seem like voodoo and a curse. Just remember there's always a way to get the result you want; the three techniques above will get you through basically all situations. ![Cyber Medusa](/static/img/mm/cyberspace/cyber-medusaSmall.jpg) ### Objects If you're one of those students who started with hands-on tutorials before seeking to learn the basics, then you've probably used objects before. Anytime you see the dot notation such as `Array.isArray()` or `someString.split()` or `Object.keys()` that's either an object, or a class. Sharp-eyed students may have noticed I included strings as objects! But in part 1, strings were one of the data types ***not*** counted as an object! On a certain level, everything is an object in JavaScript; the two categories of data types from lesson 1, the "primitives" vs "objects", still stands as the line between values being copied vs objects being referenced. Try not to think about it too hard. Choose any real-world object in your line of sight. It has properties: it has at least one color, at least one shape, is it shiny or dull, is it powered, is it rechargeable, and so on. In programming, that's your most basic object. An object is a variable type. When used at this simplistic level, its equivalents in some other languages include the dictionary, or the hash, or the associative array. The most basic JavaScript object is just a variable that holds sub-variables. You can define one very easily on the fly, almost anywhere in your code: ```js const ball = { color: 'red', bounciness: 1.0, size: 'large', }; ``` Notice this is basically identical to JSON, the **J**ava**S**cript **O**bject **N**otation language. This kind of object is very useful, but not very powerful. What if it could handle its own bounce command? Of course, it can: ```js const ball = { color: 'red', bounciness: 1.0, size: 'large', isBouncing: false, bounce: (milliseconds) => { // bounce for a number of milliseconds console.log('The ball starts bouncing.'); ball.isBouncing = true; setTimeout( (obj) => { console.log('The ball stops bouncing.'); obj.isBouncing = false; }, milliseconds, ball); }, }; // Set it off ball.bounce(1000); // Monitor status console.log(ball.isBouncing); setTimeout( () => { console.log(ball.isBouncing); }, 1500); ``` But what if you need your ball code to be reusable? You create a **class**. Then your code can say something like this: ```js const zipper = new Ball( { color: 'red', bounciness: 1.0, size: 'large' } ); zipper.bounce(1000); ``` ### Object Classes A class is like a second data type, for objects. Writing class-based code unlocks new features in object-oriented programming, such as using `this` to mean "this instance of the class as an object". Making object classes is easy: ```js class Ball { constructor(arg) { this.color = arg.color; this.bounciness = arg.bounciness; this.size = arg.size; } bounce(milliseconds) { // bounce for a number of milliseconds console.log('The ball starts bouncing.'); this.isBouncing = true; // "this" instead of "ball" setTimeout( (obj) => { console.log('The ball stops bouncing.'); obj.isBouncing = false; }, milliseconds, this); // "this" instead of "ball" } } const zipper = new Ball({color: 'red', bounciness: 1.0, size: 'large'}); zipper.bounce(1000); ``` Important but dumb thing about terminology: A function that resides inside an object or object class, is called a "method". Methods are functions. Go ahead, do a `console.log(typeof zipper.bounce)` and see if it doesn't confirm - methods are functions. Almost everything that's true about methods is true about functions, and virtually everything I'll ever say about functions also applies to methods. They're the same thing (so why couldn't they have the same name?) ... The *method* `constructor` runs whenever there's a `new Ball()`. The `constructor` method receives the arguments given to `new Ball()`. Here we have it configured for a single argument: ```js class Ball { constructor(arg) { ``` And when `new Ball()` is called, we create an object on the fly to serve as the single argument: ```js const zipper = new Ball( {color: 'red', bounciness: 1.0, size: 'large'} ); ``` #### Extending Classes Remember the analogy about putting code on top of code, like layers in an image? This is where you do that. Consider if we rework the `Ball` class like so: ```js class Toy { constructor(arg) { this.name = arg.name; this.color = arg.color; this.size = arg.size; this.brand = arg.brand; this.cost = arg.cost; this.owner = arg.owner; } } class Ball extends Toy { constructor(arg) { super(arg); // call the parent's constructor this.bounciness = arg.bounciness; } bounce(milliseconds) { // code } } ``` The `extends` keyword creates a parent/child relationship between two classes. The line `super(arg)` in the `constructor` for `Ball` refers to the `constructor` of `Toy`. In this relationship, `Toy` is known as the "parent class". The "child class" is `Ball`. The child class absorbs all the properties of the parent, including methods. But that's not all. The new child class is also linked to the parent of the parent class, and the whole ancestry, leading back to a class that doesn't extend anything. Every property and method from that family line, gets absorbed into the child class. Anytime the same method name appears in two or more classes in the family line, the child class overrides. The child class method can (and often should) call the parent class method, similarly to how the parent's constructor was called from the child's constructor: ```js class Toy { constructor() { } setColor(color) { this.color = color; console.log('The color ' + color + ' was saved.'); } } class Ball extends Toy { constructor() { super(); // this line is mandatory in child constructors } setColor(color) { super.setColor(color); console.log('The ball changed colors!'); } } ``` `super.setColor()` calls the `setColor` method on the parent class. It's almost always a good idea to put the input from the child method, into the input for the parent method, as I've done in the example: ```js setColor(color) { super.setColor(color); ``` ![What I imagine some students must feel like when they see there's still more to the lesson](/static/img/mm/cyberspace/cyber-decapitationSmall.jpg) Now that we've swept through the most powerful features of objects and classes, let's wind this lesson down with two more class features that are often overlooked by rookie programmers. Both of them deal with the keyword `static` and how it modifies properties and methods. #### Classes as Data Tables When you normally give properties to a class, you do so by defining the properties in the class constructor. (Yes, you might declare or even define normal class properties above the constructor, but sometimes you will have more complex initialization for a property than what is allowed in that format, and why divide up your initialization between two parts of the code?) This makes it so the property is associated with an *instance* of that class, aka an object of that class. You can't use those properties until you create a new instance of the object: ```js const zipper = new Ball( {color: 'red', bounciness: 1.0, size: 'large'} ); console.log(zipper.color); ``` But it's also possible to put data into a class in such a way that you don't need an object of that class to access those properties. These are called "static properties". Consider: ```js class Toy { static purpose = 'fun'; } console.log(Toy.purpose); ``` This is a great place to put configuration flags, error messages, and so forth. Don't forget that these properties can be objects, so you might wind up with: ```js class Toy { static ERR = { UBROKE: 'You broke it!', NOTFUN: "You don't find it fun anymore.", PENYWS: 'The toy fell into a sewer grate!' }; } // throw an error: we don't want to play with Pennywise throw( Toy.ERR.PENYWS ); ``` #### Classes as Function Libraries The same trick works on methods. Functions that seem logically grouped within a class's overall concept, are excellent candidates for static methods in that class. Perhaps we had already written a `throw()` function before creating the `Ball` class. ```js function throw(obj, thrower, target) { console.log(thrower + " throws the " + obj + " to " + target + "."); obj.owner = target; } ``` It might make more sense for this to become an object method, depending on your project. But perhaps we want the code to be capable of throwing non-ball objects *in the manner of a ball*. So we move `throw` to the `Ball` class with just one tiny change: ```js static throw(obj, thrower, target) { console.log(thrower + " throws the " + obj.name + " to " + target + "."); obj.owner = target; } ``` #### When to Use `static` and When Not To There's two simple guidelines about whether or not your method or property ought to be a static: - Needs an Object: if the method needs to read from, or write to, live object properties, then the method *probably* shouldn't be static. (However, don't forget the example above for `Ball.throw`, where the object was passed in as an argument, making the code more powerful because it can operate on anything.) Same for properties: if a property describes a specific object, it shouldn't be static. There is no exception for properties. Static properties should describe **all** objects of the class. You wouldn't make `size` a static property of a Ball class, but you might make `shape` a static property equal to "round" (if you wanted to troll American football fans). - Object More Convenient: Soon it will be common for you to encounter *objects full of objects* as you code. Normally each class goes in its own file called a module; to use a static class feature or make a new object of a class, you have to go back to the top of your file and `require` that class module. It's a minor hassle and totally avoidable if an object of that class is already conveniently available in your code's current context. In that case, common sense favors making the method or property *not* static. ### Lesson 2 Conclusion I know we're going through these topics at breakneck speed. Pace yourself through each lesson as you see fit. I think coding is exciting, and I want these lessons to be here for people who want to binge this education.
🔍

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