# TreeHouse JS
## JS with DOM
[difference between htmlcollection and nodelist](https://dev.to/jharteaga/difference-between-htmlcollection-and-nodelist-25bp)

[event lists](https://developer.mozilla.org/en-US/docs/Web/Events#event_listing) | [在哪設eventHandler](https://javascript.info/bubbling-and-capturing) | [事件委托](https://javascript.info/event-delegation) | [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event) object,常用來觀察事件目標:
```
document.addEventListener('click' , (e) => {
console.log(e.target); //用來觀察事件觸發的目標是誰
});
```
### Linters & Validators:
[Linters in Visual Studio Code](https://code.visualstudio.com/docs/languages/javascript#_linters) | [Beautify Tools : JavaScript Validator](https://beautifytools.com/javascript-validator.php)
### JSdocs
[JSdocs](https://jsdoc.app/index.html) | [JSdocs cheat sheet](https://devhints.io/jsdoc) | [VScode plug-in](https://code.visualstudio.com/docs/languages/javascript#_jsdoc-support)
## Asynchronous
### How it works
[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop) | [Visualize Demo](http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D)

```
function func3() {
console.log('Students!');
}
function func2() {
console.log('Treehouse');
}
function func1() {
console.log('Hi');
setTimeout(func2, 1000)
func3();
}
func1();
// Hi
// Students!
// Treehouse
```
#### event loop

### Callback Functions
1. DOM Events
```
btn.addEventListener('click', () => {
// Perform some action on click inside this callback
console.log('I was clicked!');
});
```
2. Iteration methods
```
map()
const capitalizedFruits = fruits.map( fruit => fruit.toUpperCase() );
filter()
const sNames = names.filter( name => {
return name.charAt(0) === 'S';
});
forEach()
fruits.forEach( fruit => console.log(fruit) );
```
3. Continuation-passing style (CPS) (callback hell)
```
function add(x, y, callback) {
callback(x + y)
}
function subtract(x, y, callback) {
callback(x - y);
}
function multiply(x, y, callback) {
callback(x * y);
}
function calculate(x, callback) {
callback(x);
}
calculate(5, (n) => {
add(n, 10, (n) => {
subtract(n, 2, (n) => {
multiply(n, 5, (n) => {
console.log(n); // 65
});
});
});
});
```
#### return callback() vs. only callback()
using `return` to ensure that execution in the current scope does not continue past invoking `callback(data)`. This might prevent multiple callbacks from being accidentally invoked
#### (legacy)用API1的資料改網址抓API2的資料例(wiki)
```
const astrosUrl = 'http://api.open-notify.org/astros.json';
const wikiUrl = 'https://en.wikipedia.org/api/rest_v1/page/summary/';
function getJSON(url, callback) {...};
function generateHTML() {...};
getJSON(astrosUrl, (json) =>{ //用astroUrl先抓取所有太空人資料(array of objects)
json.people.map(person =>{ //物件取出太空人資料,再map array
getJSON(wikiUrl + person.name, generateHTML); //wikiUrl網址直接加上人名來抓取wikiAPI的資料
});
});
```
## [非同步函示](https://www.casper.tw/development/2020/10/16/async-await/) | [瀏覽器處理非同步的原理](https://www.freecodecamp.org/news/javascript-asynchronous-operations-in-the-browser/)
### promises
A promise is a regular **JavaScript object** that changes from a *pending state* to either a *fulfilled* or *rejected* state
* methods
**then()**: status changes to resolved
**catch()**: status changes to rejected *(It's best to specify a rejection reason and call catch() on a promise – if you don’t, the promise will silently fail)*
You can pass *then* and *catch* references to named functions.
* Promise Chaining
Remember: `.then()` itself always **returns a new promise**. By chaining multiple thens together, you can transform values or run additional async operations one after another.
```
function addFive(n) {
return n + 5;
}
function double(n) {
return n * 2;
}
function finalValue(nextValue) {
console.log(`The final value is ${nextValue}`);
}
const mathPromise = new Promise( (resolve, reject) => {
setTimeout( () => {
// resolve promise if 'value' is a number; otherwise, reject it
if (typeof value === 'number') {
resolve(value);
} else {
reject('You must specify a number as the value.')
}
}, 1000);
});
const value = 5;
mathPromise
.then(addFive)
.then(double)
.then(addFive) // called twice
.then(finalValue)
.catch( err => console.log(err) )
// The final value is 25
```
#### Promise.all()
an efficient way to fire off and keep track of multiple asynchronous operations with this **method**. `Promise.all` is useful when your program needs to wait for more than one promise to resolve.(只要有一個request錯就會回傳catch, all or nothing)
#### finally()
It gets called once a promise is fully settled, regardless of whether it's fulfilled or rejected. Finally is useful when you need to do some clean up after the promise sequence finished.
### Fetch
現代抓api方法, `fetch()` returns a `Promise` that resolves to a `Response` object.
```
fetch('api位址') //從網址抓取資料
.then( response => response.json() ) //轉化資料格式為json
.then.....
```
(legacy)直接取代以下抓取資料功能
```
//以下可被fetch()取代
function getJSON(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => {
if(xhr.status === 200) {
let data = JSON.parse(xhr.responseText);
resolve(data);
} else {
reject( Error(xhr.statusText) );
}
};
xhr.onerror = () => reject( Error('A network error occurred') );
xhr.send();
});
}
```
### Async/Await
Async/await is a supplement [syntactic sugar](https://javascript.info/async-await) to `Promise` for creating functions that return and wait for promises.
#### Examples of async/await patterns
```
// Anonymous async function
(async function() {
const response = await fetch('...');
})();
// Async function assignment
const getData = async function() {
const response = await fetch('...');
};
// Arrow functions
const getData = async () => {
const response = await fetch('...');
};
// Async Function as an argument
btn.addEventListener('click', async () => {
const response = await fetch('...');
});
```
#### try...catch
try...catch is the most common way to handle exceptions when using async/await.
```
async function getJSON(url) {
try {
const response = await fetch(url); //抓資料
return await response.json(); //將資料轉為json格式
} catch (error) { //錯誤處理
throw error;
}
}
```
[throw](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw)
#### Using `try...catch` and `async/await` to handle errors in the event listener:
```
btn.addEventListener('click', async (event) => {
event.target.textContent = 'Loading...';
try {
const astros = await getPeopleInSpace(astrosUrl);
generateHTML(astros);
} catch(e) {
peopleList.innerHTML = '<h3>Something went wrong!</h3>';
console.error(e);
} finally {
event.target.remove();
}
});
```
## Fetch API
前置觀念: promise / http
(通常瀏覽器有內建)[Fetch polyfill](https://github.com/github/fetch) | [Sending cookies with fetch()](https://github.com/github/fetch#sending-cookies) | [Response methods](https://developer.mozilla.org/en-US/docs/Web/API/Response#Methods) | [Sending a request with credentials included](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#sending_a_request_with_credentials_included)
```
fetch('https://api.com/api')
```
`fetch()` returns a `Promise` that resolves to a `Response` object.
#### reuseable data fetching function
```
function fetchData(url){
return fetch(url)
.then(checkStatus) //用HELPER FUNCTIONS檢查response status
.then(res => res.json()) //轉json格式
.catch(error => console.log('Something went wrong!',error)) //出錯處理
}
Promise.all([ //等到複數筆資料成功抓回才一起回傳
fetchData('API1'),
fetchData('API2')
])
.then(data => { //回傳的是一個array, with兩個response objects
const dataForFunc1 = data[0].message;
const dataForFunc2 = data[1].message;
func1(dataForFunc1); //使用回傳資料
func2(dataForFunc2);
})
// HELPER FUNCTIONS
function func1(data){...};
function func2(data){...};
function checkStatus(response) { //檢查response status的function
if (response.ok) {
return Promise.resolve(response);
} else {
return Promise.reject(new Error(response.statusText)); //抓response status
}
}
```
#### another way to compose promises with `fetch()` and `Promise.all`
```
// store urls to fetch in an array
const urls = [
'https://dog.ceo/api/breeds/list',
'https://dog.ceo/api/breeds/image/random'
];
// use map() to perform a fetch and handle the response for each url
Promise.all(urls.map(url =>
fetch(url)
.then(checkStatus)
.then(parseJSON)
.catch(logError)
))
.then(data => {
// do something with the data
})
```
#### Posting Data with `fetch()`
[JSONPlaceholder API](https://jsonplaceholder.typicode.com/)
```
form.addEventListener('submit', postData);
function postData(e) {
e.preventDefault();
const name = document.getElementById('name').value;
const comment = document.getElementById('comment').value;
const config = { //*request config設定,放入fetch第二個參數
method: 'POST', //request method都要大寫
headers: {
'Content-Type': 'application/json' //json格式
},
body: JSON.stringify({ name, comment }) //轉成json string, ES2015{name: name, comment:comment}key-value若相同則可以縮寫
}
fetch('https://jsonplaceholder.typicode.com/comments', config) //*重點 fetch('api', {request config}) 第二個參數可放request config設定
.then(checkStatus)
.then(res => res.json())
.then(data => console.log(data))
}
```
## Callback
a function passed into another function as a parameter
主要用途:
* One off and repeating timers
* Triggered by a user interaction
* Loading data from a server using AJAX
* Opening files in Node.js
```
const callbackFunction = function(){
//Do something
}
function executeCallback(callback){
//Do something
}
executeCallback(callbackFunction); //注意callbackFunction不加(),把callbaclFunction當成var來使用
======
function executeCallback(() => {}); //anonumous callback functions,注意在{}後不能放';'
```
* `setTimeout(callback, delay)`: delay執行一次
* `setInterval(callback, delay)`: delay執行好多次,第一次也會delay執行,所以如果需要第一次立即執行則不適合用anonumous callback
#### event handler
把callback用在Async event-base programming
event handler有一個argument: event object
```
function(event){
//Do something
}
=== or ===
event => {
//Do something
}
```

* **event object**: use the same callback for multiple types of events or elements.
`event.type`: what type of event triggered the callback
---`'focus'`: 被click或是tab選到
---`'blur'`: 沒被選到
`event.target`: the element that event was triggered on
## Array iteration Methods
Array: List-like Object
#### `forEach()`
* bugs easier to avoid:
--- Infinite loops impossible
--- Avoids incrementing mistakes
--- Wrong contition
* cons:
--- Can't breakout early (use **For** or **While** instead)
```
array.forEach(function callback(currentValue [, index][, array]){
//your iterator
}[,thisArg]);
```
#### [`filter()`]
Doesn't infect the origin array, returns a filted new array.
```
//過濾掉開頭不是大寫'S'的名字
const newArray = array.filter(name => name.charAt(0) === 'S');
```
* Removing Duplicates from an Array
This works because the indexOf method will return the index of the first occurrence that is found in the array.
```
const numbers = [1, 1, 2, 3, 4, 3, 5, 5, 6, 7, 3, 8, 9, 10];
const unique = numbers.filter(function(number, index, array) {
return index === array.indexOf(number);
});
console.log(unique); // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```
#### [`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
Doesn't infect the origin array, returns a mapped new array with same amount of item.
#### [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
Use values of an array and return a single value.
```
const result = arr.reduce((acc, cur) => { /* … */ }, initValue) //如果沒給initialValue,會直接拿arr[0]當成initialValue
```
--- accumulator: 購物車
--- currentValue: arr裡的item
--- initialValue: accumulator起始值
* Array Flattening
```
//use spread operator
const flatArr = arr
.reduce((flatArr, innerArr) => [...flatArr, ...innerArr], []);
//use arr.concat method
const flatArr = arr
.reduce((flatArr, innerArr) => flatArr.concat(innerArr), []);
```
### Chaining Array Methods
[How numbers work in JavaScript](https://www.youtube.com/watch?v=MqHDDtVYJRI): 如果result有很多小數點,可以用resault.toFixed(2)來抓取小數點後兩位
* Combine `filter()` and `map()`: to clean an array of unwanted values before transforming the leftover items.
* Combine `filter()` with `reduce()`: remove values from an array, using the results to compute some value.
* Combine `map()` with `reduce` for advanced array problems:
```
const users = [
{
name: 'Samir',
age: 27,
favoriteBooks:[
{title: 'The Iliad'},
{title: 'The Brothers Karamazov'}
]
},
{
name: 'Angela',
age: 33,
favoriteBooks:[
{title: 'Tenth of December'},
{title: 'Cloud Atlas'},
{title: 'One Hundred Years of Solitude'}
]
},
{
name: 'Beatrice',
age: 42,
favoriteBooks:[
{title: 'Candide'}
]
}
];
// Result: ['The Iliad', 'The Brothers Karamazov', 'Tenth of December', 'Cloud Atlas', 'One Hundred Years of Solitude', 'Candide'];
```
```
const books = users
.map(user => user.favoriteBooks.map(book => book.title))
.reduce((arr, titles) => [ ...arr, ...titles ], []);
```