# Javascipt筆記
###### tags: `JavaScript`
## 變數、常數、資料
```javascript=
//單行註解
/* 多行註解
* 多行註解*/
//Variant, Constant
var camelCase//命名慣例
//undefined vs not define
var hi
console.log(hi)//undefined
console.log(hello)//not defined
//JS Vaiable Hoisting變數提昇 (var & let都有,var範圍大一些)
var v1
let v2 //推薦
//const為常數,一開始就必須指定value
const age = 13
const array = [1,2,3]
array[0] = 5 //這是合法的,矩陣起始指標沒有re-assign
// Data Type
//原始型別(Primitive Type) & 物件型別(Object Type)
//原始型別:Number, String, Boolean, Null, Undefined, Symbol
//物件型別:Array, Function, Object
console.log( typeof "123" )
let a = 1
a++
++a
let name1 = "name"
let name2 = `123`
name1 = String(123) //轉換成String
a = Number("123") //轉換成Number
a = Number("word") //NaN 類型也是number
NaN === NaN //結果是false
// 強制轉型Type Coercion
123+"hello!" //String 123hello
console.log([]+[])
console.log([]+{})
console.log({}+[])
console.log({}+{})
```
**型別判斷**
```javascript
typeof true; // 'boolean'
typeof 'Kuro'; // 'string'
typeof 123; // 'number'
typeof NaN; // 'number'
typeof { }; // 'object'
typeof [ ]; // 'object'
typeof undefined; // 'undefined'
typeof window.alert; // 'function'
typeof null; // 'object'
Number.isNaN(variable)//判斷是不是NaN true or false
Array.isArray()//判斷是不是array true or false
```
**資料傳遞方式Pass by sharing**
- 基本型別類似pass by value
- 物件型別類似pass by reference
- 例外狀況:當 function 的參數,如 function changeValue(obj){ ... } 中的 obj 被重新賦值的時候,外部變數的內容是不會被影響的。
## Scope chain
- var: funciton scope
- let / const: block scope { }
```javascript
const a = 1
function f1(){
var a = 100 // shasdowing
console.log(a) //100
}
function f2(){
console.log(a) //not defined
var a = 100
}
function f2(){
console.log(a) //can't access!
let a = 100
}
```
```javascript
if (true) {
var a = 100; //block 關不住var
let b = 100;
const c = 100;
}
console.log(a); //100
console.log(b); //can't access!
console.log(c); //can't access!
```
**Lexical scope**
```javascript
let a = 1;
function hey() {
let a = 2;
hello();
}
function hello() {
console.log(a); //找尋定義上的a
}
hey(); // 1
```
## 流程控制
```javascript=
//if else
var age =20
if(age >= 18){
console.log("adult")
}
else if(age >= 12 && age < 18){
console.log("teenager")
}
else{
console.log("child")
}
//switch
let gender = "male"
switch(gender){
case "male":
console.log("male")
break
case "female":
console.log("female")
break
default:
break
}
//判定成false的狀況:0, undefined, null, 空字串, NaN(不包含[]
)
// and: && or: || not: !
// let yourInput = prompt("Please enter a number")
//Loop
for(var i = 0; i < 5; i++){}
let n = 1
while(n<10){n++}
for(let i =5; i > 0; i-=2){
console.log(`Number${i}`)
}
for(let i =1; i<=5; i++ ){
console.log(" ".repeat(5-i) + "*".repeat(i*2-1))
}
```
## Function
```javascript=
//Anonymous Function
const myFunciton = function(){
console.log("myFunciton")
}
//Function這種寫法放哪都可以
function myFunciton2(){
console.log("myFunciton2")
}
//Arrow Function
const myFunciton3 = ()=>{
console.log("myFunction3")
}
//Parameter vs Argument
//Default Argument
function sayHello(u1 = "hi", u2, u3){
console.log(u1, u2, u3)
}
sayHello(1,2,3) //1 2 3
sayHello(1,2) //1 2 undefined
sayHello(1,2,3,4)//1 2 3
//arguments object
//function有arguments物件,但arrow function沒有
//Return Value
//所有函數都有回傳值,預設是undefined
function bmiCalculator(height, weight){
let bmi = weight / (height**2*0.0001)
// return Math.round(bmi * 100) / 100
return bmi.toFixed(2)
}
//function內可以再定義function
//裡面的function是不可以被外面取用
//好處:當一個function很龐大,可以使用內部的funciton做簡化與整理,但是那些功能外面的物件又用不到,或者不希望外面的物件去取用
funciton myFunction(){
funciton innerFunction(){
}
...
}
```
## 陣列、物件
```javascript=
//Array裡面什麼都可以放
let array = [1 ,"2" ,undefined, function(){}]
array.length
console.log( array[3] )
array[array.length-1]//拿取最後一個元素
array.push("E")//最後面放入
const last = array.pop()//最後面抽出
array.unshift("newFirst")//最前面放入
const first = array.shift()//最前面抽出
//修改元素
array[0] = 0
array.splice(0,2)//第0個元素開始,刪掉2個元素
array.splice(1,2,"x")//刪掉後放入x
array.splice(1,0,"X")//插入X
//組合Array
let H1 = ["A", "B", "C"]
let H2 = ["D", "E", "F"]//[ 'A', 'B', 'C', 'D', 'E', 'F' ]
let H1H2 = H1.concat(H2)
//陣列有沒有某個元素
let array3 = [1, 2, 3]
array3.indexOf(2)//1
array3.includes(3)//true
//forEach遍歷每個元素(使用callback function)
array3.forEach(function(i){
console.log(i)
})
myArray.forEach((element, index, array)=>{})
//find尋找符合條件的第一個元素
const foundNumber = array3.find((i)=>{
return i==3
})
//map對每個元素做某件事情,回傳新的陣列
const newArray = array3.map((i)=>{
return i*100
})
const myArray = myArray.map((element, index, array) => {})
//filter找到某個符合條件的元素
let result = array3.filter((i)=>{
return i>=3
})
//reduce妹回合傳進「累加值」和「目前這個元素」
//回傳值變成下一回的「累加值」
let scores = [87,65,92,75,98,100]
const totalScore = scores.reduce((acc,current)=>{
return acc + current
}, 0)//0是給予的初始值
reduce((accumulator, currentValue, currentIndex, array) => { /* … */ }, initialValue)
//Reference參考
a = [1,2,3]
b = a
//Object物件 = 屬性+行為 ({key: value})
let blackCat = {
name: "biubiu",
age: 3,
attack: function(){
console.log("Mio~~~")
}
}
console.log(blackCat.name)
console.log(blackCat["name"])
blackCat.attack()
blackCat.color = "Black"//增加屬性
delete blackCat.color//移除屬性
```
## DOM
```javascript=
//抓取HTML元素
const d1 = document.getElementById("doll-1")
const d2 = document.querySelector("#doll-1")
const d3 = document.getElementsByClassName("doll")//d3是集合,HTML Collection
const d4 = document.querySelectorAll(".doll")//NodeList
//抓取後進行操作
d1.textContent = "修改其中的文字內容"
d2.innerHTML = "<h1>新增一個標籤</h1>"
d3.style.color = "red"
```
## 事件 Event
```javascript=
addEventListener(type, listener)
removeEventListener(type, listener)
// 當整個HTML頁面載入完成後
// 寫法1:在html中defer
<script src="app.js" defer></script>
// 寫法2:在css中的一開始等待
document.addEventListener("DOMContentLoaded",()=>{
// 監聽click事件,當發生的時候就...
btn.addEventListener("click",funciton(){...})
})
//callback function也可以寫在外面,但是放在這裡時不能加()
btn.addEventListener("click",clickHandler)
//比較不推薦的寫法
//1. 寫在HTML中
<btn onclick="...">...</btn>
//2. onclick寫法,此寫法無法疊加事件
btn.onclick = function(){...}
// 有些HTML物件的“預設行為”可以被擋下來
link.addEventListener("click",(e)=>{
e.preventDefault()
})
//e.target
vs e.currentTarget
e.target //觸發事件的物件
e.currentTarget //監聽事件註冊的物件
```
**Event flow**
- 事件冒泡 (Event Bubbling)
- 事件捕獲 (Event Capturing)
兩種機制皆會執行
綁定事件在哪個階段上:`addEventListener(type, listener, useCapture)`,
`useCapture`預設為false(冒泡)
-
**事件指派 Event Delegation**
事件指派是利用「事件流程」以及「單一事件監聽器」來處理多個事件目標。
一般要使用迴圈等的方法針對新生成的多個事件指派監聽事件,不但麻煩,還有可能會發生重複監聽 or 忘記移除監聽事件等的狀況,此時使用event delegation就能簡單的只在一個物件上指派監聽事件,僅要針對event.taget做進一步的判斷即可。
```javascript
// 取得容器
var myList = document.getElementById('myList');
// 改讓外層 myList 來監聽 click 事件
myList.addEventListener('click', function(e){
// 判斷目標元素若是 li 則執行 console.log
if( e.target.tagName.toLowerCase() === 'li' ){
console.log(e.target.textContent);
}
}, false);
```
## ES6
```javascript=
console.log(`hello! my name is ${name}`)
// 箭頭函式終極簡化版
const addNumber = (a,b) => a + b
// 物件極簡寫,當key跟變數名稱相同時
let name = "hsuan"
let age = 25
let cat = {
name,
age,
}
// 物件解構
const hero ={
name: "Ironman",
age: 40,
location: "Earth",
}
const { name, age } = hero
// name可以換個名字username
const { age, name: username } = hero;
// 函式內的物件解構
function printUser({name, age}){}
// 點點點 ...
// 1. 展開 spread syntax
array1 = ["a", "b", "c"]
array2 = ["A", "B", "C"]
const newArray = [...array1, ...array2]
// 2. 剩下的我全收了 rest parameter
const [a, ...bc] = newArray
function sayHello(user, ...others){}
sayHello("Hsuan", "Betty", "Leo")//Betty, Leo收集成一個array了
// Optional chaining operator
const adventurer = {
name: 'Alice',
cat: {name: 'Dinah'}
};
const dogName = adventurer.dog?.name;
console.log(dogName);// Expected output: undefined
```
## 更多DOM操作
```javascript=
// 新增DOM元素
const h = document.createElement("h1")
h.textContent = "I am h1"
d.appendChild(h)
//移除DOM元素
const child = document.querySelector("ul:last-child")
if(child){
ul.removeChild(child)
//or
child.remove()
}
//取得上層Element
console.log(d.parentElement)//Element
console.log(d.parentNode)//Node
//取得子層Element
console.log(d.children)//Element
console.log(d.childNodes)//Node
//取得兄弟層Element
//Element
console.log(li.previousElementSibling)
console.log(li.nextElementSibling)
//Node
console.log(li.previousSibling)
console.log(li.nextSibling)
//在指定位置安插DOM
const ul = document.querySelector("ul")
const li = document.createElement("li")
li.textContent = "x"
ul.insertAdjacentElement("beforebegin",li)
ul.insertAdjacentElement("afterbegin",li)
ul.insertAdjacentElement("beforeEnd",li)
ul.insertAdjacentElement("afterEnd",li)
//or
const li = "<li>Z</li>"
ul.insertAdjacentHTML("...",li)
```

