feat(resources): split up topics and languages

This commit is contained in:
Jean-Philippe Sirois
2020-05-27 02:42:39 -04:00
parent 8fba0edc4f
commit 135452bd4b
35 changed files with 86 additions and 55 deletions
@@ -0,0 +1,87 @@
---
authors:
- "T0M#5956"
- "Hayden#5036"
created_at: 2019/10/08
updated_at: 2019/10/13
title: Arrow functions
---
## Arrow Function Syntax
Brought into ES6, arrow functions are a new way to declare functions, and allow for shorter syntax. For example, below is a regular function, as usually seen before ES6.
```js
const sayHello = function () {
console.log("hello");
};
```
The same function can be rewritten with arrow function syntax, as seen below.
```js
const sayHello = () => {
console.log("hello");
};
```
Parameters are listed between the parentheses, in the same way a regular function does it:
```js
const sayHello = (nameOne, nameTwo) => {
console.log(nameOne, "says hello to", nameTwo);
};
```
For a single parameter, the parentheses can be omitted:
```js
const sayHello = (name) => {
console.log("hello", name);
};
```
Another interesting feature that also reduces the syntax within arrow functions, is that if the function is an expression, you can omit the `return` keyword and the curly braces. In arrow functions without braces, the `return` is implicit, meaning you don't need to include the `return` keyword. The following function returns "hello":
```js
const sayHello = () => "hello";
```
The same is also true of functions returning an expression using parameters, too.
```js
const sayHello = (name) => `hello ${name}`;
```
Or even...
```js
const helloObject = (name) => ({
isGreeting: true,
helloName: name,
});
```
This is essentially wrapping an object expression inside a grouped expression, causing it to return an object instead of expanding into a whole function body.
Long story short, we heard you liked expressions, so we put an expression inside your expression to give you nicer expressions.
## Handling of the Keyword This
The keyword `this` is handled differently in arrow functions. In an arrow function, `this` inherits its binding from the parent scope. That means that the keyword `this` inside of an arrow function references the same object that it does immediately outside of the arrow function where the arrow function is declared. On the other hand, when you use the older `function` syntax, `this` typically refers to the object that the function was called on, if the function is called as an instance method. If the function is not called as an instance method, `this` will usually be undefined (though it is possible to use functions like `call` or `bind` to manually provide a binding).
Using regular anonymous function, `this` is refers to the `HTMLButtonObject` that called it, and therefore the function outputs `[object HTMLButtonElement]` to the console.
```js
document.querySelector("#btn").addEventListener("click", function () {
console.log(this); // outputs "[object HTMLButtonElement]"
});
```
However, if an arrow function is used, `this` would refer to `[object Window]`, as that is the object that defined the function.
```js
document.querySelector("#btn").addEventListener("click", () => {
console.log(this); // outputs "[object Window]"
});
```
@@ -0,0 +1,33 @@
---
authors:
- "veksen#1565"
created_at: 2019/12/15
title: Javascript
---
###### Documentation
- [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
- [jQuery](https://contribute.jquery.org/documentation/)
- [NodeJS](https://nodejs.org/en/docs/)
- [Typescript](https://www.typescriptlang.org/docs/home.html)
- [Discord.js](https://discord.js.org/#/docs/main/stable/general/welcome)
###### Tutorials
- [Eloquent](https://eloquentjavascript.net/)
- [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS)
- [MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps)
- [Modern JS](https://javascript.info/)
- [Evie's Accelerated JS Tutorial](https://evie.gitbook.io/js/)
###### Discord.js
- [An Idiot's Guide](https://anidiots.guide/)
- [Discord.js Guide](https://discordjs.guide/)
###### Other
- [You might not need jQuery](https://youmightnotneedjquery.com/)
- [You might not need jQuery 2](https://github.com/you-dont-need-x/you-dont-need-jquery)
- [CodingTrain | tutorials with examples using p5.js](https://www.youtube.com/user/shiffman)
@@ -0,0 +1,232 @@
---
authors:
- "veksen#1060"
- "supergrecko#3434"
created_at: "2019/07/27"
title: Iterative vs Functional array helpers
---
This article assumes that you are comfortable with the very basics of JavaScript arrays, and how they differ to objects. This article teaches you how to use the built-in functions for arrays.
Each of the functions described in this article use a callback function. If you are not familiar with callback functions I would advise you to read [this article by Mozilla](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function) before continuing.
## Getting a specific element using `find()`
It's not rare to need to look for some specific element based on a criteria. `find()` makes this particularly easy. It takes a function, taking a few arguments:
- current item
- index of current item (optional)
- original array reference (optional)
The `.find()` function loops over the items, if the callback function evaluates to truthy the item is returned, and the loop ends.
If no items evaluated to truthy `undefined` will be returned.
If you're only interested in the presence of an element, consider using `some()`, which instead returns a boolean.
Conventionally, in an iterative approach, this would be done using a preset variable, and looping through our array.
```js
// given our data
const users = [
{ name: "Joe", age: 25 },
{ name: "John", age: 17 },
{ name: "Jane", age: 16 },
];
// functional way
const foundUser = users.find((user) => user.name === "John");
// iterative way
let foundUser = null;
users.forEach((user) => {
if (user.name === "John") {
foundUser = user;
}
});
console.log(foundUser); // outputs { name: "John", age: 17 }
```
## Keep specific items using `filter()`
`filter()` makes it easy to keep specific items based on a criteria.
It takes a function, taking a few arguments:
- current item
- index of current item (optional)
- original array reference (optional)
`filter()` does not modify (mutate) the original array, instead, it returns a new one.
If the callback function evaluates to truthy, this specific item is pushed to the final array, otherwise it is ignored.
```js
// given our data
const users = [
{ name: "Joe", age: 25 },
{ name: "John", age: 17 },
{ name: "Jane", age: 16 },
];
// functional way
const youngerUsers = users.filter((user) => user.age < 18);
// iterative way
const youngerUsers = [];
users.forEach((user) => {
if (user.age < 18) {
youngerUsers.push(user);
}
});
console.log(youngerUsers); // outputs [ { name: "John", age: 17 }, { name: "Jane", age: 16 } ]
```
## Modifying all elements of an array using `map()`
It's common to want to modify every element of an array with some logic, and `map()` makes this easy.
It takes a function, taking a few arguments:
- current item
- index of current item (optional)
- original array reference (optional)
The function loops through the array and runs the callback function on each element. Just like the `filter()` function, `map()` does not mutate the original array.
```js
// given our data
const users = [
{ name: "Joe", age: 25 },
{ name: "John", age: 17 },
{ name: "Jane", age: 16 },
];
// functional way
const userNames = users.map((user) => user.name);
// iterative way
const userNames = [];
users.forEach((user) => {
userNames.push(user.name);
});
console.log(userNames); // outputs [ "Joe", "John", "Jane" ]
```
## Running custom logic using an array using `reduce()`
`reduce()` is often less understood, but it's not that complicated once you get the basics. Itself, it takes 2 arguments, a function, and an initial value. The function takes a few arguments:
- accumulator, that is a reference to the current value that was last returned, or the initial value
- current value, the currently looped element
- current index (optional)
- original array (optional)
The `reduce()` function returns the accumulative result after the callback has been ran on each element.
```js
// given our data
const users = [
{ name: "Joe", age: 25 },
{ name: "John", age: 17 },
{ name: "Jane", age: 16 },
];
// functional way
const totalAge = users.reduce((acc, user) => acc + user.age, 0);
// iterative way
let totalAge = 0;
users.forEach((user) => {
totalAge += user.age;
});
console.log(totalAge); // outputs 58
```
## Checking if any element matches a condition with `some()`
`some()` is very similar to `find()`, except it returns a boolean on a match.
Conventionally, we would prepare some variable with a value of false, loop over all of the elements, and exit the loop once we find a match. The function takes a callback function which accepts some parameters.
- the current element
- the array index of the current element (optional)
- a reference to the array (optional)
```js
// given our data
const users = [
{ name: "Joe", age: 25 },
{ name: "John", age: 17 },
{ name: "Jane", age: 16 },
];
// functional way
const hasYoungUsers = users.some((user) => user.age < 18);
// iterative way
let hasYoungUsers = false;
for (let i = 0; users.length > i; i++) {
if (user.age < 18) {
hasYoungUsers = true;
break;
}
}
console.log(hasYoungUsers); // outputs true
```
## Checking if all elements match a condition with `every()`
`every()` is similar to `some()`. The difference is that `some()` test if one or more of the items match. `every()` tests if every item in the array matches.
```js
// given our data
const users = [
{ name: "Joe", age: 25 },
{ name: "John", age: 17 },
{ name: "Jane", age: 16 },
];
// functional way
const allUsersAreOldEnough = users.every((user) => user.age < 18);
// iterative way
let allUsersAreOldEnough = true;
for (let i = 0; users.length > i; i++) {
if (user.age < 18) {
allUsersAreOldEnough = false;
break;
}
}
console.log(allUsersAreOldEnough); // outputs false
```
## Checking if an array contains a value with `includes()`
The `includes()` function tests if the passed item exists inside the array. It returns a boolean value.
```js
// given our data
const pets = ["cat", "dog", "bat"];
// functional way
const found = pets.includes("dog");
// iterative way
let found = false;
pets.forEach((pet) => {
if (pet === "dog") {
found = true;
}
});
// or most of the time done with indexOf
const found = pets.indexOf("dog") >= 0;
console.log(found); // outputs true
```
@@ -0,0 +1,170 @@
---
authors:
- "ddivad#0001"
- "veksen#1060"
created_at: "2019/10/01"
updated_at: 2020/05/20
title: Async/Await
recommended_reading:
- javascript/promises/intro
---
## Async/Await
Async/Await is another way to handle the calling of asynchronous code and is built on top of Promises. It makes it possible to write asynchronous code that feels synchronous.
**Its usage is equivalent to resolving a promise with `.then()`**.
It is made up of 2 keywords as the name suggests: `async` and `await`. These need to be used together for this method to work.
## Async
The `async` keyword is used to show that the function is going to return a promise. **Any return values from the function will be converted to a promise automatically**, if they are not already.
```js
async function getWeather() {
return "sunny";
}
getPromise();
// Promise<"sunny">
```
This basic example will return a promise with the value of `getWeather`.
## Await
The `await` keyword is used to wait until a promise has executed and fetches the result. In order to use the `await` keyword, you **need** to be inside an `async` function.
```js
async function checkWeather() {
const weather = await getWeather();
console.log(weather); // "sunny"
}
```
This code will call the `getWeather()` function from above and will wait on that line until the promise returned from the `async` function has returned.
Another example to demonstrate this is if we extended the `getWeather()` function from above to add a 5 second delay.
```js
async function getWeather() {
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("sunny"), 5000);
});
const weather = await promise; // Execution waits here until promise resolves
console.log(weather); // "sunny"
}
```
## Error Handling
As with Promises, if the Promise returns with an error, it will affect the `await` call that triggered it. When using Promises without Async / Await, the `.catch()` syntax is used, and this also exists with Aysnc / Await using the `try...catch` syntax. This can be demonstrated by making our `foo()` function throw an error.
```js
async function foo() {
throw new Error("An Error Occurred");
}
```
This would be handled using Aysnc / Await like so:
```js
async function getData() {
try {
let data = await foo();
console.log(data);
} catch (err) {
console.error(err); // err: An Error Occurred
}
}
```
## Beginner Mistakes
As mentioned in the [intro](./intro.md), Promises are the highest source of confusion for beginners. Async / Await adds another layer on top of Promises, and comes with its own pitfalls.
`await` will **only** work if the function you try to add `await` to is `async`. Using `await` in top level code will not work (yet).
```js
let weather = await getWeather(); // syntax error
```
To get around this issue, you can use an declare asynchronous function anonymously, if needed:
```js
(async () => {
let weather = await getWeather();
console.log(weather); // "sunny"
})();
```
Top-Level-Await is something that _may_ get added to Javascript in the future, but for now wrapper functions like above are needed for this functionality.
## Real World Example
Here is a real function that gets character information from the Rick and Morty API.
You can try it in your browser if you want to test it out.
```js
async function getCharacters() {
const response = await fetch(`https://rickandmortyapi.com/api/character`);
const data = await response.json();
return data.results;
}
// or, mixed
async function getCharacters() {
const data = await fetch(
`https://rickandmortyapi.com/api/character`
).then((res) => res.json());
return data.results;
}
(async () => {
try {
let characters = await getCharacters();
console.log(characters); // (20) [{...}, {...}, {...}]
} catch (err) {
console.error(err);
}
})();
```
In this example, we are querying real data from the Rick & Morty API. This API has a lot of information regarding Rick & Morty, but here, we are trying to retrieve all the characters.
- The first thing we do is use `fetch` to retrieve the data from the API. `fetch` is a built in function in web browsers to do http requests, and returns a Promise by default. We `await` the result of this.
- When we get the result, we need to get the `JSON` value of the data. To do this, the `.json()` method from fetch is used. We then return the results.
- As `await` can only be used in an `async` function, we use the method from above to make this work. As `getCharacters()` returns a Promise due to it being `async`, we `await` the result. We surround this in a `try...catch` in case fetch returns an error. Then, if no error is returned, `characters` contains the information we want, and is logged to the console. If `getCharacters()` returns an error, that error is also logged.
### Let's also look at an example from Discord.js:
```js
client.on("message", (msg) => {
if (msg.author.bot) return;
if (msg.content === "ping") {
const message = await msg.channel.send("pong");
message.react("⚠️");
}
})
```
## Comparision with default Promises
Below is the same code implemented without using Async / Await as a comparision. Here, you can see the differences between the two methods, and how Aysnc / Await makes the code appear in a _more synchronous_ pattern, and can be clearer to follow.
```js
function getCharacters() {
return fetch(`https://rickandmortyapi.com/api/character`)
.then((res) => res.json())
.then((res) => res.results);
}
getCharacters()
.then((characters) => {
console.log(characters); // (20) [{...}, {...}, {...}]
})
.catch((err) => console.error(err));
```
@@ -0,0 +1,63 @@
---
authors:
- "veksen#1060"
created_at: "2020/05/20"
title: Converting a callback
recommended_reading:
- javascript/promises/intro
- javascript/promises/async-await
---
It's still pretty common to be forced to use an API that doesn't support promises. While a promise API is often preferred, it's not always available.
Before anything check:
- If your library might support both callbacks and API, in the documentation, or README from Github
- If there is not another library that does the same thing, but supports promises.
## From a callback
Let's look at a conventional, but fictional callback API:
```js
getWeather("Los Angeles", (error, result) => {
if (err) throw err;
console.log(result); // "sunny"
});
```
## To a promise
If we want to use it as a promise, we'll need to wrap a promise around it:
```js
function getWeatherAsync(city) {
return new Promise((resolve, reject) => {
getWeather(city, (error, result) => {
if (err) reject(err);
resolve(result);
});
});
}
```
Which we can now use:
```js
getWeatherAsync("Los Angeles")
.then((weather) => {
console.log(weather);
})
.catch((error) => {
console.log(error);
});
// or using async/await
const weather = await getWeatherAsync("Los Angeles").catch((error) => {
console.log(error);
});
console.log(weather);
```
@@ -0,0 +1,112 @@
---
authors:
- "Xetera#0001"
- "veksen#1060"
created_at: 2019/07/26
updated_at: 2020/05/20
title: Introduction to Promises
recommended_reading:
- javascript/callbacks/intro
- javascript/es6/arrow-functions
---
## A Promise to Keep
A `Promise` in Javascript represents an action that has already started, but one that will be
completed at a later time. Much like in real life, when you create a promise, you are expected
to fulfill that promise. However, sometimes things go wrong where you can no longer fulfill
a promise you made. This is essentially the main idea behind how promises work in javascript.
## Basics
When you create a Promise or call a function that returns a Promise in Javascript, you're left
with an object that can either resolve into the actual value that you were promised, or it
can reject and leave you with an error for why that promise failed.
We can access these values using the `.then` and `.catch` methods on the `Promise` object respectively.
## A Simple Example
First, let's explore a bit of a made-up example. Imagine we have a promise-returning function
called `getMembers` that retrieves all the members in a discord server. When we execute this
function we see the following result.
```js
const members = getMembers("The Programmers Hangout");
console.log(members); // Promise {<pending>}
```
Normally, we would have expected to see an array of all the members but it takes time to
get all the information about members so we're instead returned a Promise of members, rather
than the members themselves.
In order to access this information, we'll have to call the `.then` method on our `members` object
to access the actual members like so.
```js
getMembers("The Programmers Hangout").then((members) => {
console.log(members); // (32k) [{...}, {...}, {...}]
});
```
This way we are able to make sure that we only try to `console.log` when the `getMembers` function has resolved and ready to be used.
## Beginner Mistakes
Promises are possibly the #1 most common source of confusion for beginners. In order
to avoid falling in pitfalls yourself, you have to remember 2 things about Javascript when
working with promises.
1. Javascript does not wait.
2. No seriously, Javascript won't wait for your promises!
You may have tried doing something like this before.
```js
// Incorrect code, don't copy!
let results;
getWeather("Los Angeles").then((weather) => {
results = weather;
});
console.log(results); // undefined
```
Why is `results` undefined? Because **Javascript doesn't wait**. Whenever a Promise is created,
your code will continue to run until there's no more code left in the stack. Only then
will javascript try to run the `.then` callback of a Promise. Even if your Promise resolves
immediately you are going to have to wait until you've run all the code in the stack before
your `.then` callback has a chance to start running. This is due to the way the event loop works,
you can watch [this amazing talk](https://youtu.be/8aGhZQkoFbQ) on it to learn more.
In order to fix this problem we need to move the `console.log` inside the `.then` callback like so:
```js
getWeather("Los Angeles").then((weather) => {
console.log(weather); // Sunny, probably
});
```
Outlining this one again, because it's very common, is to attempt to use the value of a promise, but not resolving it:
```js
// Incorrect code, don't copy!
const weather = getWeather("Los Angeles");
console.log(weather); // Promise {<pending>}
```
## Real World Example
Here is a real function that gets character information from the Rick and Morty API.
You can try it in your browser if you want to test it out.
```js
function getCharacters() {
return fetch(`https://rickandmortyapi.com/api/character`)
.then((response) => response.json())
.then((response) => response.results);
}
getCharacters().then((characters) => {
console.log(characters); // (20) [{...}, {...}, {...}]
});
```
@@ -0,0 +1,72 @@
---
authors:
- "Xetera#0001"
created_at: "2019/07/26"
title: Simplifying Promises
---
The first naive attempt, using new Promise for something that already returns a promise.
```js
function doAsync(number) {
return new Promise(function (resolve, reject) {
doDatabase().then(function (dbResult) {
otherDbFunction(dbResult).then(function (secondResult) {
resolve(secondResult + 10);
});
});
});
}
```
Turns out you don't need new Promise if you're working with a function that
already returns a promise, you can just return the original thing.
```js
function doAsync(number) {
return doDatabase().then(function (dbResult) {
otherDbFunction(dbResult).then(function (secondResult) {
return secondResult + 10;
});
});
}
```
You also don't have to nest `.then` functions, the whole point of promises
is that they allow you to chain them sequentially.
```js
function doAsync(number) {
return doDatabase()
.then(function (dbResult) {
return otherDbFunction(dbResult);
})
.then(function (secondResult) {
return secondResult + 10;
});
}
```
you also don't have to create a new function just to pass in one
variable, you can pass in the entire function itself to the then block
```js
function doAsync(number) {
return doDatabase()
.then(otherDbFunction)
.then(function (secondResult) {
return secondResult + 10;
});
}
```
And you don't need those returns if you just have ES6 arrow functions
```js
const doAsync = (number) =>
doDatabase()
.then(otherDbFunction)
.then((secondResult) => secondResult + 10);
```
Wow, that last one looks a lot cleaner to me than the first. Keeping that in mind, maybe we could be making some of our other functions cleaner as well
@@ -0,0 +1,120 @@
---
authors:
- "Aiden#8627"
title: "Spread operator"
created_at: 2019/08/10
external_resources:
- text: MDN Spread Operator
href: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax"
- text: Javascript.info Spread/rest
href: "https://javascript.info/rest-parameters-spread-operator"
- text: Freecodecamp.org Spread/rest
href: "https://www.freecodecamp.org/news/spread-operator-and-rest-parameter-in-javascript-es6-4416a9f47e5e/"
---
Spread operator (or spread syntax) is a powerful feature in Javascript which allows you to do such things as merging or copying objects, expanding an array into function arguments and a lot more. In this post, we are going to cover most of its use-cases.
## Copying an object
In Javascript, every primitive is copied when passed around. However, objects (arrays are also objects), gets their reference copied. Which means that if you're modifying an object, the original one is modified too. For example:
```js
const a = { x: 5 };
const b = a;
b.x = 10;
console.log(a.x); // 10
```
Sometimes this is not what you want as it can introduce nasty side-effects. Thankfully, you can copy an object easily with spread operator:
```js
const a = { x: 5 };
const b = { ...a };
b.x = 10;
console.log(a.x); // 5 - Not modified!
console.log(b.x); // 10
```
You can also do it with arrays:
```js
const a = [1, 2];
const b = [...a];
b[0] = 10;
console.log(a[0]); // 1 - Not modified!
console.log(b[0]); // 10
```
> Note: This is just a shallow copy, which means that if you have nested objects, they won't get copied!
## Merging objects
Spread operator also allows you to merge objects:
```js
const a = { x: 5 };
const b = { y: 10 };
console.log({ ...a, ...b }); // { x: 5, y: 10 } - Merged!
```
With arrays:
```js
const a = [1, 2];
const b = [3, 4];
console.log([...a, ...b]); // [1, 2, 3, 4] - Merged!
```
Here's a real-world example. Imagine that you're making a function which accepts an `options` object as argument, but you also want to have default values for this object. Here's a not so good way to do it without spread operator:
```js
const f = (opts) => {
const options = {};
options.foo = opts.foo || "default value";
options.bar = opts.bar || "default value";
options.x = opts.x || 10;
// ...
};
```
This works, but here's a better way:
```js
const f = (opts) => {
const defaults = {
foo: "default value",
bar: "default value",
x: 10,
};
const options = { ...defaults, ...opts };
// ...
};
```
## Expanding an array as function arguments
Spread operator also allows you to expand an array as function arguments. Each element of the array will be an argument of the function. For example:
```js
const numbers = [2, 4, 8, 10, 11, 14];
console.log(Math.max(...numbers)); // 14
```
The array gets expanded like so: `Math.max(2, 4, 8, 10, 11, 14)`
## Variadic functions
A variadic function is a function which accepts an arbitrary amount of arguments (called rest parameters in Javascript). For example:
```js
const f = (...args) => {
console.log(args);
};
f(1, 2, 3, 5); // [1, 2, 3, 5]
```
As you can see, you can call the function with an infinite number of arguments and it will receive them as an array. Keep in mind that rest parameters must always be at the end.
@@ -0,0 +1,203 @@
---
authors:
- "ddivad#0001"
created_at: "2019/10/06"
title: Variables
---
A `variable` in Javascript is a "named container" that can hold data. Variables are declared by giving them a name and a value - the `name` allows you to reference the variable throughout your program, and the `value` is the current value the variable represents.
Variables can be defined in the following ways:
Initialized without a starting value:
```js
let myVar;
```
Initialized with a value:
```js
let myVar = "Hello World";
```
Initialized with a value and redefined:
```js
let myVar = "Hello World";
console.log(myVar); // variable is used by referencing value with 'let'
myVar = "New value";
console.log(myVar);
```
## Dynamic Types
Variables in Javascript do not have explicit types, as some other languages (eg: Java) do. This means that when declaring a variable, you only use `name = value`, and don't need to add a type as well. The type is automatically chosen based on the value.
```js
let myString = "Hello";
let myBoolean = true; // false is also valid
let myNumber = 1;
let myFloat = 1.0;
```
## Let vs Const
Javascript has two "types" of variable declaration: `let` and `const`. These behave in largely the same way, with **one important difference**:
- Let: the value of variables using `let` can be changed throughout the course of your program.
- Const: the value of variables using `const` cannot be changed after they are defined.
```js
let myVar = "Hello World";
myVar = "New Value!"; // This is ok, and myVar's value will be changed.
const myVar2 = "Hello World";
myVar2 = "New Value!"; // This will fail, as myVar2 has already been defined.
```
If the value of the variable will only ever have one value, `const` should be used to define it. This is safer, as if you try to re-assign it somewhere else you will get an error, instead of having unexpected behaviour by accidentally overwriting the value.
If the value of the variable needs to change throughout the course of a program (user input, calculations, etc...), `let` should be used to define it.
### Const Quirks:
The `const` declaration in Javascript allows you to create a variable that cannot be redeclared after it has been declared initially. This is similar to a lot of other languages.
However, in Javascript, there is an edge case to be aware of when using objects and arrays.
```js
const myArray = [1, 2, 3];
console.log(myArray); // [1,2,3]
myArray.push(4);
myArray.push(5);
console.log(myArray); // [1,2,3,4,5]
const myObj = { foo: "bar" };
console.log(myObj); // {foo: bar}
myObj.foo = "re-assigned";
console.log(myObj); // {foo: re-assigned}
```
This, however, will still not work:
```js
const myArray = [1, 2, 3];
myArray = [1, 2, 3, 4, 5]; // Error: myArray has already been defined
```
## What about var?
As well as using `let` or `const` to define variables, there is a 3rd way that exists using `var`. Var is from older versions of Javascript from before let and const were introduced. Nowadays, using `let` or `const` is recommended over `var`.
There are a number of problems that come from using `var` to declare variables, that are fixed by using `let` or `const` instead.
### Block & Function Scoping
Var is not "block scoped" (a block is anything that contains `{}`, so functions, if, for loop, etc...) This means that variables declared using `var` are not _just_ defined in the block they are declared, but declared and can be accessible globally. This can have un-intended side effects.
```js
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log(i); // 5 as i is a 'global' variable
if (true) {
var myVar = "test";
}
console.log(myVar); // 'test'
```
This is fixed using `let`
```js
for (let i = 0; i < 5; i++) {
console.log(i);
}
console.log(i); // Error: i is undefined
if (true) {
let myVar = "test";
}
console.log(myVar); // Error: myVar is undefined
```
However, if the above code was in a function, this would not be the case. In the case of a function, the variable will be scoped to the given function, and not accessible outside it. Eg:
```js
function myFunction() {
var myVar = "test";
}
console.log(myVar); // Error: test is undefined
```
This can cause confusing behaviour, as `var` has different scoping behaviour depending on if its in a function or not. This is solved by using `let` or `const`.
### Hoisting
Variables defined using `var` are also hoisted to the top of the function they are declared in, which can cause some weird behaviour.
```js
console.log(myVar); // undefined.
var myVar = "test";
```
You may have expected the output to be "ReferenceError: myVar is not defined", but instead the output is just "undefined". This is because the variable declaration is hoisted to the top of the function (global scope in this case), and essentially looks like this at runtime:
```js
var myVar;
console.log(myVar); // undefined.
myVar = "test";
```
This also happens when declaring `var` inside a function:
```js
function myFunction() {
myVar = "test";
console.log(myVar);
var myVar;
}
myFunction(); // this will work fine and log 'test'
```
The above works, because with hoisting, it is essentially the same as
```js
function myFunction() {
var myVar;
myVar = "test";
console.log(myVar);
}
myFunction();
```
This creates problems when writing code using `var`, as it means that you can try to use a variable before it was given a value.
There is also weird behaviour when it comes to declarations like
```js
var myVar = "test";
```
Variable declarations get hoisted, but the assignments don't. Going back to the previous example:
```js
console.log(myVar); // undefined.
var myVar = "test";
```
Here, the declaration is hoisted to the top of the function, but the assignment of `test` to the variable happens where is appears in the code. Eg:
```js
var myVar;
console.log(myVar); // undefined.
myVar = "test"; // assignment isn't hoisted
```
In summary, using `let` and `const` is recommended when writing modern Javascript to solve these issues. Sometimes you will see `var` in old tutorials. If you come across this, it is good practice to replace it with `let` or `const` when following the tutorial, and getting into the habit of using up-to-date practices when learning.