From 3a1c9c689e2e116b85970af52f9ac26af7de9434 Mon Sep 17 00:00:00 2001 From: huseyingulec Date: Sun, 11 Feb 2024 16:52:24 +0100 Subject: [PATCH] chore(odin): add new content --- .../async_and_await.md | 233 +++++++++++ .../asynchronous_code.md | 99 +++++ .../project_weather_app.md | 34 ++ .../working_with_apis.md | 237 +++++++++++ .../a_very_brief_intro_to_cs.md | 43 ++ .../common_data_structures_algorithms.md | 61 +++ .../hash_map_data_structure.md | 211 ++++++++++ .../project_binary_search_trees.md | 75 ++++ .../computer_science/project_hash_map.md | 76 ++++ .../project_knights_travails.md | 45 ++ .../computer_science/project_linked_lists.md | 66 +++ .../computer_science/project_recursion.md | 51 +++ .../computer_science/recursive_methods.md | 61 +++ .../computer_science/space_complexity.md | 155 +++++++ .../computer_science/time_complexity.md | 336 +++++++++++++++ .../conclusion.md | 13 + .../javascript/introduction/a_quick_review.md | 19 + .../introduction/how_this_course_will_work.md | 24 ++ .../dynamic_user_interface_interactions.md | 54 +++ .../form_validation_with_javascript.md | 57 +++ .../javascript_in_the_real_world/linting.md | 67 +++ .../what_is_es6.md | 21 + .../classes.md | 64 +++ .../es6_modules.md | 182 ++++++++ .../factory_functions_and_module_pattern.md | 294 +++++++++++++ .../organizing_your_javascript_code/json.md | 15 + .../objects_and_object_constructors.md | 392 ++++++++++++++++++ .../oop_principles.md | 94 +++++ ...izing_your_javascript_code_introduction.md | 16 + .../project_library.md | 33 ++ .../project_restaurant_page.md | 56 +++ .../project_tic_tac_toe.md | 16 + .../project_todo_list.md | 34 ++ .../webpack.md | 70 ++++ .../testing_javascript/more_testing.md | 92 ++++ .../testing_javascript/project_battleship.md | 36 ++ .../project_testing_practice.md | 39 ++ .../testing_javascript/testing_basics.md | 37 ++ 38 files changed, 3508 insertions(+) create mode 100644 content/odin/javascript/asynchronous_javascript_and_apis/async_and_await.md create mode 100644 content/odin/javascript/asynchronous_javascript_and_apis/asynchronous_code.md create mode 100644 content/odin/javascript/asynchronous_javascript_and_apis/project_weather_app.md create mode 100644 content/odin/javascript/asynchronous_javascript_and_apis/working_with_apis.md create mode 100644 content/odin/javascript/computer_science/a_very_brief_intro_to_cs.md create mode 100644 content/odin/javascript/computer_science/common_data_structures_algorithms.md create mode 100644 content/odin/javascript/computer_science/hash_map_data_structure.md create mode 100644 content/odin/javascript/computer_science/project_binary_search_trees.md create mode 100644 content/odin/javascript/computer_science/project_hash_map.md create mode 100644 content/odin/javascript/computer_science/project_knights_travails.md create mode 100644 content/odin/javascript/computer_science/project_linked_lists.md create mode 100644 content/odin/javascript/computer_science/project_recursion.md create mode 100644 content/odin/javascript/computer_science/recursive_methods.md create mode 100644 content/odin/javascript/computer_science/space_complexity.md create mode 100644 content/odin/javascript/computer_science/time_complexity.md create mode 100644 content/odin/javascript/finishing_up_with_javascript/conclusion.md create mode 100644 content/odin/javascript/introduction/a_quick_review.md create mode 100644 content/odin/javascript/introduction/how_this_course_will_work.md create mode 100644 content/odin/javascript/javascript_in_the_real_world/dynamic_user_interface_interactions.md create mode 100644 content/odin/javascript/javascript_in_the_real_world/form_validation_with_javascript.md create mode 100644 content/odin/javascript/javascript_in_the_real_world/linting.md create mode 100644 content/odin/javascript/javascript_in_the_real_world/what_is_es6.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/classes.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/es6_modules.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/factory_functions_and_module_pattern.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/json.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/objects_and_object_constructors.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/oop_principles.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/organizing_your_javascript_code_introduction.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/project_library.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/project_restaurant_page.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/project_tic_tac_toe.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/project_todo_list.md create mode 100644 content/odin/javascript/organizing_your_javascript_code/webpack.md create mode 100644 content/odin/javascript/testing_javascript/more_testing.md create mode 100644 content/odin/javascript/testing_javascript/project_battleship.md create mode 100644 content/odin/javascript/testing_javascript/project_testing_practice.md create mode 100644 content/odin/javascript/testing_javascript/testing_basics.md diff --git a/content/odin/javascript/asynchronous_javascript_and_apis/async_and_await.md b/content/odin/javascript/asynchronous_javascript_and_apis/async_and_await.md new file mode 100644 index 00000000..ff75a717 --- /dev/null +++ b/content/odin/javascript/asynchronous_javascript_and_apis/async_and_await.md @@ -0,0 +1,233 @@ +### Introduction + +Asynchronous code can become difficult to follow when it has a lot of things going on. `async` and `await` are two keywords that can help make asynchronous read more like synchronous code. This can help code look cleaner while keeping the benefits of asynchronous code. + +For example, the two code blocks below do the exact same thing. They both get information from a server, process it, and return a promise. + +```javascript +function getPersonsInfo(name) { + return server.getPeople().then(people => { + return people.find(person => { return person.name === name }); + }); +} +``` + +```javascript +async function getPersonsInfo(name) { + const people = await server.getPeople(); + const person = people.find(person => { return person.name === name }); + return person; +} +``` + +The second example looks much more like the kind of functions you are used to writing. However, did you notice the `async` keyword before the function declaration? How about the `await` keyword before `server.getPeople()`? + +If you'd like to try running these functions on your own, paste the following code block representing a server before the function definitions. How this "server" works is not important and is just an abstraction. The goal here is so that you can see that both functions behave exactly the same and return a promise. + +```javascript +const server = { + people: [ + { + name: "Odin", + age: 20, + }, + { + name: "Thor", + age: 35, + }, + { + name: "Freyja", + age: 29, + }, + ], + + getPeople() { + return new Promise((resolve, reject) => { + // Simulating a delayed network call to the server + setTimeout(() => { + resolve(this.people); + }, 2000); + }); + }, +}; +``` + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + + - Explain how you declare an `async` function + - Explain what the `async` keyword does + - Explain what the `await` keyword does + - Explain what an `async` function returns + - Explain what happens when an error is thrown inside an `async` function + - Explain how you can handle errors inside an `async` function + +### The async keyword + +The `async` keyword is what lets the JavaScript engine know that you are declaring an asynchronous function. This is required to use `await` inside any function. When a function is declared with `async`, it automatically returns a promise; returning in an `async` function is the same as resolving a promise. Likewise, throwing an error will reject the promise. + +An important thing to understand is `async` functions are just syntactical sugar for `promises`. + +The `async` keyword can also be used with any of the ways a function can be created. Said differently: it is valid to use an `async` function anywhere you can use a normal function. Below you will see some examples that may not be intuitive. If you don't understand them, come back and take a look when you are done with the assignments. + +```javascript +const yourAsyncFunction = async () => { + // do something asynchronously and return a promise + return result; +} +``` + +```javascript +anArray.forEach(async item => { + // do something asynchronously for each item in 'anArray' + // one could also use .map here to return an array of promises to use with 'Promise.all()' +}); +``` + +```javascript +server.getPeople().then(async people => { + people.forEach(person => { + // do something asynchronously for each person + }); +}); +``` + +### The await keyword + +`await` does the following: it tells JavaScript to wait for an asynchronous action to finish before continuing the function. It's like a 'pause until done' keyword. The `await` keyword is used to get a value from a function where you would normally use `.then()`. Instead of calling `.then()` after the asynchronous function, you would assign a variable to the result using `await`. Then you can use the result in your code as you would in your synchronous code. + +### Error handling + +Handling errors in `async` functions is very easy. Promises have the `.catch()` method for handling rejected promises, and since async functions just return a promise, you can call the function, and append a `.catch()` method to the end. + +```javascript +asyncFunctionCall().catch(err => { + console.error(err) +}); +``` + +But there is another way: the mighty `try/catch` block! If you want to handle the error directly inside the `async` function, you can use `try/catch` just like you would inside synchronous code. + +```javascript +async function getPersonsInfo(name) { + try { + const people = await server.getPeople(); + const person = people.find(person => { return person.name === name }); + return person; + } catch (error) { + // Handle the error any way you'd like + } +} +``` + +Doing this can look messy, but it is a very easy way to handle errors without appending `.catch()` after your function calls. How you handle the errors is up to you, and which method you use should be determined by how your code was written. You will get a feel for what needs to be done over time. The assignments will also help you understand how to handle your errors. + +### Practice + +Remember the Giphy API practice project? (If not, you should go back and complete the API lesson). We are going to convert the promise based code into `async/await` compatible code. Here's a refresher of the code we are starting with: + +```javascript + +``` + +Since `await` does not work on the global scope, we will have to create an `async` function that wraps our API call to Giphy. + +```javascript + +``` + +Now that we have a function that is asynchronous, we can then start refactoring from using promises to using `await`: + +```javascript + +``` + +Since `response` is still the same object we have passed to the `.then()` block at the start, we still need to use the `.json()` method, which in turn returns a promise. Because `.json()` returns a promise, we can use `await` to assign the response to a variable. + +```javascript + +``` + +To use this function, we just need to call it with `getCats()` in our code. + +```javascript + +``` + +This code will behave exactly like the code from the last lesson; it just looks a bit different after refactoring. `async/await` are very useful tools when it comes to cleaning up asynchronous JavaScript code. It is important to remember `async/await` are just promises written in a different way. Do the assignments below, and dive deeper into the understanding of `async/await`. + +### Assignment + +
+ +1. Read this [Async and Await article](https://javascript.info/async-await) for a solid introduction to async/await. This [Async and Await examples article](https://codeburst.io/javascript-es-2017-learn-async-await-by-example-48acc58bad65) also has some good examples of its use. +2. Watch this [Async and Await video by Wes Bos](https://www.youtube.com/watch?v=9YkUCxvaLEk) for a good overview on async/await and its purpose, along with a special trick. + +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + + - [How do you declare an `async` function?](#the-async-keyword) + - [What does the `async` keyword do?](#the-async-keyword) + - [What does the `await` keyword do?](#the-await-keyword) + - [What is returned from an `async` function?](https://javascript.info/async-await#summary) + - [What happens when an error is thrown inside an `async` function?](https://javascript.info/async-await#error-handling) + - [How can you handle errors inside an `async` function?](https://javascript.info/async-await#error-handling) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +1. This [Change promises to async/await video](https://www.youtube.com/watch?v=COKdtOgopWQ) is an example of how you can change callbacks, to promises, to async/await. +2. This [Promises, Async and Await video](https://www.youtube.com/watch?v=vn3tm0quoqE) gives a comprehensive view of Promises, async, and await. +3. For a more interactive explanation and example, try a [Scrim on async and await](https://scrimba.com/scrim/crd4eMc6?embed=odin,mini-header,no-next-up). diff --git a/content/odin/javascript/asynchronous_javascript_and_apis/asynchronous_code.md b/content/odin/javascript/asynchronous_javascript_and_apis/asynchronous_code.md new file mode 100644 index 00000000..e9b6253b --- /dev/null +++ b/content/odin/javascript/asynchronous_javascript_and_apis/asynchronous_code.md @@ -0,0 +1,99 @@ +### Introduction + +Since JavaScript is the language of the web, there are some functions that by necessity are going to take a decent amount of time to complete, such as fetching data from a server to display on your site. For this reason, JavaScript includes support for asynchronous functions, or to put it another way, functions that can happen in the background while the rest of your code executes. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + + - Explain what a callback is + - Explain what a promise is + - Explain the circumstances under which promises are better than callbacks + - Explain what the `.then()` function does + +### Callbacks + +In the recent past, the way that these were most commonly handled were with __callbacks__, and even now they are still used quite a lot in certain circumstances. + +> A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action. [MDN](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function) + +Callbacks are functions that get passed into other functions. For example: + +```javascript +myDiv.addEventListener("click", function(){ + // do something! +}) +``` + +Here, the function `addEventListener()` takes a callback (the "do something" function) and then calls it when `myDiv` gets clicked. + +You will likely recognize this pattern as something that happens _all the time_ in JavaScript code. Unfortunately, though they are useful in situations like the above example, using callbacks can get out of hand, especially when you need to chain several of them together in a specific order. The rest of this lesson discusses patterns and functions that will help keep you out of [Callback hell](http://callbackhell.com/). + +Take a moment to skim through this [Callback article](https://github.com/maxogden/art-of-node#callbacks) before moving on. Or, if you prefer to watch a video of [Callback functions](https://www.youtube.com/watch?v=QRq2zMHlBz4). + +### Promises + +There are multiple ways that you can handle asynchronous code in JavaScript, and they all have their use cases. Promises are one such mechanism, and they're one you will see somewhat often when using other libraries or frameworks. Knowing what they are and how to use them is quite useful. + +Essentially, a promise is an object that might produce a value at some point in the future. Here's an example: + +Lets say `getData()` is a function that fetches some data from a server and returns it as an object that we can use in our code: + +```javascript +const getData = function() { + // go fetch data from some API... + // clean it up a bit and return it as an object: + return data +} +``` + +The issue with this example is that it takes some time to fetch the data, but unless we tell our code that, it assumes that everything in the function happens essentially instantly. So, if we try to do this: + +```javascript +const myData = getData() +const pieceOfData = myData['whatever'] +``` + +We're going to run into trouble because when we try to extract `pieceOfData` out of the returned data, the function `getData()` will most likely still be fetching, so `myData` will not be the expected data, but will be `undefined`. Sad. + +We need some way to solve this problem, and tell our code to wait until the data is done fetching to continue. Promises solve this issue. We'll leave learning the specific syntax for the articles you're about to read, but essentially Promises allow you to do this: + +```javascript +const myData = getData() // if this is refactored to return a Promise... + +myData.then(function(data){ // .then() tells it to wait until the promise is resolved + const pieceOfData = data['whatever'] // and THEN run the function inside +}) +``` + +Of course, there are many more occasions where one would want to use Promises beyond fetching data, so learning these things now will be very useful to you. + +### Assignment + +
+ +1. Read this [Promises article](https://davidwalsh.name/promises). It's a good starting place and it's short and to the point. +2. Watch this [Promises video](https://youtu.be/DHvZLI7Db8E). It's a good place to get a feel for how one might actually use promises in the wild. +3. Watch this [What is Event Loop? video](https://www.youtube.com/watch?v=8aGhZQkoFbQ) to understand how asynchronous code works in JavaScript. +4. Read [Chapter 2: Callbacks](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/async%20%26%20performance/ch2.md) and [Chapter 3: Promises](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/async%20%26%20performance/ch3.md) from `You Don't Know JS`. In _Chapter 2_, the author explains the problems with callbacks and why callback hell will be your worst enemy (hint: it's the inversion of control and non-linear nature of callbacks). In _Chapter 3_, you go __deep__ into the how and why of promises. This chapter is not the easiest read, but you'll be a promise professional if you take the time to properly digest it. It's worth the effort. + +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + + - [What is a callback?](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function) + - [What is a promise?](#promises) + - [When should you use promises over callbacks?](http://callbackhell.com/) + - [What does the `.then()` function do?](https://davidwalsh.name/promises) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +1. This [Callback article](https://www.sitepoint.com/demystifying-javascript-closures-callbacks-iifes/) is another useful article about Callback functions in JavaScript. +2. The [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) for Promises. It might not be the best resource for _learning_ all about them, but once you've read a more friendly article or tutorial, this will probably be the place you return to for a refresher. +3. These [ES6 Promises video](https://www.youtube.com/watch?v=vQ3MoXnKfuQ), [Promises by The Net Ninja](https://www.youtube.com/watch?v=yswb4SkDoj0) and [Promises by ColorCode](https://www.youtube.com/watch?v=TnhCX0KkPqs) are nice introductions to Promises if you need more repetition and additional information. +4. This [Understanding JavaScript Promises Tutorial](https://www.digitalocean.com/community/tutorials/understanding-javascript-promises) is another good introduction. +5. For a more interactive explanation and example, try a [Scrim on asynchronous coding](https://scrimba.com/scrim/cof4e4fb797a2d0a236ea38ce?embed=odin,mini-header,no-next-up). diff --git a/content/odin/javascript/asynchronous_javascript_and_apis/project_weather_app.md b/content/odin/javascript/asynchronous_javascript_and_apis/project_weather_app.md new file mode 100644 index 00000000..f09943ae --- /dev/null +++ b/content/odin/javascript/asynchronous_javascript_and_apis/project_weather_app.md @@ -0,0 +1,34 @@ +### Introduction + +Use everything we've been discussing to create a weather forecast site using the weather API from the previous lesson. You should be able to search for a specific location and toggle displaying the data in Fahrenheit or Celsius. + +You should change the look of the page based on the data, maybe by changing the color of the background or by adding images that describe the weather. (You could even use the Giphy API to find appropriate weather-related gifs and display them). Feel free to use promises or async/await in your code, though you should try to become comfortable with both. + +
+ +#### WeatherAPI free tier + +After creating your API key from WeatherAPI, you will automatically be moved to the free tier after 14 days. This will limit the forecast data available to you to 3 days. You may want to keep this in mind while making design choices for the forecast display. + +
+ +### API keys, secrets, and security + +Not all APIs are free, and depending on how they're set up, they can cost money per use. This makes them a prime target for people looking to use the API without paying by using **your** API key. They can also be rate-limited, and if someone has access to your API key they can use up all of your uses. One way to prevent this issue is to store your API keys on the server and never send them to the frontend in the first place, this is often done using environment variables and it makes the key available only on the server the code is deployed to. + +When talking about API keys and security you'll often hear "Never trust the client" (client meaning the frontend). Often this means not to trust that data coming *from* the client is valid, but it also means that you cannot trust anything we send *to* the client. Because of this, when you leak an API key, Github will alert you that you have committed an API key publicly. After following this project, and indeed exposing the API key, you may notice that Github will send you this alert. This is totally OK for this project as this API key is publicly available and there is no consequence for exposing it. This is not to say ALL keys are this way. Later during the backend courses you will learn ways to securely deal with these topics. + +### Assignment + +
+ +1. Set up a blank HTML document with the appropriate links to your JavaScript and CSS files. +2. Write the functions that hit the API. You're going to want functions that can take a location and return the weather data for that location. For now, just `console.log()` the information. +3. Write the functions that _process_ the JSON data you're getting from the API and return an object with only the data you require for your app. +4. Set up a form that will let users input their location and will fetch the weather info (still just `console.log()` it). +5. Display the information on your webpage! +6. Add any styling you like! +7. Optional: add a 'loading' component that displays from the time the form is submitted until the information comes back from the API. Use DevTools to test for low-end devices. +8. Push that baby to github and share your solution below! + +
diff --git a/content/odin/javascript/asynchronous_javascript_and_apis/working_with_apis.md b/content/odin/javascript/asynchronous_javascript_and_apis/working_with_apis.md new file mode 100644 index 00000000..c9903cf5 --- /dev/null +++ b/content/odin/javascript/asynchronous_javascript_and_apis/working_with_apis.md @@ -0,0 +1,237 @@ +### Introduction + +One of the most powerful things a web developer can do is fetching data from a server and displaying it creatively on their site. In many cases, the server solely exists for that specific site. The server could contain blog posts, user data, high scores for a game or anything else. In other cases, the server is an open service that serves data to anyone that wants to use it (i.e. weather data or stock prices). In either case, the methods of accessing and then using that data are essentially the same. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + + - Explain what an API is + - Explain broadly how access to an API works + - Explain how to fetch and extract data from an API + - Explain why your API request might be blocked by the browser, and how to fix this + +### APIs + +Servers that are created for serving data for external use (in websites or apps) are often referred to as APIs or ['Application Programming Interfaces'](https://www.youtube.com/watch?v=s7wmiS2mSXY). + +There are multiple ways of requesting data from an API, but all of them basically do the same thing. For the most part, APIs are accessed through URLs, and the specifics of how to query these URLs change based on the specific service you are using. For example, WeatherAPI has several types of data that you can request. To get the current weather in a specific location, you can pass in the name of a city (optionally, you can also pass a zip code & even an ip-address!) as a URL query string parameter, like so: + +``` +https://api.weatherapi.com/v1/current.json?q=london +``` + +The specifics for using any API are usually documented on the service's website. [Check here for the WeatherAPI documentation](https://www.weatherapi.com/docs/). If you haven't already, go ahead and paste the weather URL above, with the city of your choice, into your browser... (we'll wait). + +You'll probably get an error like this: + +``` +{{"error":{"code":1002,"message":"API key is invalid or not provided."}}} +``` + +This brings us to another point about APIs. In most cases, you will have to create an account and request an "API key" from the API service before attempting to fetch data from their endpoints (specific URLs that you use to access a particular function or data within the API). Once obtained, an API key will usually have to be included with every data request, such as _another_ URL query string parameter: + +``` +https://api.weatherapi.com/v1/current.json?key=11111111111111111&q=london +``` + +As you can imagine, an API key is random and unique to you. As such, services like WeatherAPI can correlate your API key to your requests of their data, including how much and how often you are requesting it. + +On one hand, issuing API keys allows an API service to better track abuse of their systems and data. On the other hand, it can also be a way for those services to mitigate and recuperate operating costs. WeatherAPI, for example, provides not only a free tier but a variety of paid tiers that can cost up to 65 USD/month! After all, running servers costs money, and APIs are no exception. While a single request to an API might cost a fraction of a penny, imagine using that API to create an amazing weather app that gets used all over the world... you could easily have thousands of people accessing that data every minute! The cost to handle that traffic could quickly balloon up to significant sums for the API service. + +As such, you'll find that most API services, if not all, provide paid tiers that come with the ability to make more frequent requests, or provide access to more information unavailable in lower tiers. For example, WeatherAPI's free plan only allows your app to make a monthly total of 1 million requests and limits the information provided, while the "Business" tier allows up to 10,000,000 requests per month and gives you all of the available information! The free tier also comes with basic hourly and daily forecasting data, but it does not include data for a 30-day forecast ([details here if you're interested](https://www.weatherapi.com/pricing.aspx)). So, if your app becomes successful and needs additional features, you'll probably need to pay for a better account. + +Because your API key is **your** key to these services and data, securing them is an important habit, especially if you are using a paid tier. There are plenty of bots that crawl GitHub repositories solely for hardcoded/unsecured API keys, allowing bad agents to then access and [utilize the services and data you've paid for](https://web.archive.org/web/20150102022540/http://www.devfactor.net/2014/12/30/2375-amazon-mistake/). In fact, the more eagle-eyed readers may have noticed a problem with the demonstration above: The API key is right there in the URL request. It would not take much for an internet traffic sniffer to pick up on the API key, least of all someone looking over your shoulder! + +At this point in the curriculum, though, this point is largely moot. After all, we're leveraging free access to APIs, and the majority of our apps are only going to be used by us and the people that view our portfolios. Just make a note of the severe limitations of using API keys as demonstrated above for now. The basics of securing and obfuscating API keys from GitHub and from your requests will be covered later in the curriculum. + +Back to WeatherAPI. Go ahead and [create an account](https://www.weatherapi.com/signup.aspx) to obtain an API key from their free tier. Once the key has been activated, try making a new request with the city of your choice AND the API key passed in as query string parameters, like the example above. You'll hopefully see a proper response, something like: + +```JSON +{"location":{"name":"London","region":"City of London, Greater London","country":"United Kingdom","lat":51.52,"lon":-0.11,"tz_id":"Europe/London","localtime_epoch":1676482062,"localtime":"2023-02-15 17:27"},"current":{"temp_c":13.0,"temp_f":55.4,"is_day":0,"condition":{"text":"Clear","icon":"//cdn.weatherapi.com/weather/64x64/night/113.png","code":1000},"wind_mph":12.5,"wind_kph":20.2,"wind_degree":210,"wind_dir":"SSW","pressure_mb":1022.0,"pressure_in":30.18,"precip_mm":0.0,"precip_in":0.0,"humidity":58,"cloud":0,"feelslike_c":11.7,"feelslike_f":53.1,"vis_km":10.0,"vis_miles":6.0,"uv":4.0,"gust_mph":12.1,"gust_kph":19.4}} +``` + +Congratulations on making your first API request! + +### Fetching data + +So how do we actually get the data from an API into our code? + +A couple of years ago the main way to access API data in your code was using an `XMLHttpRequest`. This function still works in all browsers, but unfortunately, it is not particularly nice to use. The syntax looks something like this: + +```javascript +// Just getting XHR is a mess! +if (window.XMLHttpRequest) { // Mozilla, Safari, ... +  request = new XMLHttpRequest(); +} else if (window.ActiveXObject) { // IE +  try { +    request = new ActiveXObject('Msxml2.XMLHTTP'); +  } +  catch (e) { +    try { +      request = new ActiveXObject('Microsoft.XMLHTTP'); +    } +    catch (e) {} +  } +} + +// Open, send. +request.open('GET', 'https://url.com/some/url', true); +request.send(null); +``` + +Ouch. That was painful. + +Developers, feeling the pain of having to write that stuff out, began writing 3rd party libraries to take care of this and make it much easier to use. Some of the more popular libraries are [axios](https://github.com/mzabriskie/axios) and [superagent](https://github.com/visionmedia/superagent), both of which have their strengths and weaknesses. + +More recently, however, web browsers have begun to implement a new native function for making HTTP requests, and that's the one we're going to use and stick with for now. Meet fetch: + +```javascript +// URL (required), options (optional) +fetch('https://url.com/some/url') +  .then(function(response) { + // Successful response :) + }) + .catch(function(err) { +  // Error :( + }); +``` +In case you've forgotten, scroll back up and look at how you would use XHR to do the same thing. While you're admiring how nice and clean that code is, notice the `.then()` and `.catch()` functions there. Do you remember what those are? (PROMISES!) + +Let's change up our API for this example. We're going to walk through an example using fetch with the [giphy](https://giphy.com/) API to display a random gif on a webpage. The API requires you to sign up and get a free API key, so go ahead and [do that here](https://developers.giphy.com/docs/api#quick-start-guide). + +Giphy has several methods for searching and finding GIFs which you can read about in their documentation. Today we're just going to use the 'translate' endpoint because it's the simplest one for our purposes. You can find the appropriate URL in their documentation by scrolling down [here](https://developers.giphy.com/docs/api/endpoint#translate). What it tells us is that the correct URL is `api.giphy.com/v1/gifs/translate` and that it requires 2 parameters, your `api_key` and a `search term`. If you put it all together correctly (with YOUR API key) you should get something like this: + +```javascript +'https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats' +// of course we're searching for cats +``` + +Go ahead and try that URL (with YOUR API key) in a browser. If everything goes well you should get a relatively long string of data and no errors. + +### CORS + +A side note before we start putting this into our code. For security reasons, by default, browsers restrict HTTP requests to outside sources (which is exactly what we're trying to do here). There's a very small amount of setup that we need to do to make fetching work. Learning about this is outside our scope right now, but if you want to learn a bit about it this [Wikipedia article](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) and this [Javascript.info article](https://javascript.info/fetch-crossorigin) are good starting points. + +Whether or not you took the detour to learn all about Cross Origin Resource Sharing (CORS) the fix is straightforward. With fetch, you are able to easily supply a JavaScript object for options. It comes right after the URL as a second parameter to the fetch function: + +```javascript +fetch('url.url.com/api', { +  mode: 'cors' +}); +``` + +Adding the `{mode: 'cors'}` after the URL, as shown above, will solve our problems for now. In the future, however, you may want to look further into the implications of this restriction. + +### Let's do this +For now, we're going to keep all of this in a single HTML file. So go ahead and create one with a single blank image tag and an empty script tag in the body. + +```HTML + + + +  Document + + +  + + +``` + +In the script tag, let's start by selecting the image and assigning it to a variable so that we can change the URL once we've received it from the Giphy API. + +```HTML + +``` + +Adding fetch with our URL from above is also relatively easy: + +```HTML + +``` + +You should now be able to open the HTML file in your browser, and while you won't see anything on the page, you _should_ have something logged in the console. The trickiest part of this whole process is deciphering how to get to the data you desire from the server's response. In this case, inspecting the browser's console will reveal that what's being returned is _another_ Promise... to get the data we need another `.then()` function. + +```HTML + +``` + +Now we have a JavaScript object and if you inspect it closely enough you'll find that the data we need (an image URL) is nested rather deeply inside the object: + +![response](https://cdn.statically.io/gh/TheOdinProject/curriculum/284f0cdc998be7e4751e29e8458323ad5d320303/javascript/async-apis/APIs/imgs/00.png) + +To get to the data we need to drill down through the layers of the object until we find what we want! + +```HTML + +``` + +Running the file should now log the URL of the image. All that's left to do is set the source of the image that's on the page to the URL we've just accessed: + +```HTML + +``` + +If all goes well, you should see a new image on the page every time you refresh! + +If you've gotten lost along the way, check out this [jsbin project](http://jsbin.com/canofar/edit?html,output). Besides the glorious styling, this is what your version should look like. + +While we are pushing this API key to the frontend, this isn't something you should do with any key that is not free. Keys used on the client-side are considered public knowledge, so caution must be taken with sensitive and non-free keys. Handling keys without pushing them to the frontend will be taught in later sections if you haven't already learned it in the Ruby course. + +### Assignment + +
+ +1. Read this [Fetch documentation](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch). It's not all that complicated to use, but we've only really scratched the surface at this point. +2. Check out this [list of Public APIs](https://github.com/n0shake/Public-APIs) and let your imagination go wild. +3. Expand on our little project here by adding a button that fetches a new image without refreshing the page. +4. Add a search box so users can search for specific gifs. You should also investigate adding a `.catch()` to manage most errors (i.e. invalid API key). Keep in mind that Giphy responds with a status code of 200 with an empty data array when it doesn't find any gifs with the searched keyword, in other words the `.catch()` won't be executed. Adjust your code to effectively handle such scenarios, displaying a default image or an error message if the search fails. + +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + + - [What is an API?](#apis) + - [How is access to an API restricted?](#apis) + - [How do you fetch and extract data from an API?](#fetching-data) + - [Why might your API request be blocked by the browser, and how might you fix this?](#cors) diff --git a/content/odin/javascript/computer_science/a_very_brief_intro_to_cs.md b/content/odin/javascript/computer_science/a_very_brief_intro_to_cs.md new file mode 100644 index 00000000..2e8b5689 --- /dev/null +++ b/content/odin/javascript/computer_science/a_very_brief_intro_to_cs.md @@ -0,0 +1,43 @@ +### Introduction + +You've learned how to build some cool stuff already and, frankly, you could probably make some decent websites without a crazy amount of additional formal education. However, just because you can write English at a grade school level doesn't mean you will be editing the New York Times anytime soon. + +In the world of programming, there's a difference between solving a problem the brute force way and solving a problem WELL. We touched on the first layer of this when we covered basic programming and how you should break apart your code into well-organized chunks. + +If you assume those lessons were all about learning how to write good code, these next few lessons are going to be about training yourself to figure out the best code to write -- the most elegant solution to the problem at hand. It becomes particularly important whenever you start working with large data sets, like when your website becomes highly successful. + +We're taking a look at some more Computer Science-y concepts here because they are fundamental for a reason. Some problems require you to use tools beyond just arrays and iterators. There's no sense reinventing the wheel when others have already figured out good methods for solving certain types of problems. + +If that doesn't get you interested, remember that this curriculum is meant to prepare you for life beyond the web page. If you're interested in applying for a job, you'll be asked questions that directly touch on some of this stuff. It's going to require you to put on your thinking cap (sorry, it had to happen sometime), but we won't be doing anything too crazy. We'll stick to the practical side of this material as opposed to getting too stuck in theory. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- What is an algorithm? +- What is pseudo-code? + +### Assignment + +
+ 1. Watch [Introduction to Algorithms by David Malan](https://www.youtube.com/watch?v=6hfOvs8pY1k) on TedEd to see how to think about algorithms. + 2. Watch [What is an Algorithm?](https://youtu.be/e_WfC8HwVB8) on YouTube for a more structured look at solving problems using algorithms. + 3. Read [this Quora answer about the importance of algorithms in web development](https://qr.ae/py3NAc) to get some context for why we're going over this stuff. + 4. Watch [What is pseudocode?](https://www.youtube.com/watch?v=Rg-fO7rDsds) +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [What is an Algorithm?](https://youtu.be/e_WfC8HwVB8) +- [What is the importance of algorithms in web development?](https://qr.ae/py3NAc) +- [What is pseudocode?](https://www.youtube.com/watch?v=Rg-fO7rDsds) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +- [Wikipedia on Computer Science](http://en.wikipedia.org/wiki/Computer_science) +- [Wikipedia on Algorithms](http://en.wikipedia.org/wiki/Algorithm) +- [Map of Computer Science](https://youtu.be/SzJ46YA_RaA) diff --git a/content/odin/javascript/computer_science/common_data_structures_algorithms.md b/content/odin/javascript/computer_science/common_data_structures_algorithms.md new file mode 100644 index 00000000..9501bea5 --- /dev/null +++ b/content/odin/javascript/computer_science/common_data_structures_algorithms.md @@ -0,0 +1,61 @@ +### Introduction + +The basic idea of a **data structure** is to store data in a way that meets the needs of your particular application. You might be inclined to store a particular kind of data in one giant array, but it would be rather time consuming to locate a specific value if you had a significant number and depth of items. So you need to look to other options. + +Depending on the application, there are a batch of other basic data structures available to help you out. The differences between them typically have to do with trade-offs between how long it takes to first populate the structure, how long it takes to add or find elements, and how large the structure is in memory. + +We'll save the specifics of data structures for more computer-science-oriented courses, but this introduction should again expand your toolbox slightly so you can identify and solve certain problems where plain old Arrays, Hashes and Sets don't quite cut it. New structures and strategies will be particularly relevant, for instance, when you're trying to search through a large batch of data for a particular value or plan out a strategy several moves in advance. + +You've already had a brief introduction to **algorithms** over some of the other lessons and you even got to write your own Merge Sort algorithm in the last project. You'll find that sorting algorithms are quite common. Another major area for algorithms is in search, where milliseconds count. When you're searching through enormous troves of data, the quality of your search algorithm is incredibly important. Traversing a data tree looking for a particular element is a related problem that's common in data intensive applications. + +Luckily for you, these complex algorithmic problems have all been solved many times in the past. Understanding _how_ they are solved will give you some great tools to apply to other (similar) problems on your own. Algorithms are really just ways of solving problems systematically. In this brief introduction, we'll focus on a couple of algorithms that you may run into when coding on your own -- breadth-first-search and depth-first-search. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- What is a data structure? +- What are stacks and queues? +- What's the best way to implement stacks and queues in JavaScript? +- Why bother having many different search algorithms? +- What are breadth-first-search (BFS) and depth-first-search (DFS)? +- What situations would you want to use BFS? +- What situations would you want to use DFS instead? + +### Assignment + +
+ 1. Glance over the [Wikipedia entry on Data Structures](http://en.wikipedia.org/wiki/Data_structure) for a high level overview of things. + 2. Learn about basic algorithms from Coursera's Algorithms course in [this video](http://www.youtube.com/watch?v=u2TwK3fED8A). The first 10 minutes are really the meat of the introduction to algorithms, the rest gets more mathematical (if you're so inclined). + 3. Read [What is an Algorithm and How Does it Make You a Better Programmer](http://blog.thefirehoseproject.com/posts/what-is-an-algorithm/) for another basic look at what algorithms are. + 4. Learn about how binary search works by watching [this video](https://www.youtube.com/watch?v=T98PIp4omUA) from Harvard's CS50 on YouTube. + 5. Now, we're going to focus on learning about binary search trees. Start by watching [this video](https://www.youtube.com/watch?v=FvdPo8PBQtc) to learn how a binary search tree is constructed from an unordered array. + 6. Next, learn about the principles of queues and stacks, which are concepts used in breadth-first search and depth-first search, respectively, by watching [this video](https://www.youtube.com/watch?v=6QS_Cup1YoI). + 7. Finally, learn about breadth-first search and depth-first search of binary search trees from this series of videos on YouTube: + - [Binary tree traversal](https://www.youtube.com/watch?v=9RHO6jU--GU) + - [Breadth-first traversal](https://www.youtube.com/watch?v=86g8jAQug04) + - [Depth-first traversal](https://www.youtube.com/watch?v=gm8DUJJhmY4) +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [What is the difference between a stack and a queue?](https://www.youtube.com/watch?v=6QS_Cup1YoI) +- [What are the enqueue and dequeue properties?](http://blog.thefirehoseproject.com/posts/what-is-an-algorithm/) +- [What is a linked list? What is a node?](https://en.wikipedia.org/wiki/Data_structure#Examples) +- [Which recursive problem-solving method/algorithm design principle does binary search implement?](https://youtu.be/T98PIp4omUA?t=20) +- [What abstract data type would you use to defer/store nodes in a breadth-first tree traversal?](https://youtu.be/86g8jAQug04?t=103) +- [What abstract data type would you use to defer/store nodes in a depth-first tree traversal?](https://youtu.be/gm8DUJJhmY4?t=499) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +- [Khan Academy's great Algorithms Course](https://www.khanacademy.org/computing/computer-science/algorithms) +- [Stanford's Coursera 4-Part Algorithm Course](https://www.coursera.org/specializations/algorithms) +- [Visualizing Algorithms from Mike Bostock](http://bost.ocks.org/mike/algorithms/) +- [Another free course on algorithms by Udacity](https://www.udacity.com/course/intro-to-algorithms--cs215) +- [A more detailed video on stacks and queues](https://www.youtube.com/watch?v=idrrIMXXeHM) +- [An article](https://web.archive.org/web/20221207000421/https://www.crondose.com/2016/06/create-a-binary-search-tree-array) that discusses how to construct a binary search tree from an unordered array. +- [A stack overflow discussion](https://stackoverflow.com/questions/3332947/when-is-it-practical-to-use-depth-first-search-dfs-vs-breadth-first-search-bf) on the relative strengths of BFS and DFS. diff --git a/content/odin/javascript/computer_science/hash_map_data_structure.md b/content/odin/javascript/computer_science/hash_map_data_structure.md new file mode 100644 index 00000000..eb8cc143 --- /dev/null +++ b/content/odin/javascript/computer_science/hash_map_data_structure.md @@ -0,0 +1,211 @@ +### Introduction + +One of the most used data structures across programming languages is a hash table, aka hash map. If you've worked with JavaScript Object Literals (`{}`), [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set), or [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), then you have used structures based on hash tables. But how do they work internally? How can we save key value pairs and later retrieve them? + +In this lesson, you will learn how it all works, and even implement your own hash map in the next project! To start, here is a brief description of a hash map: a hash map takes in a key value pair, produces a hash code, and stores the pair in a bucket. Hash codes? Buckets? What? Don't fret, we'll learn all about these concepts and more. Buckle up and let's dive in! + +### Lesson overview + +By the end of this lesson, you will learn about: + +- Hash codes and how to generate them. +- Hash maps and how they work internally. + +### What is a hash code? + +Let's start by learning what it means to hash a value. Hashing involves taking an input in and generating a corresponding output. A hash function should be a pure function. Hashing the same input should always return the same hash code, and there should be no random generation component. For example, let's look at a hashing function that takes a name and gives us the first letter of that name: + +```javascript +function hash(name) { + return name.charAt(0); +} +``` + +We created our first basic hashing function. + +There is a key difference between hashing and ciphering (encryption): reversibility. +Hashing is a one-way process. Using the above example, you can make a hash code from a name, but you cannot take a hash code and revert it back to a name. If you have a name `"Carlos"`, we can hash it to `"C"`. But it's impossible to reverse it from `"C"` back to its original form. You cannot know if it's `"Carlos"`, maybe it's `"Carla"` or `"Carrot"`. We don't know. + +
+Hashing is very good for security. Given a password, you can save the hash of that password rather than the password's plain text. If someone steals your hashes, they cannot know the original passwords since they are unable to reverse the hash back to the password. +
+ +#### Use cases + +What can we do with those hashes? You have probably seen it in school where a folder is organized into smaller folders, and each folder holds information about people with the same first letter: + +```text +C: + carlos.txt + carla.txt +B: + bryan.txt + bob.txt + beatrice.txt + bella.txt + benjamin.txt + bianca.txt +``` + +If we get a new student in our school with the name `"Carlos"`, we can run our hash function to find out which folder to place them in. `hash("Carlos") -> "C"` so we put `"Carlos"` in the directory labeled `C`. + +You might have spotted a problem: what if our school is populated with many people whose names share the same first letter `C`? Then we will have a directory labeled `C` that holds too many names while other directories could be empty. To eliminate this duplication and better separate our students, we need to rework our hash function. + +```javascript +function hash(name, surname) { + return name.charAt(0) + surname.charAt(0); +} +``` + +Instead of just taking the first name letter, we take the first name and last name letters. `"Carlos Smith"` will have a hash code of `"CS"`. This will spread our students among more directories and will eliminate many duplicate hash codes from being generated. + +![Example of hashing a name using first name's first letter, and last name's first letter](https://cdn.statically.io/gh/TheOdinProject/curriculum/7ea463cfb7c05c330d72f5977cc5fe3b0c640b86/javascript/computer_science/hash_map_data_structure/imgs/00.png) + +But it still doesn't solve our problem. What if we have a common combination of first letters in students' names? Then we will still have an imbalance in the size of the directories. We need to make it easier to find the person we're looking for, so let's rework our hash code. + +```javascript +function stringToNumber(string) { + let hashCode = 0; + for (let i = 0; i < string.length; i++) { + hashCode += string.charCodeAt(i); + } + + return hashCode; +} + +function hash(name, surname) { + return stringToNumber(name) + stringToNumber(surname); +} +``` + +We not only consider the first letters with this technique. Instead, we take the entire name and convert it into numbers. + +You might be thinking, wouldn't it just be better to save the whole name as a hash code? That is true. This would make it unique for each name, but in the context of hash maps, we need the hash code to be a number. This number will serve as the index to the bucket that will store the key value pair. More on buckets in the next section. + +### Buckets + +Buckets are storage that we need to store our elements. Simply, it's an array. For a specific key, we decide which bucket to use for storage through our hash function. The hash function returns a number that serves as the index of the array at which we store this specific key value pair. Let's say we wanted to store a person's full name as a key "Fred" with a value of "Smith": + +1. Pass "Fred" into the hash function to get the hash code which is `385`. +1. Find the bucket at index `385`. +1. Store the key value pair in that bucket. In this case, the key would be "Fred" and the value would be "Smith". + +This is an oversimplified explanation; we'll discuss more internal mechanics later in the lesson. + +Now if we wanted to get a value using a key: + +1. Put each entry inside a bucket as a `Node` item, which holds both the key and the value. +1. To retrieve the value, we hash the key and calculate its bucket number. +1. If the bucket is not empty, then we go to that bucket. +1. Now we compare if the node's key is the same key that was used for the retrieval. +1. If it is, then we can return the node's value. Otherwise, we return `null`. + +Maybe you are wondering, why are we comparing the keys if we already found the index of that bucket? Remember, a hash code is just the location. Different keys might generate the same hash code. We need to make sure the key is the same by comparing both keys that are inside the bucket. + +This is it, making this will result in a hash table with `has`, `set` and `get`. + +What if we found the hash code, but also the key value is the same as what we already have in the bucket? We check if it's the same item by comparing the keys, then we overwrite the value with our new value. This is how we can only have unique values inside a `Set`. A `Set` is similar to a hash map but the key difference (pun intended) is that a `Set` will have nodes with only keys and no values. + +#### Insertion order is not maintained + +A hash map does not guarantee insertion order when you iterate over it. The translation of hash codes to indexes does not follow a linear progression from the first to the last index. Instead, it is more unpredictable, irrespective of the order in which items are inserted. That means if you are to retrieve the array of keys and values to iterate over them, then they will not be in order of when you inserted them. + +Some libraries implement hash tables with insertion order in mind such as JavaScript's own `Map`. For the coming project however we will be implementing an unordered hash table. +Example: if we insert the values `Mao`, `Zach`, `Xari` in this order, we may get back `["Zach", "Mao", "Xari"]` when we call an iterator. + +If iterating over a hash map frequently is your goal, then this data structure is not the right choice for the job, a simple array would be better. + +### Collisions + +We have another problem that we need to deal with: collisions. A collision occurs when two different keys generate the exact same hash code. Because they have the same hash code, they will land in the same bucket. + +Let's take an example: hashing the name `"Sara"` and the name `"raSa"` will generate the same hash code. That is because the letters in both names are the same, just arranged differently. +We can rework our `stringToNumber` function so that it can give us unique hash codes that depend on where the letters appear in the name using an algorithm. + +```javascript +function stringToNumber(string) { + let hashCode = 0; + + const primeNumber = 31; + for (let i = 0; i < string.length; i++) { + hashCode = primeNumber * hashCode + string.charCodeAt(i); + } + + return hashCode; +} +``` + +With our new function we will have different hash codes for the names `"Sara"` and `"raSa"`. This is because even if both names have the same letters, some of the letters appear in different locations. The hash code started to change because we are multiplying the old hash with every new iteration and then adding the letter code. + +
+ Notice the usage of a prime number. We could have chosen any number we wanted, but prime numbers are preferable. Multiplying by a prime number will reduce the likelihood of hash codes being evenly divisible by the bucket length, which helps minimize the occurrence of collisions. +
+ +Even though we reworked our hash function to avoid the `"Sara"/"raSa"` collision, there is always the possibility for collisions. Since we have a finite number of buckets, there is no way to eliminate collisions entirely. Let's try to minimize them. + +#### Dealing with collisions + +Up until now, our hash map is a one-dimensional data structure. What if each `Node` inside the bucket can store more than one value? Enter `Linked Lists`. Now, each bucket will be a Linked List. When inserting into a bucket, if it's empty, we insert the head of Linked List. If a head exists in a bucket, we follow that Linked List to add to the end of it. + +You probably understand by this point why we must write a good hashing function which eliminates as many collisions as possible. Most likely you will not be writing your own hash functions, as most languages have it built in, but understanding how hash functions work is important. + +### Growth of a hash table + +Let's talk about the growth of our buckets. We don't have infinite memory, so we can't have the infinite number of buckets. We need to start somewhere, but starting too big is also a waste of memory if we're only going to have a hash map with a single value in it. So to deal with this issue, we should start with a small array for our buckets. We'll use an array size `16`. + +
+ Most programming languages start with the default size of `16` because it's a power of 2, which helps with some techniques for performance that require bit manipulation for indexes. +
+ +How are we going to insert into those buckets when our hash function generates big numbers like `20353924`? We make use of the modulo `%` operation `given any number modulo by 16 we will get a number between 0 and 15`. +For example, if we are to find the bucket where the value `"Manon"` will land, then we do the following: +![hashing using hash code and modular operation example](https://cdn.statically.io/gh/TheOdinProject/curriculum/7ea463cfb7c05c330d72f5977cc5fe3b0c640b86/javascript/computer_science/hash_map_data_structure/imgs/01.png) + +As we continue to add nodes into our buckets, collisions get more and more likely. Eventually, however, there will be more nodes than there are buckets, which guarantees a collision (check the additional resources section for an explanation of this fact if you're curious). + +Remember we don't want collisions. In a perfect world each bucket will either have 0 or 1 node only, so we grow our buckets to have more chance that our nodes will spread and not stack up in the same buckets. To grow our buckets, we create a new buckets list that is double the size of the old buckets list, then we copy all nodes over to the new buckets. + +#### When do we know that it's time to grow our buckets size? + +To deal with this, our hash map class needs to keep track of two new fields, the `capacity` and the `load factor`. + +- The `capacity` is the total number of buckets we currently have. Keeping track of this will let us know if our map has reached a certain threshold aka `load factor`, + +- The `load factor` is a number that we can assign our hash map to at the start. It's the factor that will determine when it is a good time to grow our buckets. For example, a load factor of `0.75` means our hash map will need to grow its buckets when the capacity reaches 75% full. Setting it too low will consume too much memory by having too many empty buckets, while setting it too high will allow our buckets to have many collisions before we grow them. Hash map implementations across various languages use a load factor between `0.75` and `1`. + +### Computation complexity + +A hash map is very efficient in its insertion, retrieval and removal operations. This is because we use array indexes to do these operations. A hash map has an average case complexity of `O(1)` for the following methods: + +- Insertion +- Retrieval +- Removal + +Assuming we have a good hash map written. The worst case of those operations would be `O(n)` and that happens when we have all our data hashes to the same exact bucket. The complexity itself surfaces because of the linked list, and `O(n)` is because we are traversing the linked list to insert yet again another node into the same bucket, which happens specifically because of collisions. + +The growth of our hash map has the complexity of `O(n)` at all times. + +### Assignment + +
+ +- Read [What are Hash Functions and How to choose a good Hash Function](https://www.geeksforgeeks.org/what-are-hash-functions-and-how-to-choose-a-good-hash-function) for a more technical overview of a hash function. +- Watch [This Video](https://www.youtube.com/watch?v=btT4bCOvqjs) from CS50 that explains the concept of hash maps using buckets. + +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [What does it mean to hash?](#what-is-a-hash-code) +- [What are buckets?](#buckets) +- [What is a collision?](#collisions) +- [When is it a good time to grow our table?](#when-do-we-know-that-its-time-to-grow-our-buckets-size) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +- [This discussion goes through the usages of prime numbers](https://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier/299748) +- The [pigeonhole principle](https://en.wikipedia.org/wiki/Pigeonhole_principle) mathematically guarantees collisions when there are more nodes than boxes. diff --git a/content/odin/javascript/computer_science/project_binary_search_trees.md b/content/odin/javascript/computer_science/project_binary_search_trees.md new file mode 100644 index 00000000..c68e6b59 --- /dev/null +++ b/content/odin/javascript/computer_science/project_binary_search_trees.md @@ -0,0 +1,75 @@ +### Introduction + +You have learned about [binary search trees](http://en.wikipedia.org/wiki/Binary_search_tree), where you take a group of data items and turn them into a tree full of nodes, with each left node being "lower" than each right node. The tree starts with the "root node" and any node with no children is called a "leaf node". You have also learned about tree traversal algorithms like breadth-first and depth-first. + +Now, let's take a look at balanced binary search trees (BST). A BST allows fast operations for lookup, insertion, and deletion of data items. Read [this article](https://www.geeksforgeeks.org/sorted-array-to-balanced-bst/) and watch [this video](https://youtu.be/VCTP81Ij-EM) to understand the basic algorithm used to build a balanced BST. Although the last resource does not use JavaScript, you should understand it well enough to develop your own pseudocode. + +### Assignment + +You'll build a balanced BST in this assignment. Do not use duplicate values because they make it more complicated and result in trees that are much harder to balance. Therefore, be sure to always remove duplicate values or check for an existing value before inserting. + +
+ +1. Build a `Node` class/factory. It should have an attribute for the data it stores as well as its left and right children. + +1. Build a `Tree` class/factory which accepts an array when initialized. The `Tree` class should have a `root` attribute, which uses the return value of `buildTree` which you'll write next. + +1. Write a `buildTree(array)` function that takes an array of data (e.g., `[1, 7, 4, 23, 8, 9, 4, 3, 5, 7, 9, 67, 6345, 324]`) and turns it into a balanced binary tree full of `Node` objects appropriately placed (don't forget to sort and remove duplicates!). The `buildTree` function should return the level-0 root node. + + **Tip:** If you would like to visualize your binary search tree, here is a `prettyPrint()` function that will `console.log` your tree in a structured format. This function will expect to receive the root of your tree as the value for the `node` parameter. + + ```javascript + const prettyPrint = (node, prefix = "", isLeft = true) => { + if (node === null) { + return; + } + if (node.right !== null) { + prettyPrint(node.right, `${prefix}${isLeft ? "│ " : " "}`, false); + } + console.log(`${prefix}${isLeft ? "└── " : "┌── "}${node.data}`); + if (node.left !== null) { + prettyPrint(node.left, `${prefix}${isLeft ? " " : "│ "}`, true); + } + }; + ``` + +1. Write `insert(value)` and `delete(value)` functions that insert/delete the given value. You'll have to deal with several cases for delete, such as when a node has children or not. If you need additional resources, check out these two articles on [inserting](https://www.geeksforgeeks.org/insertion-in-binary-search-tree/?ref=lbp) and [deleting](https://www.geeksforgeeks.org/binary-search-tree-set-2-delete/?ref=lbp), or [this video](https://youtu.be/wcIRPqTR3Kc) with several visual examples. + +
+ You may be tempted to implement these methods using the original input array used to build the tree, but it's important for the efficiency of these operations that you don't do this. If we refer back to the [Big O Cheatsheet](https://www.bigocheatsheet.com/), we'll see that binary search trees can insert/delete in `O(log n)` time, which is a significant performance boost over arrays for the same operations. To get this added efficiency, your implementation of these methods should traverse the tree and manipulate the nodes and their connections. +
+ +1. Write a `find(value)` function that returns the node with the given value. + +1. Write a `levelOrder(callback)` function that accepts an optional callback function as its parameter. `levelOrder` should traverse the tree in breadth-first level order and provide each node as an argument to the callback. As a result, the callback will perform an operation on each node following the order in which they are traversed. `levelOrder` may be implemented using either iteration or recursion (try implementing both!). The method should return an array of values if no callback is given as an argument. **Tip:** You will want to use an array acting as a queue to keep track of all the child nodes that you have yet to traverse and to add new ones to the list (as you saw in the [video](https://www.youtube.com/watch?v=86g8jAQug04)). + +1. Write `inOrder(callback)`, `preOrder(callback)`, and `postOrder(callback)` functions that also accept an optional callback as a parameter. Each of these functions should traverse the tree in their respective depth-first order and yield each node to the provided callback. The functions should return an array of values if no callback is given as an argument. + +1. Write a `height(node)` function that returns the given node's height. Height is defined as the number of edges in the longest path from a given node to a leaf node. + +1. Write a `depth(node)` function that returns the given node's depth. Depth is defined as the number of edges in the path from a given node to the tree's root node. + +1. Write an `isBalanced` function that checks if the tree is balanced. A balanced tree is one where the difference between heights of the left subtree and the right subtree of every node is not more than 1. + +1. Write a `rebalance` function that rebalances an unbalanced tree. **Tip:** You'll want to use a traversal method to provide a new array to the `buildTree` function. + +#### Tie it all together + +Write a driver script that does the following: + +1. Create a binary search tree from an array of random numbers < 100. You can create a function that returns an array of random numbers every time you call it if you wish. +1. Confirm that the tree is balanced by calling `isBalanced`. +1. Print out all elements in level, pre, post, and in order. +1. Unbalance the tree by adding several numbers > 100. +1. Confirm that the tree is unbalanced by calling `isBalanced`. +1. Balance the tree by calling `rebalance`. +1. Confirm that the tree is balanced by calling `isBalanced`. +1. Print out all elements in level, pre, post, and in order. + +
+ +### Additional resources + +This section contains helpful links to related content. It isn't required, so consider it supplemental. + +- Yicheng Gong has some excellent videos that help visualize the call stack when traversing binary search trees: [In-order](https://www.youtube.com/watch?v=4_UDUj1j1KQ&t=1s), [Post-order](https://www.youtube.com/watch?v=4Xo-GtBiQN0), and [Pre-order](https://www.youtube.com/watch?v=8xue-ZBlTKQ&ab_channel=ygongcode) Traversal Algorithms. diff --git a/content/odin/javascript/computer_science/project_hash_map.md b/content/odin/javascript/computer_science/project_hash_map.md new file mode 100644 index 00000000..de2d7071 --- /dev/null +++ b/content/odin/javascript/computer_science/project_hash_map.md @@ -0,0 +1,76 @@ +### Introduction + +You already know the magic behind hash maps, now it's time to write your own implementation! + +#### Limitation + + Before we get started, we need to lay down some ground rules. JavaScript's dynamic nature of array allows us to insert and retrieve indexes that are outside our array size range. Example: if we create an array of size `16` to be our buckets size, nothing stopping us from storing items at index `500`. This defeats the purpose we are trying to demonstrate, so we need to put some self restriction to work around this. + + Use the following snippet whenever you access a bucket through an index. We want to throw an error if we try to access an out of bound index: + +```javascript +if (index < 0 || index >= buckets.length) { + throw new Error("Trying to access index out of bound"); +} +``` + +### Assignment + +
+ + Start by creating a `HashMap` class or factory function. It's up to you which you want to use. Then proceed to create the following methods: + + 1. `hash(key)` takes a key and produces a hash code with it. We did implement a fairly good `hash` function in the previous lesson. As a reminder: + + ```javascript + function hash(key) { + let hashCode = 0; + + const primeNumber = 31; + for (let i = 0; i < key.length; i++) { + hashCode = primeNumber * hashCode + key.charCodeAt(i); + } + + return hashCode; + } + ``` + + You are free to use that, or if you wish, you can conduct your own research. Beware, this is a deep deep rabbit hole. + + Also, there is one edge case with long keys that was not taken into consideration in the function or rather how we applied a modulo `%` operator. JavaScript is unable to hold large numbers precisely. At some point, calculations are going to be inacccurate, which significantly increases the chances of collisions. There are a few ways how we could handle it but we recommend that you apply the modulo operator on *each iteration* instead of outside the loop at the end. In that case, we prevent the output from becoming larger than our bucket's length. + + You might find yourself confusing keys with hash codes while accessing key-value pairs later. We would like to stress that the key is what your `hash` function will take as an input. In a way, we could say that the key is important for us only inside the `hash` function. But we never access a bucket directly with the key. Instead we do so with the hash code. + +
+ Hash maps could accommodate various data types for keys like numbers, strings, objects. But for this project, only handle keys of type strings. +
+ + 1. `set(key, value)` takes two arguments, the first is a key and the second is a value that is assigned to this key. If a key already exists, then the old value is overwritten or we can say that we *update* the key's value (e.g. `Carlos` is our key but it is called twice: once with value `I am the old value.`, and once with value `I am the new value.`. From the logic stated above, `Carlos` should contain only the latter value). + + In the meantime, a collision is when *TWO DIFFERENT* keys sit inside the same bucket, because they generate the same hash code (e.g. `Carlos` and `Carla` are both hashed to `3`, so `3` becomes a location for `Carlos` AND `Carla`. However, we know that it is the collision. It means we should find a way how to resolve it — how to *deal with collisions*, which was mentioned in the previous lesson). + + - Remember to grow your buckets size when it needs to, by calculating if your bucket has reached the `load factor`. Some of the methods in this assignment that are mentioned later could be reused to help you handle that growth logic more easily. So you may want to hold onto implementing your growing functionality just for now. However, the reason why we mention it with `set()` is because it's important to grow buckets exactly when they are being expanded. + + 1. `get(key)` takes one argument as a key and returns the value that is assigned to this key. If a key is not found, return `null`. + + 1. `has(key)` takes a key as an argument and returns `true` or `false` based on whether or not the key is in the hash map. + + 1. `remove(key)` takes a key as an argument. If the given key is in the hash map, it should remove the entry with that key and return `true`. If the key isn't in the hash map, it should return `false`. + + 1. `length()` returns the number of stored keys in the hash map. + + 1. `clear()` removes all entries in the hash map. + + 1. `keys()` returns an array containing all the keys inside the hash map. + + 1. `values()` returns an array containing all the values. + + 1. `entries()` returns an array that contains each `key, value` pair. Example: `[[firstKey, firstValue], [secondKey, secondValue]]` + +Remember that a hash map does not preserve insertion order when you are retrieving your hash map's data. It is normal and expected for keys and values to appear out of the order you inserted them in. + +#### Extra Credit + +- Create a class `HashSet` that behaves the same as a `HashMap` but only contains `keys` with no `values`. + +
diff --git a/content/odin/javascript/computer_science/project_knights_travails.md b/content/odin/javascript/computer_science/project_knights_travails.md new file mode 100644 index 00000000..ec6cc7c9 --- /dev/null +++ b/content/odin/javascript/computer_science/project_knights_travails.md @@ -0,0 +1,45 @@ +### Introduction + +Now you're a pro with DFS and BFS. Let's try using our search algorithms on a real problem. + +For this project, you'll need to use a data structure that's similar (but not identical) to a binary tree. For a summary of a few different examples, reference [this article](https://www.khanacademy.org/computing/computer-science/algorithms/graph-representation/a/describing-graphs). + +A knight in chess can move to any square on the standard 8x8 chess board from any other square on the board, given enough turns (don't believe it? See [this animation](https://cdn.statically.io/gh/TheOdinProject/curriculum/284f0cdc998be7e4751e29e8458323ad5d320303/ruby_programming/computer_science/project_knights_travails/imgs/00.png)). Its basic move is two steps forward and one step to the side or one step forward and two steps to the side. It can face any direction. + +All the possible places you can end up after one move look like this: + +![Knights Travails board](https://cdn.statically.io/gh/TheOdinProject/curriculum/d30038e0aaca1f35e58e205e37a21b2c9d31053d/javascript/computer_science/project_knights_travails/imgs/01.png) +Note: The picture is only to explain the problem, There is no need to create a GUI. + +### Assignment + +Your task is to build a function `knightMoves` that shows the shortest possible way to get from one square to another by outputting all squares the knight will stop on along the way. + +You can think of the board as having 2-dimensional coordinates. Your function would therefore look like: + +- `knightMoves([0,0],[1,2]) == [[0,0],[1,2]]` + +
+Sometimes _there is more than one fastest path_. Examples of this are shown below. Any answer is correct as long as it follows the rules and gives the shortest possible path. + +- `knightMoves([0,0],[3,3]) == [[0,0],[2,1],[3,3]]` or `knightMoves([0,0],[3,3]) == [[0,0],[1,2],[3,3]]` +- `knightMoves([3,3],[0,0]) == [[3,3],[2,1],[0,0]]` or `knightMoves([3,3],[0,0]) == [[3,3],[1,2],[0,0]]` +- `knightMoves([0,0],[7,7]) == [[0,0],[2,1],[4,2],[6,3],[4,4],[6,5],[7,7]]` or `knightMoves([0,0],[7,7]) == [[0,0],[2,1],[4,2],[6,3],[7,5],[5,6],[7,7]]` +
+ +
+1. Think about the rules of the board and knight, and make sure to follow them. +2. For every square there is a number of possible moves, choose a data structure that will allow you to work with them. Don't allow any moves to go off the board. +3. Decide which search algorithm is best to use for this case. Hint: one of them could be a potentially infinite series. +3. Use the chosen search algorithm to find the shortest path between the starting square (or node) and the ending square. Output what that full path looks like, e.g.: + +```bash + > knightMoves([3,3],[4,3]) + => You made it in 3 moves! Here's your path: + [3,3] + [4,5] + [2,4] + [4,3] +``` + +
diff --git a/content/odin/javascript/computer_science/project_linked_lists.md b/content/odin/javascript/computer_science/project_linked_lists.md new file mode 100644 index 00000000..ad19223d --- /dev/null +++ b/content/odin/javascript/computer_science/project_linked_lists.md @@ -0,0 +1,66 @@ +### Introduction + +In Computer Science, one of the most basic and fundamental data structures is the +linked list, which functions similarly to an array. The principal benefit of a linked +list over a conventional array is that the list elements can easily be inserted or +removed without reallocation of any other elements. + +In some programming languages, the size of an array is a concern and one of the ways +to overcome that problem and allow dynamically allocated data is using linked lists. + +Luckily in **JavaScript**, arrays aren't limited to a certain size, and both insertion and deletion can be done trivially at any index using the appropriate built in array method, so you don't have to think about overcoming those limitations. + +So if array size, array insertion and array deletion are not limitations in JavaScript, are linked lists really necessary? +The short answer to that is *no*; however, it's the simplest of the dynamic data +structures and it will give you a solid foundation, so you can understand more +complex data structures like graphs and binary trees with more ease. + +### Structure of a linked list + +A *linked list* is a linear collection of data elements called nodes that "point" +to the next node by means of a pointer. + +Each node holds a single element of data and a link or pointer to the next node in the list. + +A head node is the first node in the list, a tail node is the last node in the list. Below is a basic representation of a linked list: + +`[ NODE(head) ] -> [ NODE ] -> [ NODE(tail) ] -> null` + +For a more thorough explanation, use these resources: + +1. [Linked Lists in Plain English](https://www.youtube.com/watch?v=oiW79L8VYXk) +1. [What's a Linked List, Anyway?](https://dev.to/vaidehijoshi/whats-a-linked-list-anyway) +1. [A more verbose explanation with plenty of diagrams](https://web.archive.org/web/20200217010131/http://www.cs.cmu.edu/~adamchik/15-121/lectures/Linked%20Lists/linked%20lists.html) + +### Assignment + +
+ +If you wish to use multiple ES6 modules, remember that Node uses CommonJS modules by default and so you must [tell Node to use ES6 modules](https://blog.logrocket.com/commonjs-vs-es-modules-node-js/) instead. + +You will need two classes or factories: + +1. `LinkedList` class / factory, which will represent the full list. +1. `Node` class / factory, containing a `value` property and a link to the `nextNode`, set both as `null` by default. + +Build the following functions in your linked list class / factory: + +1. `append(value)` adds a new node containing `value` to the end of the list +1. `prepend(value)` adds a new node containing `value` to the start of the list +1. `size` returns the total number of nodes in the list +1. `head` returns the first node in the list +1. `tail` returns the last node in the list +1. `at(index)` returns the node at the given `index` +1. `pop` removes the last element from the list +1. `contains(value)` returns true if the passed in value is in the list and otherwise returns false. +1. `find(value)` returns the index of the node containing value, or null if not found. +1. `toString` represents your LinkedList objects as strings, so you can print them out and preview them in the console. + The format should be: `( value ) -> ( value ) -> ( value ) -> null` + +### Extra credit + +1. `insertAt(value, index)` that inserts a new node with the provided `value` at the given `index`. +1. `removeAt(index)` that removes the node at the given `index`. + +**Extra Credit Tip:** When you insert or remove a node, consider how it will affect the existing nodes. Some of the nodes will need their `nextNode` link updated. +
diff --git a/content/odin/javascript/computer_science/project_recursion.md b/content/odin/javascript/computer_science/project_recursion.md new file mode 100644 index 00000000..42675c81 --- /dev/null +++ b/content/odin/javascript/computer_science/project_recursion.md @@ -0,0 +1,51 @@ +### Warmup: Fibonacci + +The [Fibonacci Sequence](http://en.wikipedia.org/wiki/Fibonacci_number), which sums each number with the one before it, is a great example of a problem that can be solved recursively. + +### Assignment 1 + +
+ 1. Using iteration, write a function `fibs` which takes a number and returns an array containing that many numbers from the Fibonacci sequence. Using an example input of `8`, this function should return the array `[0, 1, 1, 2, 3, 5, 8, 13]`. + 1. Now write another function `fibsRec` which solves the same problem recursively. +
+ +In order to run these functions you'll need to run it from somewhere. You can run scripts from the command line using the node command installed with nodejs. You can read about the common ways to do this [here](https://github.com/nodejs/nodejs.dev/blob/aa4239e87a5adc992fdb709c20aebb5f6da77f86/content/learn/command-line/node-run-cli.en.md). + +#### Understanding recursive Fibonacci + +Did you figure it out? Congratulations! But do you really understand what is taking place? If you need some help understanding what's going on with this function, give [Khan Academy's Stepping Through Recursive Fibonacci Function video](https://www.youtube.com/watch?v=zg-ddPbzcKM) a watch. If you prefer to read, [Recursive Fibonacci Explained](https://www.scaler.com/topics/fibonacci-series-in-javascript/) is also very helpful! + +### Project: merge sort + +Sorting algorithms are a great way to get to grips with recursion. One such algorithm is [Merge Sort](http://en.wikipedia.org/wiki/Merge_sort), a type of sort that lends itself well to recursion and can be much faster than other algorithms such as bubble sort on the right data sets. You'll build a function which sorts a given array but uses a "merge sort" function for doing so. + +It can be a bit strange to wrap your head around, but just remember you're "dividing and conquering" the problem. +
+ The Wikipedia article above uses merge sort examples written in C-like code. This code will likely look quite different from what you're used to. Don't waste time trying to understand the specifics of the language, and just focus on the idea it is trying to get across. +
+ +### Background viewing + +The first step is to actually understand what the merge sort algorithm is doing: + +1. Check out this [introductory video from Harvard's CS50x course](https://youtu.be/Ns7tGNbtvV4). +1. Check out this more [detailed video explanation by David J. Malan](https://youtu.be/4oqjcKenCH8?t=6248) (watch only until 1:58:33). +1. [The concept of merging](https://youtu.be/6pV2IF0fgKY) and [Merge Sort -- How it Works part](https://youtu.be/mB5HXBb_HY8) on YouTube give you a more formal look at this problem if you're still unclear. +1. (Optional) Play with this [Merge Sort Visualizer](https://www.hackerearth.com/practice/algorithms/sorting/merge-sort/visualize/) to get a better feel for exactly what is happening during a Merge Sort. + +### Assignment 2 + +
+ 1. Build a function `mergeSort` that takes in an array and returns a sorted array, using a recursive merge sort methodology. An input of `[3, 2, 1, 13, 8, 5, 0, 1]` should return `[0, 1, 1, 2, 3, 5, 8, 13]`, and an input of `[105, 79, 100, 110]` should return `[79, 100, 105, 110]`. + 2. Tips: + 1. Think about what the base case is and what behavior is happening again and again and can actually be delegated to someone else (e.g. that same function!). + 2. It may be helpful to check out the background videos again if you don't quite understand what should be going on. +
+ +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +- Another look at [merge sort](http://www.sorting-algorithms.com/merge-sort) +- Visualize and [understand](https://www.educative.io/courses/recursion-for-coding-interviews-in-javascript/NEZ7kKgMJKK) the memory allocation for recursive functions +- For more attempts at recursion try the first 5 problems in [Project Euler](https://projecteuler.net/problems) diff --git a/content/odin/javascript/computer_science/recursive_methods.md b/content/odin/javascript/computer_science/recursive_methods.md new file mode 100644 index 00000000..eaadb3a5 --- /dev/null +++ b/content/odin/javascript/computer_science/recursive_methods.md @@ -0,0 +1,61 @@ +### Introduction + +Recursion is the idea that a function calls itself. That is all there is to it. It's used to take a big problem and start breaking it down into smaller and smaller pieces ("Divide and Conquer") and continuing to feed their solutions back into the original function until some sort of answer is achieved and the whole chain unwinds. + +From the [Wikipedia entry on Divide and Conquer Algorithms](http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm): + +> In computer science, divide and conquer (D&C) is an important algorithm design paradigm based on multi-branched recursion. A divide and conquer algorithm works by recursively breaking down a problem into two or more sub-problems of the same (or related) type, until these become simple enough to be solved directly. The solutions to the sub-problems are then combined to give a solution to the original problem. + +There's also a right and wrong way to use recursion. The fact is, any problem you can solve recursively, you can also solve using the iterators that you know and love. If you find yourself saying "why didn't I just use a `while` loop here?" then you probably should have. You won't often end up using a recursive solution to a problem, but you should get a feel for when it might be a good idea. Some problems also break down into far too many pieces and totally overwhelm your computer's memory. There's a balance. + +In this brief lesson, you'll get a chance to learn more about when and how to use recursion and then in the next project you will get the chance to apply some of that (since it probably won't really stick until you've had a chance to try it). + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Why is recursion a useful technique for solving a big problem? +- What are the limitations of using recursive solutions? +- What types of problems are more suited for loops than recursion? +- What is meant by "recursive depth"? +- What is a "stack overflow" (the concept, not the website)? +- Why is that relevant to a recursive problem? + +### Assignment + +
+ 1. Read [this recursion resource](https://javascript.info/recursion) for a good intro to recursion. + 2. Watch [this explanation of recursion](https://www.youtube.com/watch?v=6oDQaB2one8) by Web Dev Simplified and [this additional example of recursion](https://youtu.be/LteNqj4DFD8?t=340) by DevSage. + 3. Watch this [Video on Recursion](https://www.youtube.com/watch?v=mz6tAJMVmfM) from CS50. + 4. Read the ["Implementation Issues" section of the wiki article](http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm#Implementation_issues) to get an overview of some of the limitations of recursion. +
+ +### Test yourself + +
+ 1. Solve each of the questions from this [Code Quiz](https://www.codingame.com/playgrounds/5422/js-interview-prep-recursion) on Recursion. It is not important to have recursive algorithms committed to memory at this point; just understand how to create and use them. + +
+The solution for "Question 6: Search JS object" is incorrect. See the [corrected solution](https://gist.github.com/JoshDevHub/b00125f483d4a1ecc257eaa030916973) after you solve it. +
+ +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [How would you briefly define a recursive function?](#introduction) +- [What is the point of recursion? Is it more efficient than using a plain loop?](http://ruby.bastardsbook.com/chapters/recursion/) +- [What are the 2 essential parts in a recursive function?](https://youtu.be/mz6tAJMVmfM?t=193) +- [Why is "stack overflow" relevant to a recursive problem?](https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm#Stack_size) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +- [Efficient Recursion from U of Alberta](http://webdocs.cs.ualberta.ca/~holte/T26/efficient-rec.html) +- [A good resource of recursion by example](https://www.javascripttutorial.net/javascript-recursive-function/) +- [Visualize how recursion works on factorials](https://pythontutor.com/render.html#code=function%20calcFactorial%28num%29%20%7B%0A%20%20%20%20if%20%28num%20%3D%3D%3D%201%29%20%7B%0A%20%20%20%20%20%20%20%20return%201%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20num%20*%20calcFactorial%28num%20-%201%29%3B%0A%7D%0A%0AcalcFactorial%285%29%3B&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=js&rawInputLstJSON=%5B%5D&textReferences=false) +- [You may want to watch this nice freecodecamp course. It explains the logic of recursion very well.](https://www.youtube.com/watch?v=IJDJ0kBx2LM&t=2333s) +- [This video presents a few interesting problems with recursion](https://www.youtube.com/watch?v=ngCos392W4w) diff --git a/content/odin/javascript/computer_science/space_complexity.md b/content/odin/javascript/computer_science/space_complexity.md new file mode 100644 index 00000000..546e7a30 --- /dev/null +++ b/content/odin/javascript/computer_science/space_complexity.md @@ -0,0 +1,155 @@ +### Introduction + +In the last lesson, we focused on measuring complexity from the perspective of time. We learned about the various ways in which algorithm complexity can be measured and why Big O was the preferred way. We also showed some examples of how this applied to measuring the time complexity of an algorithm. + +In this lesson, we'll focus on space complexity and see how the same notations we've already learned can be used to measure how a change in input for our algorithms can affect the amount of memory it uses. + +When we talk about memory, we mean primary memory, which is the working memory available to your system to execute algorithms. You can read more about the topic in this [GeeksforGeeks Primary Memory article](https://www.geeksforgeeks.org/primary-memory/). + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- What do we mean by space complexity. +- Why is it important to consider how our algorithm uses memory space. +- How do we measure space complexity. + +### What do we mean by space complexity? + +Space complexity can be considered to be the total space used by an algorithm relative to the size of the input. Thinking back to the previous lesson, you'll recall that we don't consider the efficiency of an algorithm in how it performs in one specific instance of that algorithm running. Instead, we want to know how the efficiency changes when the size of the input changes. + +Measuring space complexity considers the space used by your algorithm input and auxiliary space. Auxiliary space is the extra space used by the algorithm. These can be things like temporary variables created during the execution of the algorithm. They won't have a lasting impact on memory space but during the execution of the algorithm will need to be considered. Therefore, you can consider the space complexity to be the total amount of working memory our algorithm needs. + +### The importance of considering space complexity + +If you do any of your own research into algorithm efficiency, you'd be forgiven for assuming that space complexity isn't all that important. Most articles you'll read on the subject spend all or the majority of the time covering time complexity and if they do mention space complexity at all, it's pretty much a footnote. + +There are some fair arguments for why. Memory these days is pretty cheap compared to processing power, and therefore it's often easier to get around space constraints by increasing primary memory, for example by buying more [RAM](https://en.wikipedia.org/wiki/Random-access_memory). It's also fair to say that most algorithms you write will probably deal with very manageable input sizes, and therefore space doesn't really become a problem. You'll invariably run into an issue with your program being slow before you have any issues with memory being used up. + +On the flip side, although memory is cheap, your hardware will usually have a fixed amount. You can't expand memory easily on the fly in most scenarios. When the problem is one of time, you can just allow the program to run for longer and it will eventually come back with a result. You can't do this with space. + +On balance, you'll probably come across problems in which the time it takes to execute is more important than the space it uses, but knowing about measuring space complexity will mean when you do run into a situation where there are space constraints, you'll be prepared to handle it. + +### Measuring space complexity + +The good news is that we measure space complexity in exactly the same way as time complexity. You already learned about Big O in the last lesson, so you already know how to measure the efficiency of your code. The difference is that you'll need to think about how your algorithm is utilizing memory rather than time. + +The first thing to know is that, like time complexity, we measure space complexity by considering all steps including any constants, and then we drop the constants when applying a Big O Notation to the algorithm. So we may have an algorithm that uses memory in Linear Complexity as the input changes, and in doing so creates 3 temporary variables. So we can think of the complexity of our algorithm as O(N) + 3 auxiliary variables using memory. Because those 3 variables are the same no matter our input size, we don't concern ourselves with them when considering the space complexity of our algorithm. So we'd say the space complexity is O(N). This should be familiar to you from the time complexity lesson. + +As a reminder the Big O Notations are: + +- O(1) - Constant Complexity +- O(log N) - Logarithmic Complexity +- O(N) - Linear Complexity +- O(N log N) - N x log N Complexity +- O(n²) - Quadratic Complexity +- O(n³) - Cubic Complexity +- O(2ⁿ) - Exponential Complexity +- O(N!) - Factorial Complexity + +Let's work through some examples. We won't go through every possible complexity because most don't apply for the data structures you'll be familiar with and use the most. We'll cover the most common ones. + +#### O(1) - Constant complexity + +Consider this example + +```js +function multiply(num1, num2) { + return num1 * num2; +} +``` + +Here it should hopefully be clear that no matter the arguments we pass to the function call, only a single value is created (the product of the numbers). It doesn't change. Therefore, we can consider the space this takes is always O(1). + +#### O(N) - Linear complexity + +Most data structures you come across will have a space complexity of O(N). That makes sense - when you increase the number of items in your data structure, it increases the space that data structure occupies in a linear way. + +```js +function sumArr(arr) { + const copyArr = arr.slice(); + let sum = 0; + copyArr.forEach((number) => { + sum += number; + }); + return sum; +} +``` + +We wrote this in a slightly more verbose way than you'd normally write it in JavaScript to make it a little clearer. Here we have a method which accepts an array. Within, we have two variables. One called `sum` and the other `copyArr` which holds a copy of the array passed in. We then have a `forEach` loop that iterates over the array. The amount of space that this algorithm takes depends on the array that is passed to it. It could be 3 elements in the array or 300. When we don't know the length of the array, we refer to it as N, so we have N + 1 variable called `sum`. We know that we drop constants with Big O, so we are left with N, or O(N) for its Big O notation. + +Why did we make a copy of the array? That will be discussed in a later section. + +The complexity is replicated no matter the data structure: + +```js +function sumObjectValues(obj) { + const copyObject = { ...obj }; + let sum = 0; + Object.values(copyObject).forEach((value) => { + sum += value + }); + return sum; +} +``` + +Here as the object size increases, the space it uses grows in a linear way. + +#### Other complexities + +As we've stated, many data structures share O(N) space complexity, and therefore you won't write many algorithms with a space complexity that differs. + +You do find some recursive functions that may have a different space complexity and some sorting algorithms. You normally won't have much reason to consider anything else though. + +In the last lesson one of the assignments was a link to the [Big-O cheat sheet](https://www.bigocheatsheet.com/). If you take another look at it now, you may have a better appreciation for just how amazing it is as a reference for space and time complexity. If you scroll down to the data structures and then the sorting algorithms section, you'll see it gives you the time and space complexities. Notice just how many are O(N), especially for data structures. Many sorting algorithms have just O(1) space complexity, something to keep in mind as you come across different sorting algorithms during your learning. + +That's why we won't be diving into examples for other Big O notations with space complexity. We'd have to come up with convoluted examples that wouldn't represent most code you'll write. If you do come across a good real world example in your own code, then do let us know and we may consider adding it here for others to consider. + +#### Other considerations + +One of the common areas that causes confusion when considering space complexity is what constitutes using space in the context of an algorithm. In an earlier example we wrote methods that duplicated an array and object argument. We did that to be explicit. But what if we'd written the method as: + +```js +function sumArr(arr) { + let sum = 0; + arr.forEach((number) => { + sum += number; + }); + return sum; +} +``` + +When a data structure is passed in as the argument, especially for languages that pass arrays by reference rather than value, it can be a bit unclear if that method considers the space used by that data structure when calculating its space complexity. If we didn't count it, then it would be easy for all our methods to have great space usage on paper because we put the onus on the caller to allocate that space. If we did count it, but the data structure was created for use by many different methods, then the space complexity for all those methods is O(N) when they aren't utilizing additional space. Then consider that if your method receives an array as an input and loops it, an index must be created for the loop which uses additional space. + +The first answer to [analyzing space complexity](https://cs.stackexchange.com/questions/127933/analyzing-space-complexity-of-passing-data-to-function-by-reference) provides some great context to the question and gives some thought-provoking answers. + +Ultimately when you consider Big O measures the worst-case scenario, it would be easier to err on the side of caution and do consider the space of arguments passed to your method. + +### Wrapping up + +Measuring the complexity of your algorithms, whether time or space, can be difficult. It takes practice and consideration. For most practice code you write, it's not something that will cross your mind, especially as you wrestle with getting your code to work. + +Once your code is working though, and you might be looking to refactor it, it's definitely worth taking a moment to consider if the code is as efficient as it could be. Are you creating unnecessary variables? Or does your algorithm use a data structure with a worse time complexity for what it's mostly used for than another data structure would have been? + +On top of these considerations, you also need to balance the readability of your code. If you start introducing [memoization](https://en.wikipedia.org/wiki/Memoization) in order to make your code more efficient, does it mean it's much harder to understand? Is that trade-off worth it? Ultimately, you need to make a call on it. Our advice would be to consider the readability first, and look to refactor for better efficiency if there is a clear impact on performance. + +### Assignment + +
+ +1. Read [this article on big O and space complexity](https://dev.to/mwong068/big-o-space-complexity-lcm). It isn't detail heavy but does a good job explaining things clearly, and does lightly cover recursive functions. The code examples are in Ruby, but you should be able to follow along. +1. [This article on recursion and space complexity](https://dev.to/elmarshall/recursion-and-space-complexity-13gc) offers a little more context to recursive functions and their space complexity. + +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [What is space complexity?](#what-do-we-mean-by-space-complexity) +- [How do we measure space complexity?](#measuring-space-complexity) +- [What are the main considerations we should take into account when optimising code?](#other-considerations) + +### Additional resources + +This section contains helpful links to related content. It isn’t required, so consider it supplemental. diff --git a/content/odin/javascript/computer_science/time_complexity.md b/content/odin/javascript/computer_science/time_complexity.md new file mode 100644 index 00000000..2c9381f7 --- /dev/null +++ b/content/odin/javascript/computer_science/time_complexity.md @@ -0,0 +1,336 @@ +### Introduction + +You've written a lot of code up to this point, and you've hopefully moved on from just trying to write code that works, to now considering code readability and maintainability. You might spend some time considering how you can create the necessary abstractions so that your code stays easy to work with even as the requirements for it grows. + +Code readability and maintainability are super important. After all, you will likely spend as much, if not more, time reading code than writing it. You need to make sure new features are integrated with ease. + +However, there is another consideration that can be just as important when writing code. Efficiency! You need to understand how the code you write will perform. You also need to understand how the choices you make impact performance so that you can choose the right data structure and algorithm for your requirement. + +In programming, there are two ways we can measure the efficiency of our code. We can measure the time complexity or the space complexity. + +In this lesson, we'll introduce the core concepts around measuring the time efficiency of the code you write. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- How the efficiency of an algorithm is measured. +- What is Big O. +- What are the Big O notations used to measure an algorithm's efficiency. +- How else can we measure an algorithm's efficiency. +- What to do when two algorithms have the same complexity. + +### Efficiency basics + +The very first step in mastering efficient code is to understand how to measure it. Let's take a look at a little program that prints out all odd numbers between 1 and 10. + +```js +function oddNumbersLessThanTen() { + let currentNumber = 1; + + while (currentNumber < 10) { + if (currentNumber % 2 !== 0) { + console.log(currentNumber); + } + + currentNumber += 1; + } +} +``` + +If you were to run this in your terminal, you should get the numbers `1`, `3`, `5`, `7` and `9` printed to the console. It probably took a fraction of a second to run. If you were to run it again, it might take the same time, or it might be faster or slower depending on what else your computer is doing. If you were to run it on a different computer, it would again run faster or slower. Therefore it's important to understand that you never measure the efficiency of an algorithm by how long it takes to execute. + +So how do we measure it? + +The way to measure code efficiency is to evaluate how many 'steps' it takes to complete. If you know that one algorithm you write takes 5 steps and another one takes 20 steps to accomplish the same task, then you can say that the 5-step algorithm will always run faster than the 20-step algorithm on the same computer. + +Let's go back to our `oddNumbersLessThanTen function`. How many steps does our algorithm take? + +1. We assign the number 1 to a variable. That's one step. + +2. We have a loop. For each iteration of the loop, we do the following: + + 1. Compare `currentNumber` to see if it is less than 10. That is 1 step. + 2. We then check if currentNumber is odd. That is 1 step. + 3. If it is then we output it to the terminal. That's 1 step every 2 iterations. + 4. We increase `currentNumber` by 1. That is 1 step. + +3. To exit the loop, we need to compare `currentNumber` one last time to see that it is not less than ten any more. That is one last step. + +So there are 3 steps for every loop iteration and it iterates 9 times which is 27 steps. Then we have one step which iterates for only half the loop iteration which is 5 steps. Assigning an initial value to `currentNumber` and checking the exit condition of the loop is one step each. 27 + 5 + 1 + 1 = 34 steps. + +Therefore, we can say our algorithm takes 34 steps to complete. + +While this is useful to know, it isn't actually helpful for comparing algorithms. To see why, let's slightly modify our initial algorithm to take in a number instead of setting a hard default of 10. + +```js +function oddNumbers(maxNumber) { + let currentNumber = 1; + + while (currentNumber < maxNumber) { + if (currentNumber % 2 !== 0) { + console.log(currentNumber); + } + + currentNumber += 1; + } +} +``` + +How many steps does this algorithm take? + +You've probably realised the answer is it depends. If you set `maxNumber` to be 10, like we did before, the number of steps is 34, but if you enter another number then the number of steps changes. There is no concrete number we can use to measure the efficiency of our code because it changes based on an external input. + +So what we really want to be able to measure is how the number of steps of our algorithm changes when the data changes. This helps us answer the question of whether the code we write will scale. + +To do that, we need to delve into a new concept: Asymptotic Notations and, in particular, Big O. + +### Asymptotic notations + +Asymptotic Notations are used to describe the running time of an algorithm. Because an algorithm's running time can differ depending on the input, there are several notations that measure that running time in different ways. The 3 most common are as follows: + +- Big O Notation - represents the upper bound of an algorithm. This means the worst-case scenario for how the algorithm will perform. +- Omega Notation - represents the lower bound of an algorithm. This is the best-case scenario. +- Theta Notation - represents both the upper bound and lower bound and therefore analyses the average case complexity of an algorithm. + +Big O is the one you'll most commonly see referenced because you need to be sure the worst-case scenario for any code you write is scalable as the inputs grow in your application. + +It's also worth noting that the Notations given below for Big O also apply to Omega and Theta notations. The differences are in how they look to measure the efficiency of the algorithm and therefore which Notation should apply. This should become clearer as you read on. + +### What is Big O? + +Big O gives us a consistent way to measure the efficiency of an algorithm. It gives us a measurement for the time it takes for an algorithm to run as the input grows so that you can directly compare the performance of two algorithms and pick the best one. + +Big O is not a piece of code you can put your algorithm into and it tells you how efficient it is. You will need to measure how the number of steps changes as the data grows, and using this you can apply a Big O Notation to it and measure it against other algorithms. In many cases you'll be using a data structure in which the ways you interact with it are well known, and in that case it's easier to judge how it will scale as the input changes. + +Firstly, we'll summarise the Big O Notations and then provide a little more context for each one. The reading materials will dive into greater detail. + +#### Big O notation + +The Big O Notations in the order of speed from fastest to slowest are: + +- O(1) - Constant Complexity +- O(log N) - Logarithmic Complexity +- O(N) - Linear Complexity +- O(N log N) - N x log N Complexity +- O(n²) - Quadratic Complexity +- O(n³) - Cubic Complexity +- O(2ⁿ) - Exponential Complexity +- O(N!) - Factorial Complexity + +#### O(1) - Constant complexity + +To understand Constant Complexity, let's use an array. + +```js +arr = [1, 2, 3, 4, 5]; +``` + +If we want to look up what is at index 2, we can get to the element using `arr[2]` which would give us back `3`. This takes just one step. If we double our array... + +```js +arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +``` + +We can still access any element in just one step. `arr[7]` gives us `8` in a single step. Our array can keep growing and we can always access any element in a single step. It's constant. Hence we have `O(1)`. + +Looking up something in one step is as good as it gets for time complexity. + +While we're looking at the simplest form of Big O, let's take a look at one of its little gotchas to keep in mind. You may have thought a moment ago, is it really just one step? The answer is technically no, in reality the computer must first look up where the array is in memory, then from the first element in the array it needs to jump to the index argument provided. That's at least a couple of steps. So you wouldn't be wrong for writing something like `O(1 + 2(steps))`. However, the 2 steps are merely incidental. With an array of 10,000 elements, it still takes the same amount of steps as if the array was 2 elements. Because of this, Big O doesn't concern itself with these incidental numbers. They don't provide any context to how the complexity grows when the data size changes, because they are constant, and so in Big O they are dropped. Big O only wants to tell us an algorithm's complexity relative to the size of the input. + +Do the number of steps matter? Yes, they might. We'll touch on when this may be the case a little later. + +#### O(log N) - Logarithmic complexity + +Logarithmic Complexity tells us that the numbers of steps an algorithm takes increases by 1 as the data doubles. That's still pretty efficient when you think about it. Going from 5,000 to 10,000 data elements and only taking one additional step can scale really well. + +One such algorithm that does this is Binary Search. It only works on sorted arrays, but if you have an array of 10 items in sorted order + +```js +arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +``` + +and wanted to know if it had the number `7`, Binary Search would guess the middle item of the array and see what is there. Because the array is sorted, if the number at the middle index was `6`, then we know anything to the left of that index cannot be the number 7, as those items must be lower than 6 in a sorted array. + +```js +arr = [-, -, -, -, -, 6, 7, 8, 9, 10] +``` + +Therefore in just one step, we've eliminated half of the array. We can do the same with the remaining half. We can guess the middle index and see if it's 7. Half of that (half of an array) array eliminated again. In this case, the middle index would be 8, and we know that 7 is less than 8 so we can eliminate anything to the right of the number 8. + +```js +arr = [6, 7, 8, -, -] +``` + +We can keep doing this until we have an array of just one item. If it matches the number we're looking for, we've found it. If not, then it isn't in the array. + +The below table summarises the size of an array doubling and how many steps in Big O terms we would need to arrive at one element to see if it matches what we're looking for: + +| Size | Steps | +| ---- | ----- | +| 1 | 1 | +| 2 | 2 | +| 4 | 3 | +| 8 | 4 | +| 16 | 5 | +| 32 | 6 | + +Pretty impressive eh! + +#### O(N) - Linear complexity + +This one is pretty easy to wrap your head around. Linear Complexity just tells us that as the number of items grows, the number of steps grows at exactly the same rate. Every time you iterate over an array is an example of Linear Complexity. If you have an array of 5 items, then we can iterate every element in 5 steps. An array of 10 items can be iterated in 10 steps. If you come across any algorithm with a Big O efficiency of `O(N)`, you know that the number of steps will increase in line with the number of elements in your data structure. + +#### O(N log N) - N x log N complexity + +You can't say this one isn't appropriately named. This notation usually implies we have an algorithm which initially is `O(log N)` such as our example earlier of Binary Search where it repeatedly breaks an array in half, but with `O(N log N)` each of those array halves is processed by another algorithm with a complexity of `O(N)`. + +One such algorithm is the merge sort algorithm from our previous lesson. :) + +However, not all `O(N log N)` situations are built this way. There are special cases, like constructing a [Cartesian tree](https://www.geeksforgeeks.org/cartesian-tree/), where the algorithm naturally behaves like `O(N log N)` without using smaller parts with `O(N)` or `O(log N)` complexities inside. The keen amongst you may wish to have a peek at how this algorithm works. This shows that while nested complexities can be common, they're not the only way an algorithm can achieve a particular time complexity. + +#### O(n²) - Quadratic complexity + +You've probably written code with a Quadratic Complexity on your programming journey. It's commonly seen when you loop over a data set and within each loop you loop over it again. +For example, if our array has 3 items, the nested loops require 3² = 9 sub-steps. Adding just one more item to the array almost doubles this number to 4² = 16. Adding a 5th item takes us to 5² = 25 sub-steps. Then doubling the array size to 10 items increases the sub-steps from 25 to 100, so 4 times as much work needed! +We hope you can see where we're going with this... + +#### O(n³) - Cubic complexity + +Think triple nested loops baby. If looping over an array with n items, 1 extra item adds an extra outer loop, an extra middle loop, and an extra innermost loop. When using such triply nested loops on an array of size n, we require a total of n³ sub-steps. +For example, if our array has 3 items, the triply-nested loops require a total of 3³ = 27 sub-steps. Adding one more item more than doubles this number to 4³ = 64 sub-steps. The task almost doubles again for 5 items, with 5³ = 125 sub-steps. Doubling our array size to 10 items means we require 10³ = 1000 sub-steps in total, 8 times as many as before! 100 items in the array require a total of 1,000,000 sub-steps. Ouch! + +#### O(2ⁿ) - Exponential complexity + +Exponential Complexity means that with each item added to the data size, the number of steps doubles from the previous number of steps. Let's provide a little table to see how quickly this can get out of hand. + +| Size | Steps | +| ---- | ----- | +| 1 | 2 | +| 2 | 4 | +| 3 | 8 | +| 4 | 16 | +| 5 | 32 | +| 6 | 64 | +| 7 | 128 | +| 8 | 256 | +| 9 | 512 | +| 10 | 1024 | + +You want to avoid this if at all possible, otherwise you won't be processing much data quickly. + +#### O(N!) - Factorial complexity + +A factorial is the product of the sequence of _n_ integers. The factorial of 4(4!) is 4 * 3 * 2 * 1. + +You will come across Factorial Complexity if you ever need to calculate permutations or combinations. If you have an array and have to work out all the combinations you can make from the array, that is a Factorial complexity. It's manageable for a small number of items, but the leap with each new item in a dataset can be huge. + +The factorial of 3 is 6 (3 * 2 * 1). The factorial of 4 is 24. The factorial of 10? 3,628,800. So you can see how quickly things can get out of hand. + +### Alternatives to Big O + +If Big O gives us the worst-case scenario of how our algorithm will scale, what alternatives are there? + +#### Big Ω (Omega notation) + +Omega Notations gives us the best-case scenario for an algorithm. To understand where this might be, let's look at a method and discuss how we can measure its complexity. + +```js +function findValue(arr) { + for (let i = 0; i < arr.length; i++) { + let item = arr[i]; + if (item === 1) { + return item; + } + } +} +``` + +In the worst case (Big O), which would happen if the item is not in the array, we would say it had linear complexity `O(N)`. This is because the `item` we are looking for is not in the array, so our code must iterate on every value. If the array input doubles in size then the worst case also means our method must double the number of iterations looking for the `item`. + +However, in the best-case scenario the value we are looking for will be the first item in the array. In this case our algorithm takes just one step. This has a complexity of `O(1)`. This is its Omega Complexity. + +Omega Notation isn't considered as useful because it is unlikely our item will often be the first item in our data structure search, so it doesn't give us any idea how well the algorithm will scale. + +#### Big-Θ (Big-Theta notation) + +While Omega Notation measures the best-case scenario for an algorithm's efficiency, and Big O measures the worst case, Theta looks to give the exact value or a useful range between narrow upper and lower bounds. + +If we had some code that looped every item in an array, then it doesn't matter the size of the array. Our algorithm will always run in `O(N)` time in its best-case and worst-case scenarios. In that case we know it's exact performance in all scenarios is `O(N)`, and that is the Theta performance of our algorithm. For other algorithms, Theta may represent both the lower and upper bound of an algorithm that has different complexities. We won't get into this more here because Big O is the primary notation used for general algorithm time complexity. + +This is just a simplistic explanation to try to make the topic approachable. If you do happen to be mathematically minded, then you'll find more detailed explanations with a quick search online. + +### Why Big O + +Now that we've touched on the different ways of quantifying an algorithm's efficiency, hopefully it's clear why we choose to use the worst-case scenario when measuring the efficiency of that algorithm. + +Using a worst-case scenario we can make sure our algorithm will scale in all outcomes. If we write an algorithm that could potentially run in constant time, but could also run in linear time in the worst case, it can only scale as the input grows if it still works when the worst case does happen. You need to be confident your code won't lock up and leave users frustrated if you suddenly get an input of a million items instead of 10. + +### Algorithms with the same complexity + +If we write two algorithms with the same complexity, does that mean they're equally good to use? We'll answer this question with two code examples which we'll then discuss a bit further to try and answer the question. + +The first example is some code we've seen already, our `oddNumbers` function. + +```js +function oddNumbers(maxNumber) { + let currentNumber = 1; + + while (currentNumber < maxNumber) { + if (currentNumber % 2 !== 0) { + console.log(currentNumber); + } + + currentNumber += 1; + } +} +``` + +The time complexity of this algorithm is `O(N)`. As the data size increases, the number of steps of our algorithm increases at the same rate. + +Let's look at another version: + +```js +function oddNumbers(maxNumber) { + let currentNumber = 1; + + while (currentNumber < maxNumber) { + if (currentNumber % 2 !== 0) { + console.log(currentNumber); + } + + currentNumber += 2; + } +} +``` + +Not much of a change, but this time we increase `currentNumber` by 2. How does this affect our algorithm runtime? Well, for an input of `n`, the number of steps is approximately half as we iterate by 2 each time. This is an algorithm of `O(N/2)` but as I've mentioned earlier, Big O doesn't concern itself with constants because they aren't relative to how an algorithm scales as the input changes and it wouldn't be fun or easy to have to compare an algorithm of `O(N/2 + 5 N)` against `O(N + 5 / 2N)`. Therefore, the Big O efficiency of both algorithms is `O(N)`. They scale at the same rate as the input grows. + +Therefore, you also need to ensure the code you write is as efficient as it can be within its time complexity. + +### Assignment + +
+ +1. Read through [Big O Notation in JavaScript by Doable Danny](https://www.doabledanny.com/big-o-notation-in-javascript). It covers the common complexities with graphs and examples. +2. The [Big-O cheat sheet](https://www.bigocheatsheet.com/) is an amazing resource. It gives a complexity chart where you can see how the different algorithms perform as the data size increases and also gives the time complexity for common data structure operations along with those for common sorting algorithms. +3. Read the [Step-by-step Big O Complexity Analysis Guide, using JavaScript](https://www.sahinarslan.tech/posts/step-by-step-big-o-complexity-analysis-guide-using-javascript). It has a section on Space Complexity at the end which you can skip for now. + +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [What is Big O?](#what-is-big-o) +- [What are the Big O Notations?](#big-o-notation) +- [Why use Big O?](#why-big-o) +- [What is Big Omega and why isn't it as useful?](#big--omega-notation) + +### Additional resources + +This section contains helpful links to related content. It isn’t required, so consider it supplemental. + +1. It's not a free resource but [A common sense guide to data structures and algorithms](https://pragprog.com/titles/jwdsal2/a-common-sense-guide-to-data-structures-and-algorithms-second-edition/) does a great job making these topics approachable to people not familiar with some of the mathematical terminology used. + +2. In this video, [Introduction to Big O Notation and Time Complexity](https://www.youtube.com/watch?v=D6xkbGLQesk), the author provides a step-by-step process for how to analyze and understand time complexity for various algorithms. diff --git a/content/odin/javascript/finishing_up_with_javascript/conclusion.md b/content/odin/javascript/finishing_up_with_javascript/conclusion.md new file mode 100644 index 00000000..b38666be --- /dev/null +++ b/content/odin/javascript/finishing_up_with_javascript/conclusion.md @@ -0,0 +1,13 @@ +### The end of the JavaScript course! + +Completing the JavaScript course is a major milestone! Congratulations! You've learned a lot of major JavaScript concepts like prototypes, closures, promises, and event loops. Take a moment to reflect on how far you've come. + +At this point, you can create phenomenal and dynamic frontends, all using vanilla JavaScript. However, the frontend isn't over yet. You'll be learning about accessibility, responsive design and about [React](https://react.dev/), a library for creating interfaces, in the upcoming courses. You've already experienced the pain of manually updating the DOM with respect to your underlying data changes, and that's where React simplifies the process. + +#### Give your feedback + +Before you move on to the next section, please fill out this [feedback form](https://docs.google.com/forms/d/e/1FAIpQLSeHcp46iWF5D7V7wPPHDeIHK0q5Nu0zXHZi46pP7ExVjULvZA/viewform?usp=sf_link). Your feedback is important to improve the curriculum and understand users' experience. + +#### Parting thoughts + +At the end we'd like to reiterate that learning doesn't stop here. Embrace a growth mindset and explore! Good luck! diff --git a/content/odin/javascript/introduction/a_quick_review.md b/content/odin/javascript/introduction/a_quick_review.md new file mode 100644 index 00000000..ab676546 --- /dev/null +++ b/content/odin/javascript/introduction/a_quick_review.md @@ -0,0 +1,19 @@ +### Introduction + +This course assumes that you have a decent grasp on the fundamentals of JavaScript. If you just finished our [Foundations course](https://theodinproject.com/paths/foundations) then you should skip this review and move on to the next lesson. If it's been a while and you're coming from the Ruby courses, you will probably want to take a day or two to refresh yourself on the basics. + +### Review + +Running through "part 1" of [MDN's JavaScript basics course](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics) is a great idea for a refresher on the syntax. If you just want a quick reference to skim, try [LearnXinY](https://learnxinyminutes.com/docs/javascript/). + +It might also be a good idea to do a little practicing before moving on. If you want something fresh to work on, now would be a fine time to do some coding exercises from across the net. The following sites are all great places to look. + +- [Exercism](http://exercism.org/) +- [CodeWars](https://www.codewars.com/) +- [w3schools](https://www.w3schools.com/js/default.asp) + +### jQuery? + +Before you press on, a note about jQuery. We occasionally get questions about why we don't include jQuery in our curriculum. jQuery was very popular in the past, but has fallen out of the limelight in recent years. One of the biggest reasons it has begun to fall out of favor is that you don't _need_ it anymore. When it became popular, doing things like DOM manipulation and AJAX calls were difficult in plain JavaScript, but that is no longer the case. + +A quick web-search on the topic will be more useful than any explanations here, and if you still want to learn it (many older codebases still use it, and you will see it on many older Stack Overflow posts) we are confident that you can pick it up quite easily by reading the documentation on [their website](https://jquery.com/). diff --git a/content/odin/javascript/introduction/how_this_course_will_work.md b/content/odin/javascript/introduction/how_this_course_will_work.md new file mode 100644 index 00000000..89c344f7 --- /dev/null +++ b/content/odin/javascript/introduction/how_this_course_will_work.md @@ -0,0 +1,24 @@ +### Introduction + +JavaScript is the future of the web. More and more of the logic is making its way to the client side in order to facilitate lightning-fast user experiences. JavaScript is even moving to the server side with Node.js. That's why in this course we'll be diving deep into it to make sure you come out with a good understanding of how it works. + +You've already completed the [Foundations course](https://www.theodinproject.com/paths/foundations/courses/foundations), right? Good, because now we'll be moving at warp speed into new frontiers. This section will cover a lot of ground and your brain may melt down a few times, but don't worry, that's just a flesh wound. Patch 'er up and keep going! When in doubt, build something. + +### The path + +How is this course set up? It starts with a deeper look at the basics, just like the [Ruby Programming course](/paths/full-stack-ruby-on-rails/courses/ruby-programming) did with Ruby. You don't need to have completed the Ruby Programming course or [the Ruby on Rails course](/paths/full-stack-ruby-on-rails/courses/ruby-on-rails) to understand these basics. We won't be focusing deeply on the really basic coding items, so it will move quickly. You should, however, already have completed the [Foundations course](https://www.theodinproject.com/paths/foundations/courses/foundations) -- specifically, the [JavaScript Basics section ](https://www.theodinproject.com/paths/foundations/courses/foundations#javascript-basics) -- before starting this course.  + +The last thing you'll do is a final project which integrates everything you've learned in all the courses of this curriculum. This is the kind of project that you'll be telling employers all about and a great chance to prove that you're now, officially, a serious web developer. + +### Format + +There is a lot to cover, but this course has been broken up into bite-sized lessons and their accompanying projects. These projects will give you a chance to apply what you have learned and to show what you are capable of. After a few of them, you'll really start getting the hang of things. + +### In each lesson: + +1. We will introduce the topic briefly and provide you with a list of things you should pay attention to ("Points to Ponder"). +2. You will be asked to do readings, watch videos, do online courses or otherwise consume content to initially learn the material. +3. Every few lessons you will be asked to build a larger project. +4. Finally, we will include supplemental resources and other potentially useful tidbits at the end of each lesson. + +### Enough talk - get learning! diff --git a/content/odin/javascript/javascript_in_the_real_world/dynamic_user_interface_interactions.md b/content/odin/javascript/javascript_in_the_real_world/dynamic_user_interface_interactions.md new file mode 100644 index 00000000..4212b726 --- /dev/null +++ b/content/odin/javascript/javascript_in_the_real_world/dynamic_user_interface_interactions.md @@ -0,0 +1,54 @@ +### Introduction + +JavaScript is a very powerful language. It is capable of creating complex web applications that work _everywhere_. But it is just as often used on a smaller scale. JavaScript is the glue that holds even less flashy websites together- it makes drop-downs drop down and image sliders slide.   + +Fortunately, at this point, you already have all the tools you need to make these items without resorting to using a bloated framework like bootstrap. (Nothing against bootstrap... you just do _not_ need it! Good for you!) + +We aren't presenting any new content in this lesson - just giving you the chance to practice some of the techniques that you're going to be using on a daily basis as a JavaScript programmer. + +> Animations are typically handled by CSS which is a little out of the scope of this lesson, but interactive stuff like this is no fun without a little motion! If you want to take a break and learn more about making stuff move [go watch this video](https://www.youtube.com/watch?v=8kK-cA99SA0). + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- General techniques that are used by JavaScript programmers everyday. + +### Drop-down menus + +You know what we're talking about here. Picture a nav-bar or a button and when you click it the menu slides down nicely. As mentioned previously, you already know everything you need to create this element. Here are some things to keep in mind: + +1. You can allow the menu to show up either on click or on hover. +2. You should hard-code the menu items into your HTML but hide/reveal them using JavaScript. You can do this either by adding a class (`visible` or something) or by manually setting the style in JS. +3. Make sure the JavaScript code is reusable! You should be able to create multiple drop-downs on a page using HTML and reuse the JavaScript logic to hide/reveal them. +4. If you bundle your code into a module you can [publish it to npm](https://docs.npmjs.com/getting-started/publishing-npm-packages) and then install and use it anytime you like! Nothing like publishing your own modules to make you feel like a pro 😎. + +### Mobile menus + +Mobile versions of your sites are almost definitely going to need their own menu implementation. Depending on how you decided to implement your drop-down, you _could_ reuse it here, but there are tons of more inventive options out there. + +1. [Browse the web](https://dribbble.com/search?q=mobile+menu) for [some](https://uxplanet.org/top-8-mobile-navigation-menu-design-for-your-inspiration-8a2d925bffc0) [inspiration](https://marvelapp.com/blog/hamburger-menu-alternatives-mobile-navigation/), pick something and try to implement it! + +### Image slider + +Again, there's not much instruction needed here - just practice.   + +Create an image carousel. It should contain arrows on each side to advance the image forward or backward. It should automatically move forward every 5 seconds. It should contain the little navigation circles at the bottom that indicate which slide you are on (and they should be click-able to advance to that particular slide). + +Don't spend too much time worrying about getting your images to display at the correct size -- it's more important to get the slider sliding. + +1. This one is a little more involved than the last two, so think about how you would set up the different elements within the site. +2. Set up a very wide `div` which will contain the individual "slides" of each image. By appropriately positioning that `div` inside a container `div` (which acts like a picture frame), you can choose which slide is visible at any given time. +3. Once you have the slider positioned properly, build functions for "next" and "previous" which will advance to the next or previous slide accordingly. Make the transitions smooth using effects. +4. Set up arrow buttons which activate those functions and play with cycling through the images. +5. Add in some navigation dots at the bottom of the slides. Make a horizontal series of empty circles with CSS immediately below the slideshow. Each circle represents a slide, so whenever a new slide is activated, its corresponding circle gets filled in so you can tell where in the show you are. Make each circle link to that particular slide, so you can click on the circle and it will jump to that slide. +6. Add a timeout which advances the slides every 5 seconds. +7. Play around with your slideshow! + +### Knowledge check + +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + +- [What are some different types of navigation menus?](https://uxplanet.org/top-8-mobile-navigation-menu-design-for-your-inspiration-8a2d925bffc0) +- [What are the alternatives to hamburger menus?](https://marvelapp.com/blog/hamburger-menu-alternatives-mobile-navigation/) +- [What are the downsides of hamburger menus?](https://marvelapp.com/blog/hamburger-menu-alternatives-mobile-navigation/) diff --git a/content/odin/javascript/javascript_in_the_real_world/form_validation_with_javascript.md b/content/odin/javascript/javascript_in_the_real_world/form_validation_with_javascript.md new file mode 100644 index 00000000..13dd3edf --- /dev/null +++ b/content/odin/javascript/javascript_in_the_real_world/form_validation_with_javascript.md @@ -0,0 +1,57 @@ +### Introduction + +Forms are a crucial part of most websites. Almost every major site has sign-up forms, contact forms, search forms and more! Luckily HTML5 and JavaScript have some handy built-in methods. You've already learned about validation with HTML and styling validations with CSS in our [Form Validations](https://www.theodinproject.com/paths/full-stack-javascript/courses/intermediate-html-and-css/lessons/form-validation) lesson in the Intermediate HTML and CSS course. + +In this lesson, we'll cover the Constraint Validation API: a way to validate forms on the frontend with JavaScript. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Understand the importance of validation in HTML forms. +- Understand Constraint Validation API for more control over form validation. +- Add validation using only JavaScript. + +### Assignment + +
+ +1. [This tutorial on Form Validation](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#validating_forms_using_javascript) covers how we can use JavaScript to validate forms, including the constraint validation API. + +2. It'll also prove beneficial to go through the [Constraint Validation docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation). + +3. For Reference, [this document](https://www.w3schools.com/js/js_validation_api.asp) covers the JavaScript validation API in a more concise format. These functions were explained in the previous article. Typically, with HTML forms, the inputs are validated upon form submission, but you can use these functions to check validity whenever you like (such as when a user clicks or tabs out of a specific input field). + +
+ +### Practice + +
+ +#### Warmup + +Go back to your 'Library' project and add validation to that form! Don't let your users submit without filling in all the fields! Don't forget to use your Git workflow skills you learned in [this foundations lesson](https://www.theodinproject.com/lessons/foundations-revisiting-rock-paper-scissors) to make a new branch, work on your feature and merge it back to main when it's all done. + +#### Project + +Build a browser form which collects Email, Country, Zip Code, Password and Password Confirmation fields. It should use live inline validation to inform the user whether a field is properly filled in or not. That means highlighting a field red and providing a helpful error message until it has been filled in properly. + +The form doesn't need to actually submit, but you should give an error message if the button is pushed with any active errors or unfilled required fields. For the sake of this lesson, make sure **all** of the validation occurs in the JavaScript file. If all is well and the form is "submitted", give the user a high five. + +1. Set up a blank HTML document +2. Think about how you would set up the different form elements and their accompanying validators. What objects and functions will you need? A few minutes of thought can save you from wasting an hour of coding. The best thing you can do is whiteboard the entire solution before even touching the computer. +3. Write the form elements. +4. Add the JavaScript code that checks validation as the user progresses through the form. When a user leaves a form field, it should automatically validate that field. +5. Test out all possible cases. +6. Don't forget to style validations with CSS by using the `:valid` and `:invalid` pseudo-classes! + +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [Explain the importance of validating HTML forms before submitting them to a server.](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#what_is_form_validation) +- [Describe the two types of client-side form validation.](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#different_types_of_client-side_validation) +- [Explain how JavaScript Constraint Validation API provides more control and customization of form validation.](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#validating_forms_using_javascript) +- [Could forms also be validated without using Constraint Validation API?](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#validating_forms_without_a_built-in_api) diff --git a/content/odin/javascript/javascript_in_the_real_world/linting.md b/content/odin/javascript/javascript_in_the_real_world/linting.md new file mode 100644 index 00000000..55830c03 --- /dev/null +++ b/content/odin/javascript/javascript_in_the_real_world/linting.md @@ -0,0 +1,67 @@ +### Introduction + +Before we dive all the way into the Code, we are going to take a moment to improve your editor setup and overall productivity. Doing this now will make things much easier for you going forward. This lesson will give you some information about code style, and then give you some tools to help you maintain consistent code-style throughout your projects. In some cases it can even help adjust things like indentation for you! We will also introduce template repositories which can save you time setting up projects that share a lot of configuration with other projects. + +### Style guides + +Code style is important! Having a consistent set of style rules for things such as indentation or preferred quote style makes your code more maintainable and easier to read. There are several popular JavaScript style guides on the net that set standards for these types of things, and a little time spent reading them _will_ make you a better developer. + +1. The [Airbnb Style Guide](https://github.com/airbnb/javascript) is one of the most popular. It is also very well formatted and easy to read. +2. Google also has their own [style guide](https://google.github.io/styleguide/jsguide.html) for JavaScript. +3. The [JavaScript Standard Style](https://standardjs.com/rules.html). Used by companies like NPM and GitHub, among [others](https://standardjs.com/index.html#who-uses-javascript-standard-style). + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Set up a linter and prettier to make your code better. +- Learn what template repositories are and how to set one up. + +### Linting + +The style guides we mentioned above are full of really helpful advice for formatting, organizing and composing your code. But there are a _lot_ of rules - it can be difficult to internalize them all. **Linters** are tools that will scan your code with a set of style rules and will report any errors to you that they find. In some cases, they can even auto-fix the errors! The following articles explain in more detail the benefits of using a linter while you code. + +1. [This article](https://gomakethings.com/javascript-linters/) gets right to the point... start here! +2. [This article](https://hackernoon.com/how-linting-and-eslint-improve-code-quality-fa83d2469efe) goes a little further by discussing exactly _how_ linters do what they do. + +There are multiple options for linting your JavaScript, but the most popular (and most common in the industry) is [eslint](https://eslint.org/). Getting it installed and the initial set-up is straightforward. + +1. [The official 'Getting Started' page](https://eslint.org/docs/user-guide/getting-started) is a good place to start. It covers installation and basic setup. The basic way to use this tool is to run the `eslint` command in your terminal with a specific file. +2. Far more useful are linting plugins for your favorite text editor. Most editor plugins allow you to automatically lint your code as you are writing it, and will show the errors right in the editor, which makes resolving them _much_ simpler. We can't cover _every_ editor installation but some of the more popular are: + 1. Visual Studio Code - [The Plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [a tutorial](https://www.digitalocean.com/community/tutorials/linting-and-formatting-with-eslint-in-vs-code). + 2. Sublime Text - [The Plugin](https://github.com/roadhump/SublimeLinter-eslint) and [a tutorial](http://jonathancreamer.com/setup-eslint-with-es6-in-sublime-text/). + 3. Atom - [The Plugin](https://atom.io/packages/linter-eslint) and [a tutorial](https://medium.freecodecamp.org/how-to-set-up-eslint-in-atom-to-contribute-to-freecodecamp-3467dee86e2c). + 4. Vim - [Use the ALE plugin](https://github.com/dense-analysis/ale). If you use Vim you already know what you're getting into. ALE is a great plugin, but the setup and configuration can be a little tricky. + +### Prettier + +Prettier is _awesome_. It is similar to a linter, but serves a slightly different function. Prettier will take your JS code and then automatically format it according to a set of rules. Unlike a linter, it's not looking for style errors, but specifically targeting the layout of your code and making intelligent decisions about things like spaces, indentation levels and line-breaks. + +1. [This quick talk](https://www.youtube.com/watch?v=hkfBvpEfWdA) from Prettier's creator is a great introduction. +2. Give it a test drive [here](https://prettier.io/playground). Go ahead and copy/paste some of your old JavaScript code into that editor and see what happens. +3. Setup is simple. [The homepage](https://prettier.io/) links to tutorials for most popular editors. + +Using prettier makes coding faster and easier! You don't have to worry about nailing things like indentation, or remembering every semi-colon because prettier will take care of those details for you. + +### Using ESLint and Prettier + +We **highly** recommend that you install ESlint and Prettier and use them for all future projects. It will make your code easier to read, both for yourself and for anyone else looking at it. +However, using ESLint and Prettier together causes conflicts. To fix that follow the instructions to install [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier#installation). It turns off all ESLint rules that are unnecessary or might conflict with Prettier. Doing just this is enough to resolve the conflict and get them both working smoothly with one another. +Another way to address the conflict is to use `eslint-plugin-prettier`. It lets you run Prettier as if it were a rule in ESLint. However, doing this is **not recommended**. You can learn more about it [here](https://prettier.io/docs/en/integrating-with-linters.html#notes). + +### Template repositories + +With the last few projects, you might have felt that setting up Webpack involved a fair few files and configuration, and that you may have had to look at what you configured before to copy and paste the configuration you want to reuse. You may also have noticed that whenever you create a new repository on Github, there is an option near the top for a `Repository template`. + +This is where template repositories can come very much in handy. Any of your existing repositories can be converted to a template in its settings (right under where you can rename the repository, there is a checkbox for whether the repository is a template or not). If you check this box, congratulations, that's all you need to do! Now when you go to create a new repository, the `Repository template` dropdown will have any templates listed for you to select. Selecting one will mean your new repository will be a copy of the chosen template, not an empty one! + +If you find yourself reusing a lot of setup code for multiple projects, you can make a new repository with all of the setup code you need then mark it as a template. Now you can select that template when creating a new project repository to save time getting set up, letting you dive into working on the project itself sooner! + +### Knowledge check + +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + +- [What is linting?](https://mikecavaliere.com/javascript-linting-what-developers-need-to-know/) +- [Which problems can linting prevent?](https://mikecavaliere.com/javascript-linting-what-developers-need-to-know/) +- [Why should you use Prettier?](https://www.youtube.com/watch?v=hkfBvpEfWdA) +- [What is a template repository?](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository) diff --git a/content/odin/javascript/javascript_in_the_real_world/what_is_es6.md b/content/odin/javascript/javascript_in_the_real_world/what_is_es6.md new file mode 100644 index 00000000..06d80d6e --- /dev/null +++ b/content/odin/javascript/javascript_in_the_real_world/what_is_es6.md @@ -0,0 +1,21 @@ +### Introduction + +We've been throwing around the term __ES6__ since our very first lessons, but we haven't taken the time to properly explain what it means or to investigate the implications of it in our code. + +ES6 is a version of JavaScript that was officially released in the summer of 2015. It included _many_ new features that make writing JavaScript much easier and cleaner. If you have been following our lessons you have already been learning these new features because, well, ES6 is _just JavaScript_. + +You have probably also come across articles talking about features in ES7 or ES8 or ES2015 or ES2017 etc. Part of the confusion here is that right after the release of ES6, the committee that makes these decisions changed the naming scheme from 'version numbers' (ES5, ES6, ES7 etc.) to 'release years' (ES2015, ES2016, ES2017 etc.) + +- [This article](https://codeburst.io/javascript-wtf-is-es6-es8-es-2017-ecmascript-dca859e4821c) provides a nice clean explanation and timeline of the various ECMAScript releases. +- [This document](https://github.com/lukehoban/es6features) outlines all the new features that showed up in ES6. As we've mentioned you've already been using many of these, though there are a few we haven't specifically covered yet. + +The _problem_ with JavaScript constantly updating and adding features is that it sometimes takes web-browsers a while to catch up and implement new features once they've been released. At the current time all modern browsers (Chrome, Firefox, Safari and Edge) support _all_ of ES6, and _most_ of ES7, but older browsers (various versions of Internet Explorer for instance) do not. This means, unfortunately, that if you write code that uses these new features it __will not run__ in browsers that do not support it. + +For most of us, this has not been an issue because you are almost definitely using a new browser that automatically updates itself when a new version is released. But in the real world, if you're selling products to customers you can't control which browsers people will use to connect to your site. + +Fortunately there _is_ a solution to this problem. [Babel](http://babeljs.io/) is a tool that takes your modern JavaScript code and __transpiles__ it to code that older browsers can understand. It can be used from the command line with a single command, and can also easily be added to your webpack configuration with the babel-loader. + +In all honesty, this is not something that you are going to _need_ to worry about on every project you're starting. All the ES6 features are present in the large majority of browsers used worldwide. But JavaScript is constantly changing, and as new features are announced and released, you can use Babel to try them out, often before they're available in _any_ browser! + +- Follow the instructions [here](https://github.com/babel/babel-loader) to install the babel-loader and use it with webpack. If you've already got webpack up and running in a project, adding babel is a cinch! +- Read [this article](https://blog.jakoblind.no/babel-preset-env/) to better understand presets and plugins in Babel. It will also show you how to target specific browser versions you want to support. diff --git a/content/odin/javascript/organizing_your_javascript_code/classes.md b/content/odin/javascript/organizing_your_javascript_code/classes.md new file mode 100644 index 00000000..9e2f2637 --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/classes.md @@ -0,0 +1,64 @@ +### Introduction + +JavaScript does _not_ have classes in the same sense as other Object Oriented languages like Java or Ruby. ES6, however, _did_ introduce a syntax for object creation that uses the `class` keyword. It is basically a new syntax that does the _exact_ same thing as the object constructors and prototypes we learned about in the constructor lesson. + +There is a bit of controversy about using the class syntax, however. Opponents argue that `class` is basically just _syntactic sugar_ over the existing prototype-based constructors and that it's dangerous and/or misleading to obscure what's _really_ going on with these objects. Despite the controversy, classes are beginning to crop up in real code bases that you are almost certainly going to encounter such as frameworks like React (especially if you end up working with class-based React code). + +Since we've already gone fairly in-depth with Constructors, you don't have too much left to learn here beyond the new syntax. If you choose to use classes in your code (that's fine!) you can use them much the same way as object constructors. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Describe the pros and cons of using classes in JavaScript. +- Briefly discuss how JavaScript's object creation differs from a language like Java or Ruby. +- Explain the differences between an object constructor and a class. +- Explain what "getters" and "setters" are. +- Understand what computed names and class fields are. +- Explain how to implement private class fields and methods. +- Describe function binding. +- Use inheritance with classes. +- Understand why composition is generally preferred to inheritance. + +### Assignment + +
+ +1. [JavaScript.info's article on Getters and Setters](https://javascript.info/property-accessors) should get you up to + speed on "Getters and Setters". + +2. [This article](https://javascript.info/class) is probably just about all you need to start using `class` syntax + confidently. + +3. [The MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) are, as usual, a great + resource for going a little deeper. Look especially at the ['extends' reference page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends), + including the ['Mixins' section](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends#mix-ins). In some frameworks like React, you can use classes to create your components and make them `extend` the core React component which gives you access to all their built-in functionality (though this is not the only way to create components. This will all be covered later in React section of the course). Classes can also have [private features](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields) that allow you to implement privacy similarly to factory functions. + +4. Classes can have [static properties and methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static) which are properties and methods that are accessed on the class itself and not on the instance of a class. This is similar to how some string methods are accessed on the instance of a string itself e.g. `someString.slice(0, 5)` whereas some methods are called on the String constructor directly e.g. `String.fromCharCode(79, 100, 105, 110)`. + +5. Read [this article covering opinions regarding the pros and cons of classes](https://medium.com/@rajaraodv/is-class-in-es6-the-new-bad-part-6c4e6fe1ee65). [FunFunFunction's video on Composition over Inheritance](https://www.youtube.com/watch?v=wfMtDGfHWpA) elaborates on the cons mentioned in the article and does a great job of going over the topic. +
+ +### Practice + +Go back to your [Library](https://www.theodinproject.com/lessons/node-path-javascript-library) project and refactor it to use `class` instead of plain constructors. Don't forget to use the git workflow you learned in [this foundations lesson](https://www.theodinproject.com/lessons/foundations-revisiting-rock-paper-scissors) to work on a new feature. You should get used to working like this! + +### Knowledge check +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + +- [Describe the pros and cons of using classes in JavaScript.](https://rajaraodv.medium.com/is-class-in-es6-the-new-bad-part-6c4e6fe1ee65) +- [How does JavaScript's object creation differ from a language like Java or Ruby?](https://rajaraodv.medium.com/is-class-in-es6-the-new-bad-part-6c4e6fe1ee65) +- [Explain the differences between object constructors and classes.](https://javascript.info/class#not-just-a-syntactic-sugar) +- [What are "getters" & "setters"?](https://javascript.info/property-accessors) +- [Describe computed names and class fields.](https://javascript.info/class) +- [Describe function binding.](https://javascript.info/class) +- [Describe static properties.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static) +- [Describe private class features.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields) +- [How is inheritance used with classes?](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#inheritance) +- [Why is favoring Composition over Inheritance suggested?](https://www.youtube.com/watch?v=wfMtDGfHWpA) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +- [This playlist](https://www.youtube.com/playlist?list=PLtwj5TTsiP7uTKfTQbcmb59mWXosLP_7S) from Stephen Mayeux, explains ES6 Classes and some of their methods with easy to follow examples. diff --git a/content/odin/javascript/organizing_your_javascript_code/es6_modules.md b/content/odin/javascript/organizing_your_javascript_code/es6_modules.md new file mode 100644 index 00000000..b9e551af --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/es6_modules.md @@ -0,0 +1,182 @@ +### Introduction + +Separate from the **module pattern** that we discussed in an earlier lesson, "modules" is a feature that arrived with ES6. ES6 modules are starting to appear in many code bases around the net and getting them up and running will give us a chance to explore some new parts of the JavaScript ecosystem, so it's going to be a worthy excursion! + +Don't be fooled! We're going to cover much more than just the new module syntax in this lesson! Before we can really *use* these modules, we're going to have to learn about **npm** and **webpack**, which are both topics that will be *very* useful to you even beyond this lesson. In the end, the modules themselves are simple to implement, so we're going to take this chance to learn about a few other things. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Explain what npm is and where it was commonly used before being adopted on the frontend. +- Describe what `npm init` does and what `package.json` is. +- Know how to install packages using npm. +- Describe what a JavaScript module bundler like webpack is. +- Explain what the concepts "entry" and "output" mean in relation to webpack. +- Briefly explain what a development dependency is. +- Explain what "transpiling code" means and how it relates to front-end development. +- Briefly describe what a task runner is and how it's used in front-end development. +- Describe how to write an npm automation script. +- Explain one of the main benefits of writing code in modules. +- Explain "named" exports and "default" exports. + +### The history of JavaScript + +Why do we even need or want this stuff? What do you gain from all of this added complexity? These are good questions... with good answers. + +- Read [this article](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html) for a bit of a history lesson. It's long, but it puts what we're doing here in great perspective. This article is a bit older, and those who have coded along with the example have frequently run into issues, so we don't suggest that you code along (you'll be following along with the official Webpack documentation later). Nevertheless, this article is extremely important conceptually and really clarifies the 'WHY' of the rest of this lesson. + +### npm + +The **node package manager** is a command-line tool that gives you access to a gigantic repository of plugins, libraries and tools. If you have done our Fundamentals course, you will probably have encountered it when you [installed the Jest testing framework](https://github.com/TheOdinProject/javascript-exercises#how-to-use-these-exercises) to do our exercises. + +Read through the npm links below but don't worry about running any of the commands on your computer. This section is about growing your awareness of npm. You will have an opportunity to use what you learn here in upcoming projects. + +1. Take a couple minutes to read the [About npm](https://docs.npmjs.com/getting-started/what-is-npm) page - a great introduction to npm. +1. [This tutorial](https://docs.npmjs.com/downloading-and-installing-packages-locally) teaches you how to install packages with npm. +1. [This tutorial](https://docs.npmjs.com/creating-a-package-json-file) covers the `package.json` file, which you can use to manage your project's dependencies. +1. Read [this article on development dependencies](https://dev.to/moimikey/demystifying-devdependencies-and-dependencies-5ege) to learn what they are and how to use them. **NOTE:** The author of the article clarifies something potentially confusing in the first comment. After reading about and practicing with Webpack in this lesson, you should come back to [this comment](https://dev.to/moimikey/demystifying-devdependencies-and-dependencies-5ege#comment-5ea4) and understand what they meant. +1. If you run into trouble at any point you can check out [the official docs page](https://docs.npmjs.com/) for more tutorials and documentation. + +### Yarn? + +At some point, you will probably run into [Yarn](https://yarnpkg.com/en/) - a replacement for the default `npm`. For the most part, it does the same things, though it *does* have a few more features. Recent versions of `npm` have incorporated some of the best features of Yarn, so using it won't offer you any real advantages at this point in your career. It *is* a fine package manager, however, and may be worth your consideration in the future. + +### Webpack and bundlers + +So far, your projects have had relatively basic file structures. As project complexity grows, so too will the benefits of well-organized code. A project consisting of a single, long file with lots of code can be made easier to navigate and maintain by being broken down into multiple smaller files (modules). Further benefits of writing code in modules will come below when we introduce ES6 modules. + +But there's a problem! The browser would need to make a separate HTTP request for each file. The more files you have, the more costly this becomes, particularly on slower networks and would only increase if you also imported third-party libraries into your app. + +What if we had a way to write multiple files and/or import multiple third-party libraries but eventually combine them all into fewer files at the end so the browser did not have to make so many requests? + +Enter bundlers. Give a bundler a starting file (an entry point) and it will build a dependency graph of the modules and dependencies starting from that file, then combine them into a single output file. Provide multiple entry points and it will do the same for each separately. You can read more about why we use bundlers and how they work in [this short article](https://snipcart.com/blog/javascript-module-bundler). + +Webpack is one such tool for bundling modules. There is a lot of talk across the net about how difficult and complex it is to set up and use, but at the moment our needs are few and the setup is basic enough. In fact, you can see an example of getting it up and running on the front page of [their website](https://webpack.js.org/). + +Webpack *is* a very powerful tool, and with that power comes a decent amount of complexity. Don't let it scare you off! The basic configuration is not difficult and proficiency with webpack looks *amazing* on resumes. + +To get us started, we are going to refer to the official documentation. + +1. Code along with all of the steps of [this tutorial](https://webpack.js.org/guides/getting-started/) ("Basic Setup" through "Conclusion"). + +Let's discuss what's going on there. After installing webpack using npm, we set up a project that required an external library (lodash - check it out [here](https://lodash.com/) if it's new to you) using a `script` tag. The site lists a few reasons why this is probably *not* ideal and then steps through using webpack to accomplish the same thing. + +Let us revisit two key words mentioned earlier - **entry** and **output**. In the above link's example, we rearranged the files into a `src` and `dist` folder. Technically, we could have called those folders anything, but those names are typical. `src` is our *source* directory. In other words, `src` is where we write all of the code that webpack is going to bundle up for us. When webpack runs, it goes through all of our files starting at any entry points we give it, looks for any `import` statements and then compiles *all* of the code we need to run our site into a single file per entry point inside of the `dist` folder (short for *distribution*). In this example, we have a single entry point - `/src/index.js`. The **output** file is the compiled version - `dist/main.js`. + +- Browse [this document](https://webpack.js.org/concepts/) for more details. We'll talk plugins and loaders in another lesson. + +### ES6 modules (finally!) + +Before ES6, there was no straightforward way to handle multiple JavaScript files for a single page in a browser. Outside of the browser, in a runtime like Node (remember how you used Node to run the Jest tests for the JavaScript exercises back in the Foundations course?), you could use something called `CommonJS modules` but that was only for Node and not the browser. Fortunately, we now have ES6 modules which work in both (although Node still uses CommonJS by default and you need to do a tiny bit of tweaking to get it to understand ES6 module syntax). + +Now that we (sorta) understand what webpack is doing, it's time to discuss the syntax for ES6 modules (otherwise known as ESM). There are only 2 components to it - **import** and **export**. + +The import statement is the same thing that you used during the webpack tutorial so it should be familiar by now. + +```javascript +// a file called functionOne.js +const functionOne = () => console.log('FUNCTION ONE!'); + +export { functionOne }; +``` + +```javascript +// another JS file +import { functionOne } from './functionOne'; + +functionOne(); // this should work as expected! +``` + +There are *many* benefits to writing your code in modules. One of the most compelling is code reuse. If, for instance, you have written some functions that manipulate the DOM in a specific way, putting all of those into their own file as a 'module' means that you can copy that file and reuse it very easily! + +There are also the same benefits as when using factory functions or the module pattern (the module pattern and ES6 modules are not the same things; this naming convention is frustrating). With the introduction of ES6 Modules, the module pattern (IIFEs) is not needed anymore, though you might still encounter them in the wild. When using ES6 modules, only what is exported can be accessed in other modules by importing. Additionally, any declarations made in a module are not automatically added to the global scope. By using ES6 modules, you can keep different parts of your code cleanly separated, which makes writing and maintaining your code much easier and less error-prone. Keep in mind that you can *definitely* export constructors, classes and factory functions from your modules. + +To pull it all together, let's write a module and then include it in our code. We are going to continue from where the webpack tutorial left off. Before beginning, your file directory should look something like this: + +``` +├── dist +│   ├── main.js +│   └── index.html +├── src +│   └── index.js +├── package-lock.json +├── package.json +└── webpack.config.js +``` + +In addition, you should be able to bundle and run webpack by typing `npx webpack` in the terminal, (or `npm run build` if you're using the example project created on the previous section.) . + +Add a new file to the `src` directory called `myName.js` with the following contents: + +``` javascript +const myName = (name) => 'Hi! My name is ' + name; + +export default myName; +``` + +Then, in `src/index.js`, import and use your new function: + +```javascript +// import your function +import myName from './myName'; + +function component() { +  const element = document.createElement('div'); + +  // use your function! +  element.textContent = myName('Cody'); +  return element; +} + +document.body.appendChild(component()); +``` + +Easy! Now, if you run `npx webpack` in your project directory, your page should show our new function being used. + +There are two different ways to use exports in your code: named exports and default exports. Which option you use depends on what you're exporting. Take a moment to look at the [MDN documentation about the export declaration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) and the [MDN documentation about the import declaration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) which give great examples for the different syntax on import and export. + +Here is an example with named exports, which you will most often use when you have multiple values to export in a module: + +```javascript +// a file called myModule.js +const functionOne = () => 'ONE'; +const functionTwo = () => 'TWO'; + +export { + functionOne, + functionTwo +}; +``` + +And to import them: + +```javascript +// index.js in /src folder +import {functionOne, functionTwo} from './myModule'; +``` + +Using this pattern gives you the freedom to only import the functions you need in the various files of your program. So it's perfectly fine to only import `functionOne` if that's the only one you need. + +### Knowledge check + +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + +- [Explain what npm is and where it was commonly used before being adopted on the frontend.](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html) +- [Describe what `npm init` does and what `package.json` is.](https://docs.npmjs.com/creating-a-package-json-file) +- [Know how to install packages using npm.](https://docs.npmjs.com/downloading-and-installing-packages-locally) +- [Describe what a JavaScript module bundler like webpack is.](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html) +- [Explain what the concepts "entry" and "output" mean in relation to webpack.](#webpack-knowledge-check) +- [Briefly explain what a development dependency is.](https://dev.to/moimikey/demystifying-devdependencies-and-dependencies-5ege) +- [Explain what "transpiling code" means and how it relates to frontend development.](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html) +- [Briefly describe what a task runner is and how it's used in frontend development.](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html) +- [Describe how to write an npm automation script.](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html) +- [Explain one of the main benefits of writing code in modules.](#module-knowledge-check) +- [Explain "named exports" and "default exports".](#exports-knowledge-check) + +### Additional resources + +This section contains helpful links to other content. It isn’t required, so consider it supplemental. + +- [Watch this video about ES6 Modules by Web Dev Simplified](https://youtu.be/cRHQNNcYf6s) if you find video lessons easier to absorb. It covers the same topics as discussed in this lesson. +- Here is [a brief comparison of CommonJS modules and ES6 modules](https://blog.logrocket.com/commonjs-vs-es-modules-node-js/). diff --git a/content/odin/javascript/organizing_your_javascript_code/factory_functions_and_module_pattern.md b/content/odin/javascript/organizing_your_javascript_code/factory_functions_and_module_pattern.md new file mode 100644 index 00000000..009d0be1 --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/factory_functions_and_module_pattern.md @@ -0,0 +1,294 @@ +### Why not constructors? + +We have discussed object constructors in the previous lesson. However, they are one of the many ways to organize your code. While they are fairly common and a fundamental building block of the JavaScript language, they have their flaws. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Describe the scope of a variable. +- Explore what closures are. +- Briefly consider the disadvantages of using constructors. +- Discuss Factory functions with examples. +- Discuss Private variables and functions concerning factory functions. +- Showcase object inheritance with the help of factory functions. +- Describe what module pattern and IIFEs are. +- Discuss encapsulation and how the module pattern helps with namespacing. + +### Scoopfuls of scopes + +The word "scoping" essentially asks, "Where is a certain variable available to me?" - it indicates the current context of a variable. When a variable is not declared within **any** functions, existing outside any `{ curly braces }`, they are said to be in the **global scope**, meaning that they are available everywhere. If they are within a function or `{ curly braces }`, they are known to be **locally scoped**. + +Before ECMAScript 6, JavaScript had a single keyword to declare a variable, `var`. These variables can be redefined and updated, and are said to be defined within the **function scope**, meaning, they are only available within the function they are declared in. + +In ECMAScript 6, the keywords `let` and `const` were introduced. While `var` variables were function scoped, these allow you to define variables that are **block scoped** - basically, scoping the variable to only be available within the closest set of `{ curly braces }` in which it was defined. These braces can be those of a `for` loop, `if-else` condition, or any other similar construct, and are called, a block. Let's see an example to sum this all up. + +```javascript +let globalAge = 23; // This is a global variable + +// This is a function - and hey, a curly brace indicating a block +function printAge (age) { + var varAge = 34; // This is a function scoped variable + + // This is yet another curly brace, and thus a block + if (age > 0) { + // This is a block-scoped variable that exists + // within its nearest enclosing block, the if's block + const constAge = age * 2; + console.log(constAge); + } + + // ERROR! We tried to access a block scoped variable + // not within its scope + console.log(constAge); +} + +printAge(globalAge); + +// ERROR! We tried to access a function scoped variable +// outside the function it's defined in +console.log(varAge); +``` + +Take a while to brew on that example. In the end, it's not some mind-blowing concept but there's a whole bunch of terms in there - it'll all help us understand the next mammoth - closures. + +### Closures aren't scary + +The best way to approach this would be to start with an example - take a look at this piece of code below. + +```javascript +function makeAdding (firstNumber) { + // "first" is scoped within the makeAdding function + const first = firstNumber; + return function resulting (secondNumber) { + // "second" is scoped within the resulting function + const second = secondNumber; + return first + second; + } +} +// but we've not seen an example of a "function" +// being returned, thus far - how do we use it? + +const add5 = makeAdding(5); +console.log(add5(2)) // logs 7 +``` + +A lot going on, so let's break it down: + +1. The `makeAdding` function takes an argument, `firstNumber`, declares a constant `first` with the value of `firstNumber`, and returns another **function**. +2. When an argument is passed to the returned function, which we have assigned to **add5**, it returns the result of adding up the number passed earlier to the number passed now (`first` to `second`). + +Now, while it may sound good at first glance, you may already be raising your eyebrows at the second statement. As we've learned, the `first` variable is scoped within the `makeAdding` function. When we declare and use `add5`, however, we're **outside** the `makeAdding` function. How does the `first` variable still exist, ready to be added when we pass an argument to the `add5` function? This is where we encounter the concept of closures. + +Functions in JavaScript form closures. A closure refers to the combination of a function and the **surrounding state** in which the function was declared. This surrounding state, also called its **lexical environment**, consists of any local variables that were in scope at the time the closure was made. Here, `add5` is a reference to the `resulting` function, created when the `makeAdding` function is executed, thus it has access to the lexical environment of the `resulting` function, which contains the `first` variable, making it available for use. + +This is a **crucial** behavior of functions - allowing us to associate data with functions and manipulate that data anywhere outside of the enclosing function. If you're still confused, take a small detour to examine the [second question under the Knowledge check section](#knowledge-check) - no need to read the entire thing for now, anything from "Emulating private methods with closures" onward will be discussed later in this lesson. + +### So, what's wrong with constructors? + +One of the key arguments against constructors, in fact, the biggest argument is how they *look* like regular JavaScript functions, even though they do not *behave* like regular functions. If you try to use a constructor function without the `new` keyword, not only does your program fail to work, but it also produces error messages that are hard to track down and understand. + +Yet another issue stems from the way the `instanceof` works. It checks the presence of a constructor's prototype in an object's *entire* prototype chain - which does nothing to confirm if an object was made with that constructor since the constructor's prototype can even be reassigned after the creation of an object. + +While still seen in code, these problems led to the use of a pattern that was similar to constructors but addressed a ton of these problems: Factory Functions. + +### Factory functions 🏭 + +These fancy-sounding functions work very similar to how constructors did, but with one key difference - they levy the power of closures. Instead of using the `new` keyword to create an object, factory functions set up and return the new object when you call the function. They do not use the prototype, which incurs a performance penalty - but as a general rule, this penalty isn’t significant unless you’re creating thousands of objects. Let's take a basic example to compare them to constructor functions. + +```javascript +const User = function (name) { + this.name = name; + this.discordName = "@" + name; +} +// hey, this is a constructor - +// then this can be refactored into a factory! + +function createUser (name) { + const discordName = "@" + name; + return { name, discordName }; +} +// and that's very similar, except since it's just a function, +// we don't need a new keyword +``` + +
+ +### The object shorthand notation + +Some may get confused by the way the returned object is written in the factory function example. In 2015, a shortcut to creating objects was added to JavaScript. Say we wanted to create an object with a name, age, and color, we would write it as follows: + +```javascript +const name = "Bob"; +const age = 28; +const color = "red"; + +const thatObject = { name: name, age: age, color: color }; +``` + +However, now, if we have a variable with the same name as that of the property to which we are assigning it, then we can write it once! + +```javascript +const nowFancyObject = { name, age, color }; +``` + +An added advantage to this is that it's now possible to console.log values neatly! + +```javascript +// If you wanted to log these values, earlier, +// you would have done the following +console.log(name, age, color); +// which would have resulted in a mess - Bob 28 red + +// Try wrapping it in some { curly braces } now, +// which makes it an object! +console.log({ name, age, color }); +// now it logs as - { name: "Bob", age: 28, color: "red" } +``` + +### Destructuring + +Yet another expression allows you to "unpack" or "extract" values from an object (or array). This is known as **destructuring**. When you have an object, you can extract a property of an object into a variable of the same name, or any named variable for an array. Take a look at the example below: + +```javascript +const obj = { a: 1, b: 2 }; +const { a, b } = obj; +// This creates two variables, a and b, +// which are equivalent to +// const a = obj.a; +// const b = obj.b; + +const array = [1, 2, 3, 4, 5]; +const [ zerothEle, firstEle ] = array; +// This creates zerothEle and firstEle, both of which point +// to the elements in the 0th and 1st indices of the array +``` + +[The MDN article](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) on destructuring has some great examples and should be a good read for this concept. + +
+ +### Private variables and functions + +Now you may be thinking - where does closure come into all of this? Factories seem to be returning an object. This is where we can extend our `User` factory to add a few more variables and introduce "private" ones. Take a look at this, now: + +```javascript +function createUser (name) { + const discordName = "@" + name; + + let reputation = 0; + const getReputation = () => reputation; + const giveReputation = () => reputation++; + + return { name, discordName, getReputation, giveReputation }; +} + +const josh = createUser("josh"); +josh.giveReputation(); +josh.giveReputation(); + +console.log({ + discordName: josh.discordName, + reputation: josh.getReputation() +}); +// logs { discordName: "@josh", reputation: 2 } +``` + +We’ve introduced a new metric for a new user - a reputation. Notice that the object we return in the factory function does not contain the `reputation` variable itself, nor any copy of its value. Instead, the returned object contains two functions - one that reads the value of the `reputation` variable, and another that increases its value by one. The `reputation` variable is what we call a "private" variable, since we cannot access the variable directly in the object instance - it can only be accessed via the closures we defined. + +Concerning factory functions, a private variable or function uses closures to create smaller, dedicated variables and functions within a factory function itself - things that we do not *need* to return in the object itself. This way we can create neater code, without polluting the returned object with unnecessary variables that we create while creating the object itself. Often, you do not need every single function within a factory to be returned with the object, or expose an internal variable. You can use them privately since the property of closures allows you to do so. + +In this case, we did not need control of the `reputation` variable itself. To avoid foot guns, like accidentally setting the reputation to `-18000`, we expose the necessary details in the form of `getReputation` and `giveReputation`. + +### Prototypal inheritance with factories + +In the lesson with constructors, we looked deeply into the concept of prototype and inheritance, and how to give our objects access to the properties of another. With factory functions too, there are easy ways to do that. Take another hypothetical scenario into consideration. We need to extend the `User` factory into a `Player` factory that needs to control some more metrics - there are some ways to do that: + +```javascript +function createPlayer (name, level) { + const { getReputation, giveReputation } = createUser(name); + + const increaseLevel = () => level++; + return { name, getReputation, giveReputation, increaseLevel }; +} +``` + +And there you go! You can create your User, extract what you need from it, and re-return whatever you want to - hiding the rest as some private variables or functions! In case you want to extend it, you can also use the [`Object.assign` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to add on the properties you want! + +```javascript +function createPlayer (name, level) { + const user = createUser(name); + + const increaseLevel = () => level++; + return Object.assign({}, user, { increaseLevel }); +} +``` + +### The module pattern - IIFEs + +
+ +ECMAScript 6 introduced a new JavaScript feature called "modules" - which are a set of syntax for importing and exporting code between different JavaScript files. While they are important and powerful, they are covered a bit later in the curriculum. We are not talking about them in this section. + +
+ +Oftentimes, you do not need a factory to produce multiple objects - instead, you are using it to wrap sections of code together, hiding the variables and functions that you do not need elsewhere as private. This is easily achievable by wrapping your factory function in parentheses and immediately calling (invoking) it. This immediate function call is commonly referred to as an Immediately Invoked Function Expression (duh) or IIFE in short. This pattern of wrapping a factory function inside an IIFE is called the module pattern. + +```javascript +const calculator = (function () { + const add = (a, b) => a + b; + const sub = (a, b) => a - b; + const mul = (a, b) => a * b; + const div = (a, b) => a / b; + return { add, sub, mul, div }; +})(); + +calculator.add(3,5); // 8 +calculator.sub(6,2); // 4 +calculator.mul(14,5534); // 77476 +``` + +In this example, we have a factory function creating some basic operations that we need only once. We can wrap it in parentheses and immediately call it by adding `()` - returning the result object that we store in `calculator`. In this way we can write code, wrapping away things that we do not need as private variables and functions inside our factory function and while they are tucked inside of our module, we can use the returned variables and functions outside the factory, as necessary. + +#### Encapsulating with the module pattern + +At first glance, this does not seem particularly useful. If we have some code that we use only once, why not write it in the main section of our JavaScript file itself? After all, the power of factory functions lies in being, well, a factory to make multiple objects, right? + +This is where we encounter the word **encapsulation** - bundling data, code, or something into a single unit, with selective access to the things inside that unit itself. While it sounds general, this is what happens when we wrap, or encapsulate our code into modules - we don't expose everything to the body of our program itself. This encapsulation leads to an effect called **namespacing**. Namespacing is a technique that is used to avoid naming collisions in our programs. + +Take the calculator example into consideration. It's very easy to imagine a scenario where you can accidentally create multiple functions with the name `add`. What does `add` do - does it add two numbers? Strings? Does it take its input directly from the DOM and display the result? What would you name the functions that do these things? Instead, we can easily encapsulate them inside a module called `calculator` which generates an object with that name, allowing us to explicitly call `calculator.add(a, b)` or `calculator.sub(a, b)`. + +### Assignment + +
+ +1. WesBos has a beautiful and in-depth section on scopes and closures. Please check out these sections under "Module 3 - The Tricky Bits": + - [The article on scope](https://wesbos.com/javascript/03-the-tricky-bits/scope) + - [The article on closures](https://wesbos.com/javascript/03-the-tricky-bits/closures) +1. [Tarek Sherif's article discussing the problems with constructors](https://tsherif.wordpress.com/2013/08/04/constructors-are-bad-for-javascript/) goes into depth while suggesting factories as a solution. +1. Read this article on [module pattern in JavaScript](https://dev.to/tomekbuszewski/module-pattern-in-javascript-56jm) by Tomek Buszewski. +1. In case you prefer video lessons, [this YouTube series on module pattern](https://www.youtube.com/playlist?list=PLoYCgNOIyGABs-wDaaxChu82q_xQgUb4f) covers most of the content that we have discussed. + +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson on your own. If you’re having trouble answering a question, click it and review the material it links to. + +- [Explain how scope works in JavaScript.](https://wesbos.com/javascript-scoping) +- [Explain what closures are and how they help in creating private variables.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#closure) +- [Describe the common issues that you can face when working with constructors.](#so-whats-wrong-with-constructors) +- [Describe private variables in factory functions and how they can be useful.](#private-variables-and-functions) +- [Describe how we can implement prototypal inheritance with factory functions.](#prototypal-inheritance-with-factories) +- [Explain how the module pattern works.](https://dev.to/tomekbuszewski/module-pattern-in-javascript-56jm) +- [Describe IIFEs and what they stand for.](http://adripofjavascript.com/blog/drips/an-introduction-to-iffes-immediately-invoked-function-expressions.html) +- [Explain the concept of namespacing and how factory functions help with encapsulation.](#encapsulating-with-the-module-pattern) + +### Additional resources + +This section contains helpful links to related content. It isn’t required, so consider it supplemental. + +- [This video explains closures with a good example.](https://www.youtube.com/watch?v=80O6L2Ez3GM) +- [Here is an interactive scrim on factory functions.](https://scrimba.com/scrim/c2aaKzcV) +- [This article discusses three different kinds of prototypal inheritance with some good examples](https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9) +- [Learning JavaScript Design Patterns by Addy Osmani and Lydia Hallie](https://www.patterns.dev/) diff --git a/content/odin/javascript/organizing_your_javascript_code/json.md b/content/odin/javascript/organizing_your_javascript_code/json.md new file mode 100644 index 00000000..c2904080 --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/json.md @@ -0,0 +1,15 @@ +### Introduction + +JSON (JavaScript Object Notation) is a standardized format for structuring data. It is heavily based on the syntax for JavaScript objects. You will often encounter JSON formatted data when working with external servers or APIs - it is essentially the universal format for transmitting data on the web. + +Fortunately, there isn't much to learn here. We're only including a lesson on it because some formatting rules can cause confusion if you aren't aware of them. Spend 10-15 minutes going through the following resources and you'll be good to go. + +### Assignment + +
+ +1. This [JSON MDN tutorial]( https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON) is probably all you need... +2. Read about the 2 JavaScript methods that you'll most often be using when dealing with JSON - [JSON.parse()](https://www.w3schools.com/js/js_json_parse.asp) and [JSON.stringify()](https://www.w3schools.com/js/js_json_stringify.asp). +3. Mis-formatted JSON is a common cause of errors. This [JSON formatter website](https://jsonformatter.curiousconcept.com/) lets you paste in JSON code and will search it for formatting errors. + +
diff --git a/content/odin/javascript/organizing_your_javascript_code/objects_and_object_constructors.md b/content/odin/javascript/organizing_your_javascript_code/objects_and_object_constructors.md new file mode 100644 index 00000000..f5bc8a65 --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/objects_and_object_constructors.md @@ -0,0 +1,392 @@ +### Introduction + +In our JavaScript fundamentals course, you should have learned the [basics of using objects](https://www.theodinproject.com/paths/foundations/courses/foundations/lessons/fundamentals-part-5) to store and retrieve data. Let's start with a little refresher. + +There are multiple ways to define objects but in most cases, it is best to use the __object literal__ syntax as follows: + +```javascript +const myObject = { +  property: 'Value!', +  otherProperty: 77, +  "obnoxious property": function() { +    // do stuff! +  } +}; +``` + +There are also 2 ways to get information out of an object: dot notation and bracket notation. + +```javascript +// dot notation +myObject.property; // 'Value!' + +// bracket notation +myObject["obnoxious property"]; // [Function] +``` + +Which method you use will depend on context. Dot notation is cleaner and is usually preferred, but there are plenty of circumstances when it is not possible to use it. For example, `myObject."obnoxious property"` won't work because that property is a string with a space in it. Likewise, you cannot use variables in dot notation: + +```javascript +const variable = 'property'; + +myObject.variable; // this gives us 'undefined' because it's looking for a property named 'variable' in our object + +myObject[variable]; // this is equivalent to myObject['property'] and returns 'Value!' +``` + +If you are feeling rusty on using objects, now might be a good time to go back and review the content in [__Fundamentals 5__](https://www.theodinproject.com/lessons/foundations-fundamentals-part-5) from our JavaScript Basics course. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- How to write an object constructor and instantiate the object. +- Describe what a prototype is and how it can be used. +- Explain prototypal inheritance. +- Understand the basic do's and don't's of prototypal inheritance. +- Explain what `Object.create` does. +- Explain what the `this` keyword is. + +### Objects as a design pattern + +One of the simplest ways you can begin to organize your code is by grouping things into objects. Take these examples from a 'tic tac toe' game: + +```javascript +// example one +const playerOneName = "tim"; +const playerTwoName = "jenn"; +const playerOneMarker = "X"; +const playerTwoMarker = "O"; + +// example two +const playerOne = { +  name: "tim", +  marker: "X" +}; + +const playerTwo = { +  name: "jenn", +  marker: "O" +}; +``` + +At first glance, the first doesn't seem so bad.. and it actually takes fewer lines to write than the example using objects, but the benefits of the second approach are huge! Let me demonstrate: + +```javascript +function printName(player) { +  console.log(player.name); +} +``` + +This is something that you just could NOT do with the example one setup. Instead, every time you wanted to print a specific player's name, you would have to remember the correct variable name and then manually `console.log` it: + +```javascript +console.log(playerOneName); +console.log(playerTwoName); +``` + +Again, this isn't _that_ bad... but what if you _don't know_ which player's name you want to print? + +```javascript +function gameOver(winningPlayer){ +  console.log("Congratulations!"); +  console.log(winningPlayer.name + " is the winner!"); +} +``` + +Or, what if we aren't making a 2 player game, but something more complicated such as an online shopping site with a large inventory? In that case, using objects to keep track of an item's name, price, description and other things is the only way to go. Unfortunately, in that type of situation, manually typing out the contents of our objects is not feasible either. We need a cleaner way to create our objects, which brings us to... + +### Object constructors + +When you have a specific type of object that you need to duplicate like our player or inventory items, a better way to create them is using an object constructor, which is a function that looks like this: + +```javascript +function Player(name, marker) { +  this.name = name; +  this.marker = marker; +} +``` + +and which you use by calling the function with the keyword `new`. + +```javascript +const player = new Player('steve', 'X'); +console.log(player.name); // 'steve' +``` + +Just like with objects created using the Object Literal method, you can add functions to the object: + +```javascript +function Player(name, marker) { +  this.name = name; +  this.marker = marker; +  this.sayName = function() { +    console.log(this.name) +  }; +} + +const player1 = new Player('steve', 'X'); +const player2 = new Player('also steve', 'O'); +player1.sayName(); // logs 'steve' +player2.sayName(); // logs 'also steve' +``` + + + +### Exercise + +Write a constructor for making "Book" objects. We will revisit this in the project at the end of this lesson. Your book objects should have the book's `title`, `author`, the number of `pages`, and whether or not you have `read` the book. + +Put a function into the constructor that can report the book info like so: + +```javascript +theHobbit.info(); // "The Hobbit by J.R.R. Tolkien, 295 pages, not read yet" +``` + +Note: It is almost _always_ best to `return` things rather than putting `console.log()` directly into the function. In this case, return the `info` string and log it after the function has been called: + +```javascript +console.log(theHobbit.info()); +``` + +### The prototype + +Before we go much further, there's something important you need to understand about JavaScript objects. All objects in JavaScript have a `prototype`. The `prototype` is another object that the original object _inherits_ from, which is to say, the original object has access to all of its `prototype`'s methods and properties. + +Let's break it down. + +#### 1. All objects in JavaScript have a prototype + +Pretty straightforward sentence here! Every object in JavaScript has a `prototype`. So for example, the `player1` and `player2` objects from before, (created with the `Player(name, marker)` object constructor) also have a `prototype`. Now, what does having a `prototype` mean? What even is a `prototype` of an object? + +#### 2. The prototype is another object... + +This sentence also seems pretty straightforward! The `prototype` _is just another object_ - again, like the `player1` and the `player2` objects. The `prototype` object can have properties and functions, just as these `Player` objects have properties like `.name`, `.marker`, and functions like `.sayName()` attached to them. + +#### 3. ...that the original object _inherits_ from, and has access to all of its prototype's methods and properties + +Here, the "original object" refers to an object like `player1` or `player2`. These objects are said to "inherit", or in other words, these objects have access to the `prototype`'s properties or functions, if they have been defined. For example, if there was a `.sayHello()` function defined on the `prototype`, `player1` can access the function just as if it was its own function - `player1.sayHello()`. But it's not just `player1` who can call the `.sayHello()` function, even `player2` can call it, since it's defined on the `prototype`! Read on to know the details of how it works and how you could do this yourself! + +#### Accessing an object's prototype + +Conceptually, you now might feel like you know, or at least have an idea of what a `prototype` of an object is. But how do you _know_ or actually _see_ what the prototype of an object is? Let's find out. You can try running the following code in the developer console of your browser. (Make sure you've created the `player1` and `player2` objects from before!) + +```javascript +Object.getPrototypeOf(player1) === Player.prototype; // returns true +Object.getPrototypeOf(player2) === Player.prototype; // returns true +``` + +Now, to understand this code, let's use the three points from earlier: + +1. **All objects in JavaScript have a `prototype`**: + - You can check the object's `prototype` by using the [`Object.getPrototypeOf()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) function on the object, like `Object.getPrototypeOf(player1)`. + - The return value (result) of this function refers to the `.prototype` property of the Object Constructor (i.e., `Player(name, marker)`) - `Object.getPrototypeOf(player1) === Player.prototype`. +1. **The prototype is another object...** + - The _value_ of the Object Constructor's `.prototype` property (i.e., `Player.prototype`) contains the `prototype` object. + - The _reference_ to this value of `Player.prototype` is stored in every `Player` object, every time a `Player` object is created. + - Hence, you get a `true` value returned when you check the Objects prototype - `Object.getPrototypeOf(player1) === Player.prototype`. +1. **...that the original object _inherits_ from, and has access to all of its prototype's methods and properties**: + - As said in the earlier point, every `Player` object has a value which refers to `Player.prototype`. So: `Object.getPrototypeOf(player1) === Object.getPrototypeOf(player2)` (returns `true`). + - So, any properties or methods defined on `Player.prototype` will be available to the created `Player` objects! + +The last sub-item needs a little more explanation. What does defining 'on the `prototype`' mean? Consider the following code: + +```javascript +Player.prototype.sayHello = function() { + console.log("Hello, I'm a player!"); +}; + +player1.sayHello(); // logs "Hello, I'm a player!" +player2.sayHello(); // logs "Hello, I'm a player!" +``` + +Here, we defined the `.sayHello` function 'on' the `Player.prototype` object. It then became available for the `player1` and the `player2` objects to use! Similarly, you can attach other properties or functions you want to use on all `Player` objects by defining them on the objects' prototype (`Player.prototype`). + +#### Object.getPrototypeOf() vs. .\_\_proto__ vs. [[Prototype]] + +Unlike what we have done so far using `Object.getPrototypeOf()` to access an object's `prototype`, the same thing can also be done using the `.__proto__` property of the object. However, this is a non-standard way of doing so, and [deprecated](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto). Hence, it is not recommended to access an object's `prototype` by using this property. However, the same code can thus be rewritten to become: + +```javascript +// Don't do this! +player1.__proto__ === Player.prototype; // returns true +player2.__proto__ === Player.prototype; // returns true +``` + +In some places, like legacy code, you might also come across `[[Prototype]]`, which is just another way of talking about the `.__proto__` property of an object, like `player1.[[Prototype]]`. + +This explanation about the `prototype` might have been a lot, so remember to take a breather before moving on! + +#### Prototypal inheritance + +Now, you may also have a question - what use is an object's `prototype`? What is the purpose of defining properties and functions on the `prototype`? + +We can narrow it down to two reasons: + +1. We can define properties and functions common among all objects on the `prototype` to save memory. Defining every property and function takes up a lot of memory, especially if you have a lot of common properties and functions, and a lot of created objects! Defining them on a centralized, shared object which the objects have access to, thus saves memory. +1. The second reason is the name of this section, **Prototypal Inheritance**, which we've referred to in passing earlier, in the introduction to the Prototype. In recap, we can say that the `player1` and `player2` objects _inherit_ from the `Player.prototype` object, which allows them to access functions like `.sayHello`. + +Let's now try to do the following: + +```javascript +// Player.prototype.__proto__ +Object.getPrototypeOf(Player.prototype) === Object.prototype; // true + +// Output may slightly differ based on the browser +player1.valueOf(); // Output: Object { name: "steve", marker: "X", sayName: sayName() } +``` + +What's this `.valueOf` function, and where did it come from if we did not define it? It comes as a result of `Object.getPrototypeOf(Player.prototype)` having the value of `Object.prototype`! This means that `Player.prototype` is inheriting from `Object.prototype`. This `.valueOf` function is defined on `Object.prototype` just like `.sayHello` is defined on `Player.prototype`. + +How do we know that this `.valueOf` function is defined on `Object.prototype`? We make use of another function called `.hasOwnProperty`: + +```javascript +player1.hasOwnProperty('valueOf'); // false +Object.prototype.hasOwnProperty('valueOf'); // true +``` + +Now where did this [`.hasOwnProperty` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) come from? A quick check helps: + +```javascript +Object.prototype.hasOwnProperty('hasOwnProperty'); // true +``` + +Essentially, this is how JavaScript makes use of `prototype` - by having the objects contain a value - to point to `prototype`s and inheriting from those prototypes, and thus forming a chain. This kind of inheritance using prototypes is hence named as Prototypal inheritance. JavaScript figures out which properties exist (or do not exist) on the object and starts traversing the chain to find the property or function, like so: + +1. Is the `.valueOf` function part of the `player1` object? No, it is not. (Remember, only the `name`, `marker` and `sayName` properties are part of the `Player` objects.) +1. Is the function part of the `player1`'s prototype (the `Object.getPrototypeOf(player1)` value, i.e., `Player.prototype`)? No, only the `.sayHello` function is a part of it. +1. Well, then, is it part of `Object.getPrototypeOf(Player.prototype)` (=== `Object.prototype`)? Yes, `.valueOf` is defined on `Object.prototype`! + +However, this chain does not go on forever, and if you have already tried logging the value of `Object.getPrototypeOf(Object.prototype)`, you would find that it is `null`, which indicates the end of the chain. And it is at the end of this chain that if the specific property or function is not found, `undefined` is returned. + +Note: + +1. Every `prototype` object inherits from `Object.prototype` by default. +1. An object's `Object.getPrototypeOf()` value can only be _one_ unique `prototype` object. + +#### Recommended method for prototypal inheritance + +Now, how do you utilize Prototypal Inheritance? What do you need to do to use it? Just as we use `Object.getPrototypeOf()` to 'get' or view the `prototype` of an object, we can use `Object.setPrototypeOf()` to 'set' or mutate it. Let's see how it works by adding a `Person` Object Constructor to the `Player` example, and making `Player` inherit from `Person`! + +```javascript +function Person(name) { + this.name = name; +} + +Person.prototype.sayName = function() { +  console.log(`Hello, I'm ${this.name}!`); +}; + +function Player(name, marker) { + this.name = name; +  this.marker = marker; +} + +Player.prototype.getMarker = function() { + console.log(`My marker is '${this.marker}'`); +}; + +// Object.getPrototypeOf(Player.prototype) should +// return the value of "Person.prototype" instead +// of "Object.prototype" +Object.getPrototypeOf(Player.prototype); // returns Object.prototype + +// Now make `Player` objects inherit from `Person` +Object.setPrototypeOf(Player.prototype, Person.prototype); +Object.getPrototypeOf(Player.prototype); // returns Person.prototype + +const player1 = new Player('steve', 'X'); +const player2 = new Player('also steve', 'O'); + +player1.sayName(); // Hello, I'm steve! +player2.sayName(); // Hello, I'm also steve! + +player1.getMarker(); // My marker is 'X' +player2.getMarker(); // My marker is 'O' +``` + +From the code, we can see that we've defined a `Person` from whom a `Player` inherits properties and functions, and that the created `Player` objects are able to access both the `.sayName` and the `.getMarker` functions, in spite of them being defined on two separate `prototype` objects! This is enabled by the use of the `Object.setPrototypeOf()` function. It takes two arguments - the first is the one which inherits and the second argument is the one which you want the first argument to inherit from. This ensures that the created `Player` objects are able to access the `.sayName` and `.getMarker` functions through their prototype chain. + +Note: + +Though it seems to be an easy way to set up Prototypal Inheritance using `Object.setPrototypeOf()`, the prototype chain has to be set up using this function _before_ creating any objects. Using `setPrototypeOf()` after objects have already been created can result in performance issues. + +A warning... this doesn't work: + +```javascript +Player.prototype = Person.prototype; +``` + +because it will set `Player.prototype` to directly refer to `Person.prototype` (i.e. not a copy), which could cause problems if you want to edit something in the future. Consider one more example: + +```javascript +function Person(name) { + this.name = name; +} + +Person.prototype.sayName = function() { +  console.log(`Hello, I'm ${this.name}!`); +}; + +function Player(name, marker) { + this.name = name; +  this.marker = marker; +} + +// Don't do this! +// Use Object.setPrototypeOf(Player.prototype, Person.prototype) +Player.prototype = Person.prototype; + +function Enemy(name) { +  this.name = name; +  this.marker = '^'; +} + +// Not again! +// Use Object.setPrototypeOf(Enemy.prototype, Person.prototype) +Enemy.prototype = Person.prototype; + +Enemy.prototype.sayName = function() { + console.log('HAHAHAHAHAHA'); +}; + +const carl = new Player('carl', 'X'); +carl.sayName(); // Uh oh! this logs "HAHAHAHAHAHA" because we edited the sayName function! +``` + +If we had used `Object.setPrototypeOf()` in this example, then we could safely edit the `Enemy.prototype.sayName` function without changing the function for `Player` as well. + +### Assignment + +
+ +1. Read up on the concept of the prototype from the articles below. + 1. Read the article [Understanding Prototypes and Inheritance in JavaScript](https://www.digitalocean.com/community/tutorials/understanding-prototypes-and-inheritance-in-javascript) from Digital Ocean. This is a good review of prototype inheritance and constructor functions, featuring some examples. + 2. To go a bit deeper into both the chain and inheritance, spend some time with [JavaScript.Info's article on Prototypal Inheritance](http://javascript.info/prototype-inheritance). As usual, doing the exercises at the end will help cement this knowledge in your mind. Don't skip them! Important note: This article makes heavy use of `__proto__` which is not generally recommended. The concepts here are what we're looking for at the moment. We will soon learn another method or two for setting the prototype. +2. You might have noticed us using the `this` keyword in object constructors and prototype methods in the examples above. + 1. [Dmitri Pavlutin's article on the `this` keyword](https://dmitripavlutin.com/gentle-explanation-of-this-in-javascript/) is very comprehensive and covers how `this` changes in various situations. You should have a solid understanding of the concept after reading it. Pay special attention to the pitfalls mentioned in each section. +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + +- [Write an object constructor and instantiate the object.](#object-constructors) +- [Describe what a prototype is and how it can be used.](#the-prototype) +- [Explain prototypal inheritance.](https://javascript.info/prototype-inheritance) +- [Understand the basic do's and don't's of prototypal inheritance.](#recommended-method-for-prototypal-inheritance) +- [Explain what `Object.create` does.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) +- [How does `this` behave in different situations?](https://dmitripavlutin.com/gentle-explanation-of-this-in-javascript/) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +- [This article](https://dev.to/lydiahallie/javascript-visualized-prototypal-inheritance-47co) from Lydia Hallie and [This video](https://www.youtube.com/watch?v=sOrtAjyk4lQ) from Avelx explains the Prototype concept with graphics and beginner friendly language. Try using these resources if you want another perspective to understand the concept. +- [This video](https://www.youtube.com/watch?v=CDFN1VatiJA) from mpj explains `Object.create` method with great details about it, he walks through what it is, why `Object.create` exists in JavaScript, and how to use `Object.create`. Also you can check [This video](https://www.youtube.com/watch?v=MACDGu96wrA) from techsith to understand another point of view of extending objects from others by `Object.create`. +- [The Principles of Object-Oriented JavaScript](https://www.amazon.com/Principles-Object-Oriented-JavaScript-Nicholas-Zakas/dp/1593275404) book by +Nicholas C. Zakas is really great to understand OOP in javascript, which explains concepts in-depth, which explores JavaScript's object-oriented nature, revealing the language's unique implementation of inheritance and other key characteristics, it's not free but it's very valuable. +- [This stack overflow question](https://stackoverflow.com/questions/9772307/declaring-javascript-object-method-in-constructor-function-vs-in-prototype/9772864#9772864) explains the difference between defining methods via the prototype vs defining them in the constructor. +- [A Beginner’s Guide to JavaScript’s Prototype](https://medium.com/free-code-camp/a-beginners-guide-to-javascript-s-prototype-9c049fe7b34) and [JavaScript Inheritance and the Prototype Chain](https://medium.com/free-code-camp/javascript-inheritance-and-the-prototype-chain-d4298619bdae) from Tyler Mcginnis has great examples to help you understand Prototype and Prototype Chain better from the beginner's perspective. +- [This video ](https://www.youtube.com/watch?v=wstwjQ1yqWQ) from Akshay Saini is an easy way to understand the concept of Prototype, Prototype Chain and prototypal inheritance. +- [Interactive Scrim on objects and object constructors.](https://scrimba.com/scrim/co2624f87981575448091d5a2) +- [Check out this video explanation](https://www.youtube.com/watch?v=cwChC4BQF0Q) on the `this` keyword from DevSage that gives a different perspective on how its context changes, as well as scenarios in which `this` behaves unexpectedly. diff --git a/content/odin/javascript/organizing_your_javascript_code/oop_principles.md b/content/odin/javascript/organizing_your_javascript_code/oop_principles.md new file mode 100644 index 00000000..2c319c42 --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/oop_principles.md @@ -0,0 +1,94 @@ +### Introduction + +By this point, you will have learned and had a chance to practice the most common object-creation and organization patterns in JavaScript. But that is just the _tip_ of the iceberg. More important than learning the syntax for factory functions or modules is figuring out how to use them effectively. + +This whole series of lessons has been about the "Object Oriented Programming" paradigm (OOP). The basics of creating objects and classes are relatively straightforward. But it is not straightforward to decide what to put in each object, or when to make a new object, or when to let an object 'inherit' from another one. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Explain the "Single Responsibility Principle". +- Briefly explain the additional SOLID principles. +- Explain what "tightly coupled" objects are and why we want to avoid them. + +Luckily there are several concepts and principles that can guide us into making good decisions when it comes to our objects. This lesson is an introduction to the most important of those concepts. Keep in mind that there is not usually a very clear answer to your application design questions. Some patterns and ideas are obviously better than others, but there is often some trade-off when deciding where to put a specific function. In other words... these principles are not _rules_- they're helpful guidelines.   + +As you read these resources, it might help to go back to some projects you've already done and think about how what you've written measures up to the examples you see. And of course, as you move on, keep these things in mind when crafting new projects. + +### Single responsibility + +As you craft your objects, one of the most important things to remember is the __Single Responsibility Principle__ which states that a class (or object or module... you get the point) should only have _one_ responsibility. This doesn't mean that an object can only do one thing, but it does mean that everything an object does should be part of one responsibility. + +Here's a really common example. Most of our code has functions to update and write things to the DOM in addition to our application logic. It's a _really_ good idea to separate your DOM stuff from the application logic. + +Here we have a function that should check if a game over condition has been met. There are two issues with this: + +```javascript +function isGameOver() { + +  // game over logic goes here! + +  if (gameOver) { +    const gameOverDiv = document.createElement('div'); +    gameOverDiv.classList.add('game-over'); +  gameOverDiv.textContent = `${this.winner} won the game!`; +    document.body.appendChild(gameOverDiv); +  } +} +``` + +The first issue is that the function (and the module it's in) should not directly be the one to manipulate the DOM. You should extract all the DOM manipulation into its own module and use it like so: + +```javascript +function isGameOver() { + +  // game over logic goes here! + +  if (gameOver){ +    DOMStuff.gameOver(this.winner); +  } +} +``` + +The second issue remaining is that the `isGameOver` function should only be responsible for checking if the `gameOver` condition is met. Based on `isGameOver` return value, the function that handles the game loop should be responsible for deciding whether to call `DOMStuff.gameOver(this.winner)` or not. + +Another way to think about the Single Responsibility Principle is that a given method/class/component should have a single reason to change. Otherwise, if an object is trying to have multiple responsibilities, changing one aspect might affect another. + +The Single Responsibility Principle is the first of a commonly found set of 5 design principles called the __SOLID__ principles. You will read more about these principles in the assignment articles below. + + +### Loosely coupled objects + +Obviously, all of our objects are intended to work together to form our final application. You should take care, however, to make sure that your individual objects can stand alone as much as possible. __Tightly coupled__ objects are objects that rely so heavily on each other that removing or changing one will mean that you have to completely change another one - a real bummer. + +This one is related pretty strongly to 'Single Responsibility' but takes a different angle. As an example, if we were writing a game and wanted to completely change how the User Interface worked, we should be able to do that without completely reworking the game logic. So we should be able to start off writing our game using primarily `console.log()`s and then add in a bunch of `DOM` functions later without touching the game logic. + + +### Assignment + +
+ +1. The following article mentions the acronym __SOLID__ before going on to talk about Single Responsibility. Single Responsibility is definitely the most relevant of the 5. Feel free to dig into the rest of the SOLID principles if you like, but pay special attention to Single Responsibility. + 1. [SOLID principle #1: Single responsibility (JavaScript)](https://duncan-mcardle.medium.com/solid-principle-1-single-responsibility-javascript-5d9ce2c6f4a5) has links to other very brief articles that cover the rest of 'SOLID'. They're optional, but recommended nonetheless. __Note__: this article riffs off what the SOLID videos in the next link goes in-depth on. + 2. Watch [The SOLID Design Principles by WDS](https://www.youtube.com/playlist?list=PLZlA0Gpn_vH9kocFX7R7BAe_CvvOCO_p9) to see code examples for each principle. +2. [How to Write Highly Scalable and Maintainable JavaScript: Coupling](https://web.archive.org/web/20200810210808/https://medium.com/@alexcastrounis/how-to-write-highly-scalable-and-maintainable-javascript-coupling-c860787dbdd4) explains loosely coupled objects pretty well. +
+ +### Knowledge check + +This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer. + +- [Explain the "Single Responsibility Principle".](#single-responsibility) +- [Briefly explain the additional SOLID principles.](https://medium.com/@cramirez92/s-o-l-i-d-the-first-5-priciples-of-object-oriented-design-with-javascript-790f6ac9b9fa) +- [Explain what "tightly coupled" objects are and why we want to avoid them.](https://web.archive.org/web/20200810210808/https://medium.com/@alexcastrounis/how-to-write-highly-scalable-and-maintainable-javascript-coupling-c860787dbdd4) + +### Additional resources + +This section contains helpful links to other content. It isn't required, so consider it supplemental. + +- The best book we've ever read on the subject of loose coupling is [Practical Object-Oriented Design In Ruby](http://www.poodr.com/). Unfortunately, it is not free... and not JavaScript. We feel confident in recommending it anyway. If you don't know Ruby, it is a clear enough language that you don't really need to learn it to follow the examples and the content of the book is sincerely fantastic. Alternatively, [99 Bottles of OOP](https://sandimetz.com/products) is written in both JavaScript and Ruby. It is written by the same author and may be a better option if you are brand new to OOP (it is not free either). + +- [Building a house from the inside out](https://www.ayweb.dev/blog/building-a-house-from-the-inside-out) will walk you through the process of separating your core logic and DOM logic. + +- This [brief video by Coderized](https://www.youtube.com/watch?v=q1qKv5TBaOA) covers the SOLID programming principles and more, within the context of embracing clean coding practices and establishing a maintainable code structure. You may find it helpful if you are still confused about why these principles exist and how they can work together to improve your code, code architecture, and your skills as a programmer! diff --git a/content/odin/javascript/organizing_your_javascript_code/organizing_your_javascript_code_introduction.md b/content/odin/javascript/organizing_your_javascript_code/organizing_your_javascript_code_introduction.md new file mode 100644 index 00000000..f93b901a --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/organizing_your_javascript_code_introduction.md @@ -0,0 +1,16 @@ +### Organizing your JavaScript code + +One of the most daunting parts of JavaScript is learning how to organize your code. The reason this subject can be so overwhelming is _not_ because JavaScript is so much more complex than other languages, but because it is incredibly forgiving! Many languages force you into using specific patterns and data structures in your code, but that is not true in JavaScript. + +In the beginning, this is a great thing! For example, if you just want to make a button on your webpage do something, you can set that up in a couple lines of code. However, as your program becomes more complex, it can become hard to maintain unless you take care to organize your code, and because JavaScript is such a flexible language, how you do that is entirely up to you. For many coders, making decisions about design patterns is crippling, so we're here to help. + +This lesson series is going to cover a few of the most common design patterns that occur in modern JavaScript code. We will discuss some pros and cons of each pattern and will give you a chance to practice using each pattern in a project. + +The patterns we'll be covering in this series are: + +- Plain Old JavaScript Objects and Object Constructors +- Factory Functions and the Module Pattern +- Classes +- ES6 Modules + +Going through these will give us a chance to learn about a few other important concepts in JavaScript such as "closure", "prototypes", "IIFEs" and more! This series covers the most important parts of JavaScript after learning the basics of the language... are you ready? diff --git a/content/odin/javascript/organizing_your_javascript_code/project_library.md b/content/odin/javascript/organizing_your_javascript_code/project_library.md new file mode 100644 index 00000000..69d15173 --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/project_library.md @@ -0,0 +1,33 @@ +### Introduction + +Let's extend the 'Book' example from the previous lesson and turn it into a small Library app. + +### Assignment + +
+ +1. If you haven't already, set up your project with skeleton HTML/CSS and JS files. +2. All of your book objects are going to be stored in an array, so add a function to the script (not the constructor) that can take user's input and store the new book objects into an array. Your code should look something like this: + + ```javascript + const myLibrary = []; + + function Book() { + // the constructor... + } + + function addBookToLibrary() { + // do stuff here + } + ``` + +3. Write a function that loops through the array and displays each book on the page. You can display them in some sort of table, or each on their own "card". It might help for now to manually add a few books to your array so you can see the display. +4. Add a "NEW BOOK" button that brings up a form allowing users to input the details for the new book: author, title, number of pages, whether it's been read and anything else you might want. How you decide to display this form is up to you. For example, you may wish to have a form show in a sidebar or you may wish to explore [dialogs and modals](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) using the `` tag. However you do this, you will most likely encounter an issue where submitting your form will not do what you expect it to do. That's because the `submit` input tries to send the data to a server by default. If you've done the bonus section for the calculator assignment, you might be familiar with `event.preventDefault();`. Read up on the [event.preventDefault documentation](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) again and see how you can solve this issue! +5. Add a button on each book's display to remove the book from the library. + 1. You will need to associate your DOM elements with the actual book objects in some way. One easy solution is giving them a data-attribute that corresponds to the index of the library array. +6. Add a button on each book's display to change its `read` status. + 1. To facilitate this you will want to create the function that toggles a book's `read` status on your `Book` prototype instance. + + +NOTE: You're not required to add any type of storage right now. You will have the option to come back to this project later on in the course. +
diff --git a/content/odin/javascript/organizing_your_javascript_code/project_restaurant_page.md b/content/odin/javascript/organizing_your_javascript_code/project_restaurant_page.md new file mode 100644 index 00000000..0cde084d --- /dev/null +++ b/content/odin/javascript/organizing_your_javascript_code/project_restaurant_page.md @@ -0,0 +1,56 @@ +### Introduction + +Let's use what we've learned and take a chance to continue practicing DOM manipulation by dynamically rendering a restaurant homepage! By the end, we are going to be using JavaScript alone to generate the entire contents of the website! + +**Note: DOM elements should be created using JavaScript but styling can be done in a separate CSS file.** + +### Assignment + +
+ +1. Start the project the same way you began the webpack tutorial project. + 1. run `npm init` in your project directory to generate a `package.json` file. + + 1. run `npm install webpack webpack-cli --save-dev` to install webpack to the node_modules directory of your project. + + - Quick tip: the `node_modules` folder can get *really* big. It is customary to add a `.gitignore` file to your project so that you don't have to sync the contents of `node_modules` to github. The dependencies that are stored there can be installed from your package.json by running `npm install`, so you don't need to sync them. + + 1. Create a `src` and `dist` directory with the following contents: + 1. an `index.js` file in `src` + + 1. an `index.html` file in `dist`. Go ahead and link the `main.js` file in a script tag. `main.js` is the file that will be generated by webpack. + + 1. create a `webpack.config.js` file that looks just like our file from the [tutorial](https://webpack.js.org/guides/getting-started/#using-a-configuration). + +1. Set up an HTML skeleton inside of `dist/index.html`. Inside the body, add a `
` element that contains a `