---
# System prepended metadata

title: Javascipt Note
tags: [Javascript]

---

# Javascipt Note
###### 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(冒泡)
-![](https://i.imgur.com/ELPopDe.png)

**事件指派 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)
```
![](https://i.imgur.com/vivh0mU.png)

### 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)的程式語言
![](https://i.imgur.com/FdT4mke.png)

```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中執行

![](https://i.imgur.com/iz1dzmh.png)


## 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)：**
在不影響輸出、測試結果的狀況下，對內部結構進行修改與重組，以求更好的維護性或保有未來的開發彈性。
重構的前提是開發團隊要有寫測試。