### Node 和 Element的差別
- Node: 泛指所有DOM裡面的所有Object
- Element:特殊類型的Node,有較為常見的HTML標籤
## 抓取網路資料介紹
### REST = Representational state transfer
- GET
- POST
- PUT
- PATCH
- DELETE
HTML表單僅支援GET, POST
### 資料傳輸格式
- XML
- CSV
- JSON = JavaScript Object Notation
__AJAX__ = __Asynchronous Javascript and XML__(非同步的Javascipt and XML技術)
AJAX為幾種技術的綜合體
同步 vs 非同步
JavaScript是單執行緒的程式語言(single-threaded)的程式語言

```javascript=
//使用XMLHttpRequest方式
const api = "https://jsonplaceholder.typicode.com/posts";
const req = new XMLHttpRequest();
req.addEventListener("load", () => {
const posts = JSON.parse(req.responseText);
posts.forEach((i) => {
console.log(i);
});
});
req.open("GET", api);
req.send();
```
```javascript=
//使用fetch函數
const api = "https://jsonplaceholder.typicode.com/posts";
//fetch 會回傳promise物件
fetch(api)
.then((resp) => {
return resp.json();
})
.then((data) => {
data.forEach((i) => {
console.log(i);
});
});
```
```javascript=
// 使用async & await的方法
const api = "https://jsonplaceholder.typicode.com/posts";
async function getPost() {
const resp = await fetch(api);
const req = await resp.json();
req.forEach((i) => {
console.log(i);
});
}
getPost();
console.log("Hello!");
```
__CORS__ = __Cross-Origin Resource Sharing__
一種安全機制,會阻擋瀏覽器的JS抓取該網站的資料
## Event loop
..........依附瀏覽器存在的事件監聽器
分為幾個區域:
- Call stack:後進先出(LIFO=Last In, First Out)的執行堆疊
- Web API:瀏覽器提供許多API實現非同步執行,如AJAX, Timeout
- Callback Queue:先進先出(FIFO=First In, First Out)的工作佇列,當堆疊沒有執行項目時,將工作內容送至stack中執行

