My brain is a bit fried from all the coding today. I’m having fun but I’m having trouble keeping my focus on my work.
Merlin is sleeping under my desk. He can barely fit under there but somehow he has managed to wedge his head under the back of my desk. He unplugged my speaker while down there, so I will need to plug that back in when he wakes up. One day he will be too big to fit his head under my desk.
Lexi is thinking of getting an on-campus apartment with her friends. After some questions I’m almost ok with it. It will be a learning experience. Though she had some of the learning experience during the summer while living in an apartment with three strangers. I’m happy that it is still on campus. And it looks pretty safe. There is a rent each month. I told her to ask the school about the rent, if it is out-of-pocket or if financial aid takes care of it. I have an inkling that it is out of pocket. But if she shares with three other people, they can split the rent. And all utilities and internet is paid for already. She is getting more info from the school, which I like. And I trust her.
In other news, I won a giveaway in a FB stationery group. I have never won a giveaway before. I have a new planner for next year: the Hobonichi Cousin. It’s nice. I like the layout and the fact that I don’t have to draw everything like I had to in my bullet journal. I have stickers and washi tape to decorate this thing. I hope I don’t get overwhelmed down the line and stop using it. Which sometimes happens with me. I like something, like a hobby, and then I get overwhelmed cause I hadn’t put any time aside for it.
JavaScript notes…
———————————–
As you have seen, behavior is shared through inheritance. However, there are cases when inheritance is not the best solution. Inheritance does not work well for unrelated objects like Bird and Airplane. They can both fly, but a Bird is not a type of Airplane and vice versa.
For unrelated objects, it’s better to use mixins. A mixin allows other objects to use a collection of functions.
let flyMixin = function(obj) { obj.fly = function() { console.log("Flying, wooosh!"); } };
The flyMixin takes any object and gives it the fly method.
let bird = { name: "Donald", numLegs: 2 }; let plane = { model: "777", numPassengers: 524 }; flyMixin(bird); flyMixin(plane);
Here bird and plane are passed into flyMixin, which then assigns the fly function to each object. Now bird and plane can both fly:
bird.fly(); plane.fly();
The console would display the string Flying, wooosh! twice, once for each .fly() call.
Note how the mixin allows for the same fly method to be reused by unrelated objects bird and plane.
Create a mixin named glideMixin that defines a method named glide. Then use the glideMixin to give both bird and boat the ability to glide.
let bird = { name: "Donald", numLegs: 2 }; let boat = { name: "Warrior", type: "race-boat" }; // Only change code below this line
let bird = { name: "Donald", numLegs: 2 }; let boat = { name: "Warrior", type: "race-boat" }; // Only change code below this line let glideMixin = function(obj) { obj.glide = function() { console.log("Weee!"); } }; glideMixin(bird); glideMixin(boat); bird.glide(); boat.glide();
In the previous challenge, bird had a public property name. It is considered public because it can be accessed and changed outside of bird’s definition.
bird.name = "Duffy";
Therefore, any part of your code can easily change the name of bird to any value. Think about things like passwords and bank accounts being easily changeable by any part of your codebase. That could cause a lot of issues.
The simplest way to make this public property private is by creating a variable within the constructor function. This changes the scope of that variable to be within the constructor function versus available globally. This way, the variable can only be accessed and changed by methods also within the constructor function.
function Bird() { let hatchedEgg = 10; this.getHatchedEggCount = function() { return hatchedEgg; }; } let ducky = new Bird(); ducky.getHatchedEggCount();
Here getHatchedEggCount is a privileged method, because it has access to the private variable hatchedEgg. This is possible because hatchedEgg is declared in the same context as getHatchedEggCount. In JavaScript, a function always has access to the context in which it was created. This is called closure.
Change how weight is declared in the Bird function so it is a private variable. Then, create a method getWeight that returns the value of weight 15.
function Bird() { this.weight = 15; }
function Bird() { let weight = 15; this.getWeight = function() { return weight; } } let Merlin = new Bird(); Merlin.getWeight();
A common pattern in JavaScript is to execute a function as soon as it is declared:
(function () { console.log("Chirp, chirp!"); })();
This is an anonymous function expression that executes right away, and outputs Chirp, chirp! immediately.
Note that the function has no name and is not stored in a variable. The two parentheses () at the end of the function expression cause it to be immediately executed or invoked. This pattern is known as an immediately invoked function expression or IIFE.
Rewrite the function makeNest and remove its call so instead it’s an anonymous immediately invoked function expression (IIFE).
function makeNest() { console.log("A cozy nest is ready"); } makeNest();
(function () { console.log("A cozy nest is ready"); })();
An immediately invoked function expression (IIFE) is often used to group related functionality into a single object or module. For example, an earlier challenge defined two mixins:
function glideMixin(obj) { obj.glide = function() { console.log("Gliding on the water"); }; } function flyMixin(obj) { obj.fly = function() { console.log("Flying, wooosh!"); }; }
We can group these mixins into a module as follows:
let motionModule = (function () { return { glideMixin: function(obj) { obj.glide = function() { console.log("Gliding on the water"); }; }, flyMixin: function(obj) { obj.fly = function() { console.log("Flying, wooosh!"); }; } } })();
Note that you have an immediately invoked function expression (IIFE) that returns an object motionModule. This returned object contains all of the mixin behaviors as properties of the object. The advantage of the module pattern is that all of the motion behaviors can be packaged into a single object that can then be used by other parts of your code. Here is an example using it:
motionModule.glideMixin(duck); duck.glide();
Create a module named funModule to wrap the two mixins isCuteMixin and singMixin. funModule should return an object.
let isCuteMixin = function(obj) { obj.isCute = function() { return true; }; }; let singMixin = function(obj) { obj.sing = function() { console.log("Singing to an awesome tune"); }; };
let isCuteMixin = function(obj) { obj.isCute = function() { return true; }; } let singMixin = function(obj) { obj.sing = function() { console.log("Singing to an awesome tune"); }; }; let funModule = (function () { return { isCuteMixin: function(obj) { obj.cute = function() { console.log("This is cute"); return true; }; }, singMixin: function(obj) { obj.sing = function() { console.log("Singing to an awesome tune"); return true; }; } } }());
Functional programming is a style of programming where solutions are simple, isolated functions, without any side effects outside of the function scope: INPUT -> PROCESS -> OUTPUT
Functional programming is about:
Isolated functions – there is no dependence on the state of the program, which includes global variables that are subject to change
Pure functions – the same input always gives the same output
Functions with limited side effects – any changes, or mutations, to the state of the program outside the function are carefully controlled
The members of freeCodeCamp happen to love tea.
In the code editor, the prepareTea and getTea functions are already defined for you. Call the getTea function to get 40 cups of tea for the team, and store them in the tea4TeamFCC variable.
// Function that returns a string representing a cup of green tea const prepareTea = () => 'greenTea'; /* Given a function (representing the tea type) and number of cups needed, the following function returns an array of strings (each representing a cup of a specific type of tea). */ const getTea = (numOfCups) => { const teaCups = []; for(let cups = 1; cups <= numOfCups; cups += 1) { const teaCup = prepareTea(); teaCups.push(teaCup); } return teaCups; }; // Only change code below this line const tea4TeamFCC = null; // Only change code above this line
// Function that returns a string representing a cup of green tea const prepareTea = () => 'greenTea'; /* Given a function (representing the tea type) and number of cups needed, the following function returns an array of strings (each representing a cup of a specific type of tea). */ const getTea = (numOfCups) => { const teaCups = []; for(let cups = 1; cups <= numOfCups; cups += 1) { const teaCup = prepareTea(); teaCups.push(teaCup); } return teaCups; }; // Only change code below this line const tea4TeamFCC = getTea(40); // Only change code above this line
The FCC Team had a mood swing and now wants two types of tea: green tea and black tea. General Fact: Client mood swings are pretty common.
With that information, we'll need to revisit the getTea function from last challenge to handle various tea requests. We can modify getTea to accept a function as a parameter to be able to change the type of tea it prepares. This makes getTea more flexible, and gives the programmer more control when client requests change.
But first, let's cover some functional terminology:
Callbacks are the functions that are slipped or passed into another function to decide the invocation of that function. You may have seen them passed to other methods, for example in filter, the callback function tells JavaScript the criteria for how to filter an array.
Functions that can be assigned to a variable, passed into another function, or returned from another function just like any other normal value, are called first class functions. In JavaScript, all functions are first class functions.
The functions that take a function as an argument, or return a function as a return value, are called higher order functions.
When functions are passed in to or returned from another function, then those functions which were passed in or returned can be called a lambda.
Prepare 27 cups of green tea and 13 cups of black tea and store them in tea4GreenTeamFCC and tea4BlackTeamFCC variables, respectively. Note that the getTea function has been modified so it now takes a function as the first argument.
Note: The data (the number of cups of tea) is supplied as the last argument. We'll discuss this more in later lessons.
// Function that returns a string representing a cup of green tea const prepareGreenTea = () => 'greenTea'; // Function that returns a string representing a cup of black tea const prepareBlackTea = () => 'blackTea'; /* Given a function (representing the tea type) and number of cups needed, the following function returns an array of strings (each representing a cup of a specific type of tea). */ const getTea = (prepareTea, numOfCups) => { const teaCups = []; for(let cups = 1; cups <= numOfCups; cups += 1) { const teaCup = prepareTea(); teaCups.push(teaCup); } return teaCups; }; // Only change code below this line const tea4GreenTeamFCC = null; const tea4BlackTeamFCC = null; // Only change code above this line console.log( tea4GreenTeamFCC, tea4BlackTeamFCC );
// Function that returns a string representing a cup of green tea const prepareGreenTea = () => 'greenTea'; // Function that returns a string representing a cup of black tea const prepareBlackTea = () => 'blackTea'; /* Given a function (representing the tea type) and number of cups needed, the following function returns an array of strings (each representing a cup of a specific type of tea). */ const getTea = (prepareTea, numOfCups) => { const teaCups = []; for(let cups = 1; cups <= numOfCups; cups += 1) { const teaCup = prepareTea(); teaCups.push(teaCup); } return teaCups; }; // Only change code below this line const tea4GreenTeamFCC = getTea(prepareGreenTea, 27); const tea4BlackTeamFCC = getTea(prepareBlackTea, 13); // Only change code above this line console.log( tea4GreenTeamFCC, tea4BlackTeamFCC );
Functional programming is a good habit. It keeps your code easy to manage, and saves you from sneaky bugs. But before we get there, let's look at an imperative approach to programming to highlight where you may have issues.
In English (and many other languages), the imperative tense is used to give commands. Similarly, an imperative style in programming is one that gives the computer a set of statements to perform a task.
Often the statements change the state of the program, like updating global variables. A classic example is writing a for loop that gives exact directions to iterate over the indices of an array.
In contrast, functional programming is a form of declarative programming. You tell the computer what you want done by calling a method or function.
JavaScript offers many predefined methods that handle common tasks so you don't need to write out how the computer should perform them. For example, instead of using the for loop mentioned above, you could call the map method which handles the details of iterating over an array. This helps to avoid semantic errors, like the "Off By One Errors" that were covered in the Debugging section.
Consider the scenario: you are browsing the web in your browser, and want to track the tabs you have opened. Let's try to model this using some simple object-oriented code.
A Window object is made up of tabs, and you usually have more than one Window open. The titles of each open site in each Window object is held in an array. After working in the browser (opening new tabs, merging windows, and closing tabs), you want to print the tabs that are still open. Closed tabs are removed from the array and new tabs (for simplicity) get added to the end of it.
The code editor shows an implementation of this functionality with functions for tabOpen(), tabClose(), and join(). The array tabs is part of the Window object that stores the name of the open pages.
Examine the code in the editor. It's using a method that has side effects in the program, causing incorrect behaviour. The final list of open tabs, stored in finalTabs.tabs, should be ['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium', 'new tab', 'Netflix', 'YouTube', 'Vine', 'GMail', 'Work mail', 'Docs', 'freeCodeCamp', 'new tab'] but the list produced by the code is slightly different.
Change Window.prototype.tabClose so that it removes the correct tab.
// tabs is an array of titles of each site open within the window const Window = function(tabs) { this.tabs = tabs; // We keep a record of the array inside the object }; // When you join two windows into one window Window.prototype.join = function(otherWindow) { this.tabs = this.tabs.concat(otherWindow.tabs); return this; }; // When you open a new tab at the end Window.prototype.tabOpen = function(tab) { this.tabs.push('new tab'); // Let's open a new tab for now return this; }; // When you close a tab Window.prototype.tabClose = function(index) { // Only change code below this line const tabsBeforeIndex = this.tabs.splice(0, index); // Get the tabs before the tab const tabsAfterIndex = this.tabs.splice(index + 1); // Get the tabs after the tab this.tabs = tabsBeforeIndex.concat(tabsAfterIndex); // Join them together // Only change code above this line return this; }; // Let's create three browser windows const workWindow = new Window(['GMail', 'Inbox', 'Work mail', 'Docs', 'freeCodeCamp']); // Your mailbox, drive, and other work sites const socialWindow = new Window(['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium']); // Social sites const videoWindow = new Window(['Netflix', 'YouTube', 'Vimeo', 'Vine']); // Entertainment sites // Now perform the tab opening, closing, and other operations const finalTabs = socialWindow .tabOpen() // Open a new tab for cat memes .join(videoWindow.tabClose(2)) // Close third tab in video window, and join .join(workWindow.tabClose(1).tabOpen()); console.log(finalTabs.tabs);
// Only change code below this line const tabsBeforeIndex = this.tabs.slice(0, index); // Get the tabs before the tab const tabsAfterIndex = this.tabs.slice(index + 1); // Get the tabs after the tab this.tabs = tabsBeforeIndex.concat(tabsAfterIndex); // Join them together // Only change code above this line