# TreeHouse JS ## JS with DOM [difference between htmlcollection and nodelist](https://dev.to/jharteaga/difference-between-htmlcollection-and-nodelist-25bp) ![](https://i.imgur.com/uM1D6Ja.png) [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) ![](https://i.imgur.com/lQCwrg4.gif) ``` 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 ![](https://i.imgur.com/4NDBGyH.gif) ### 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 } ``` ![](https://i.imgur.com/0Viw975.png) * **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 ], []); ```