## JQuery
這邊僅列舉簡單的JQuery使用方法
```javascript=
$().ready(() => {
const div = $("#JS101");
div.html("hello!!!!!!");
});
//ajax
const url = "https://jsonplaceholder.typicode.com/posts";
$.ajax({ url }).done((response) => {
console.log(response);
});
```
```javascript=
// Ubike api示範碼
$().ready(() => {
const api =
"https://tcgbusfs.blob.core.windows.net/dotapp/youbike/v2/youbike_immediate.json"
const keyword = $("#searchKeyword")
const form = $("#searchForm")
const siteList = $(".siteList")
form.submit((e) => {
e.preventDefault()
siteList.html("")
const query = keyword.val()
if (!query) return
$.ajax({ url: api }).done((sites) => {
sites = sites.filter((site) => {
return site.ar.includes(query)
})
sites.forEach((element) => {
const li = `<li class="list-group-item fs-5">
<i class="fas fa-bicycle"></i>
${element.sna.replace("YouBike2.0_", "")} (${element.sbi})<br>
<small class="text-muted">${element.ar}</small>
</li>`
siteList.append(li)
})
})
})
})
```
## 物件導向程式設計(Object-oriented programming, OOP)
JS本身沒有class的設計,而是使用Prototype chain 原型鏈來實現物件導向的功能,雖然有提供`class`,但也只是語法糖衣,背後的運作機制仍是Prototype chain。
**`proto` & `prototype`:**
1. 所有物件都有`__proto__`屬性,裡面放一堆有多個共用function
2. 所有的function都有`prototype`屬性
2-1. 新寫出的function,預設的`prototype`是`{}`
3.
```javascript
function makeHero(name, power) {
// 1. this -> { }
// 2. { }.__proto__ -> 生它的 function 的 prototype = { }
this.name = name;
this.power = power;
// 3. return { }
}
makeHero.prototype.action = function () {
console.log("go in function");
};
const h1 = new makeHero("kk", 200);
console.log(h1);
```
```javascript
class car {
#engine = "v8"; //private私有變數
constructor(name, color) {
this.name = name;
this.color = color;
}
speedUp(second) {
console.log(`speedup for ${second}`);
}
getEngineType() {
console.log(this.#engine);
}
}
let myCar = new car("toyota", "blue");
console.log(`my car name: ${myCar.name}, color: ${myCar.color}`);
myCar.speedUp(100);
```
## This
1. 誰呼叫,誰就是this
2. 沒人呼叫,this -> 全域物件(window/global)
3. 有沒有使用`new`
4. 有沒有使用`()=>{}` 它沒有自己的this
5. 有沒有 `call, apply, bind`綁架this?
6. 是否使用嚴格模式(strict mode) `"use strict"`,將影響第二條的狀況,寫在文件最上方或是寫在部分function內
this應用場景example:
```javascript
const hero = {
hp: 100,
mp: 30,
attack: function () {
console.log("ATTACK!!!!");
},
};
const mage = {
hp: 50,
mp: 80,
attack: function () {
console.log("attack~~~~");
},
heal: function () {
this.hp = this.hp + 30;
},
};
mage.heal();//幫自己補血
mage.heal.apply(hero);//幫英雄補血
```
## 模組化
- 瀏覽器中運行模組化,要在html的`script`標籤中加上`type="module"`,也就是
`<script src="app.js" type="module"></script>`,這樣除了有支援import/export 的效果之外,同時它也會有 defer 效果。
- export方式分為兩種:
1. named export具名匯出:可匯獨立的物件、變數、函式等等,匯出前必須給予特定名稱,而匯入時也必須使用相同的名稱。另外,一個檔案中可以有多個 named export。
2. default export預設匯出:一個檔案僅能有唯一的 default export,而此類型不需要給予名稱。
- import後,嚴格模式預設是打開的
Named export:
```javascript
方法1:
const b = 2;
const obj1 = { name: 'obj1' };
export { b, obj1};
方法2:
export function myFun(){...}
export let a =3
```
Default export:
```javascript
方法1:
export default function myFun(){...}
方法2:
function myFun(){...}
export default myFun;
```
Import:
```javascript
//Named
import {obj1, b} from "./lib/xx.js"
import {obj1 as myObj}from "./lib/xx.js" //換名字
console.log(myObj)
import * as my_module "./lib/xx.js" //一次性匯入,並且給定總名稱
my_module.obj1;
my_module.b;
//Default
import hello from "./lib/xx.js" //import default名字可以直接換
```
## 前後端溝通方法
開發網站時,後端語言(ex:Ruby)是不能直接與前端的JS溝通,因為彼此是兩種不同的程式語言,但若要傳遞訊息,便要透過HTML做為資訊傳遞的中介。
常用方法:
自定義屬性,並且該屬性有data做開頭,再使用JS dataset抓取data開頭的屬性。
```htmlembedded
<div id="total-price" data-a="123" data-b="456" data-price="1000">
$NT 1,000
</div>
```
```javascript
const price = document.querySelector("#total-price");
console.log(price.dataset);
console.log(price.dataset.price)
```
## closure 閉包
當funciton中取用的變數在未來會被released時,javascript程式會自動地將該變數保留,以此達成取用其定義範圍外的變數的效果。
```javascript
function a() {
var i = 1;
return function () {
// Closure 閉包
console.log(i);
};
}
const outer_function = a();
let i = 2;
outer_function(); // 1
```
```javascript
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 输出 1
counter(); // 输出 2
counter(); // 输出 3
```
## IIFE
IIFE = Immediately Invoked Funtion Express
宣告的函式會直接使用,外部的程式無法使用該函式,也不會被其內部的變數所影響,因此使用IIFE時可以在裡面放心大膽地宣告變數,而不需擔心污染外部範圍,此技術大型專案常用到。
```javascript
for (var i = 0; i < 3; i++) {
(function (n) {
setTimeout(() => {
console.log(n);
}, 0);
})(i);
}
```
Higher-Order Function, HOF
1. 可以把別的funciton當參數/引數來傳
2. 會回傳一個function
## 安裝第三方套件 third-party
`yarn init`or`npm init`產生儲存套件版本資訊的package.json。
```bash
$ npm init (-y)
$ npm install XXXX
$ yarn init (-y)
$ yarn add XXXX
```
**使用套件時要注意License:**
license: "BSD/MIT/ISC"
GPL我公開,你也要公開
MIT/BSD隨便用
**載入node_module:**
node_module存放下載的套件,由於下載套件的版本資訊會儲存在package.json,所以就算把node_module刪掉也沒關係,可以使用`npm install`or`yarn add`把node_module下載回來。
下載時使用`--save`or`--save-dev`參數可以將將套件下載至devDependencies裡面,這樣套件便不會上線。ex: tailWind、parcel。
```bash
$ npm install axios
$ npm install sweetalert
$ npm install
$ npm --save
$ npm install XXXX --save-dev #上線用不到,只有打包會用到
```
**打包工具parcel**
```
#package.json中
#自定義指令
""scripts":{
"xxxxx": "npx parcel index.html"
}
# HTML
type = "module"
# 執行
$ npm run xxxx
localhost:1234
```
## 自動化測試
TDD = Test-Driven Development 測試驅動開發
有經驗的工程師寫出測試用程式碼,作為規格書進行開發。
使用如jest的套件做測試。
Framework框架 = 他會執行你寫的程式
Library 函式庫 = 你會執行他寫的程式
```javascript
function adder(n, m) {
return n + m;
}
// 測試,規格
// 寫說明書
test("測試 hello world", () => {
expect(adder(2, 3)).toBe(5);
});
```
**3A 原則:Arrange, Act, Assert**
```javascript
describe("存錢功能", () => {
test("可以存錢", () => {
// 3A 原則
// Arrange 安排,無 -> 有
const account = new BankAccount(10);
// Act 動作,實際操作
account.deposit(5);
// Assert 評估結果
expect(account.balance()).toBe(15);
});
});
```
**重構 Refactor(ing):**
在不影響輸出、測試結果的狀況下,對內部結構進行修改與重組,以求更好的維護性或保有未來的開發彈性。
重構的前提是開發團隊要有寫測試。