# 統整筆記
[toc]
## JWT
- jwt
- 通常用來將 json 的驗證資訊編碼後存在 client 端 (ex. cookie),client reqeust 會將 jwt token 給 server 做驗證
- 組成
- header
- 包含 signature 採用的非對稱式加密的演算法種類(ex. SHA)
- 以 base64 編碼
- payload
- 實際要傳輸的 json object
- 以 base64 編碼
- base64 編碼是可逆的,不能存敏感資訊
- signature
- 將 base64 編碼後的 header, payload, secret 以 header 的演算法做 hash
- 優
- 可以確保 payload 沒有被更改
- 可以設 exp,過期期限
- 不用存 session 在 server
- 省空間, 查詢時間
- 如果有多台 server 不須同步 session
- 缺
- 無法像 session 一樣主動讓 token 無效
- sol :
- exp 設短一點
- 限制使用次數
- docker swarm vs k8s
- docker sawrm
- 可以讓多台 host 組成 cluster 共同管理 container
- 有基本的 ha, load balance 的功能
- service 可以用多個 task 部屬在不同台 host
- 且其中的 host 有問題,manager node 將有問題的 host 的 container 移到別台部屬
- 預設用 round-robin loaf balance service 的流量到不同 task
- 指令簡單
- 基本上和 docker 指令差不多,部屬也是用 `docker-compose.yml`
- 會 docker 上手就很快
- docker swarm 原生少了一些進階的功能
- service 沒有 auto scaling 的功能
- 所以如果要有這個功能就要自己寫腳本去監控(ex. 如果某個服務目前的 cpu, memory 使用量超過一定百分比就擴展多少個…等等的方式)
- k8s rbac 的機制較好管理存取 k8s api 的權限
- k8s 的 ingress 可以統一 host 對外的 port 號,並且再用 hostname or path name 去區分 service(service 不用一定要開在 host ip 的 port)
- 雖然 docker swarm 也可以用 traefik 做到,但我覺得 ingress 設定比較方便
- https://blog.aotoki.me/posts/2022/07/08/rails-deployment-in-practice-deploy-to-docker-swarm/
- k8s 將功能模組化的較完整,降低模組的耦合,較好針對特定功能調整
- ex. 可以用 service 決定如何存取 deployment 下的 pod,service 又有很多不同的 type (clusterip, nodeport, load balancer)
- ex. k8s rbac 由 ServiceAccount, ClusterRole, ClusterRoleBinding 組成
- 因為一個 ClusterRole 可以綁定(ClusterRoleBinding)給多個 Service account,也可以一個 Service account 綁定多個 ClusterRole,或是多對多。
- k8s 背後是 google 且有強大的社群維護
- Google, Azure, and AWS 都支援 k8s instance,不用另外再下載和安裝
## `==` vs `===` vs `Object.is`
- `==` vs `===` vs `Object.is`
- `===` : 先比較 type,再比較 value
- `==` : 先做型別轉換,再比較 value
- `Object.is` : 和 `===` 差在下面兩個而已
- `Object.is(NaN,NaN)` 為 true
- `Object.is(+0,-0)` 為 false
- object type 的比較 : object value 是記憶體位址
```js=
a = new User("a");
b = new User("b");
a == b; // false
a === b; // false
```
- 所以除了用 for loop 一個一個跑 check,還可以用 `JSON.stringfy` check 相等
```js=
a = new User("a");
b = new User("b");
JSON.stringfy(a) === JSON.stringfy(b); // true
```
- ex.
```js=
var yiifaa = 'yiifaa'
str1 = new String('yiifaa')
str2 = String('yiifaa')
yiifaa === str2; // true, Object.is 也是
str1 === str2; // false, Object.is 也是
str1 == st2; // true, 會先 str1.toString()
```
- setTimeout vs setInterval
- setTimeout = 過幾秒執行一次(只會執行一次
- setInterval = 每過幾秒就執行一次(會一直執行
## es6
- es6
- `array.map`
- 將 array 中 element 都跑過一遍,不修改原本的 array,會回傳新的 array(pure function)
- 
- 
- note : array 的 deepcopy
- `Array.from(a)`
- 
- `array.filter`
- 
- `array.foreach`
- 
- arrow function
- es7
- 
- https://ithelp.ithome.com.tw/articles/10249963
- es8
- async & await
- `Object.keys`
- `Object.values`
- 把 Object 變成二維陣列:`Object.entries`
```js=
for (let [key, value] of Object.entries(menu)) {
console.log(`${key} : ${value}`)
}
```
- es9
- `finally`
- 
- Event Bubbling
- 由內到外觸發 event handler
- Event capturing
- 由外到內觸發 event handler
- 127.0.0.1 vs localhost vs 0.0.0.0
- 127.0.0.1 : loopback address,指向本機
- localhost : domain name,通常在 `/etc/hosts` 下都會指向 127.0.0.1
- 0.0.0.0 : 在 server 的部屬上,代表指向 host 中所有的 interface。所以如果把服務開在 0.0.0.0:5000,代表可以用這台 host 上的所有 ip 存取。
- 在 route 中,是代表 default gateway
- selinux
- 基本上如果只有一個 user 有 root 權限關掉沒什麼關係
- Git rebase 可以達成什麼問題?
- es6 const, let, var 之間差別是什麼?
- 用過哪些後端框架?
- 用過哪些前端框架?
- CORS 是什麼?
- Content secure policy 是什麼?
- Content Security Policy (CSP)
- 用來限制網頁對外部的 request 的 origin,只能送白名單的 origin
- 防止被 XSS
- 去執行依些惡意的腳本
- 要注意一些 cdn 不要被擋掉
- 可以在 apache, php, html 設定
- ex. html
```html=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src ‘self’ http://js.devco.re;img-src 'self' data:;">
<title>test</title>
</head>
<body>
<p>test</p>
<img src="example.jpg">
</body>
</html>
```
- User 和 Group 如何雙向查詢?設計資料庫的結構?
- 若 user 是可以加入多個 group 就 3 張 table
- 只能加一個就 2 張
- 設計教授實驗室和負責人的 RESTFUL API 怎麼設計
- Restful API
- url 內容的部分的 root = api
- 
- get
- 將傳送的資料轉為 query string 加再 url 後面
- 如果用 get 來更新資料會有點危險,因可能別人隨便傳給你連結就點,結果是亂帶資料的
- 或是不小心點到錯誤網址
- 好處是可以藉由 url 傳參數,使用者也方便知道參數
- post
- 將資料放入 request 的 body
- 用正確的 method 可以增加 SEO
- get
- 獲得所有實驗室資料
- `/api/lab`
- 獲得單一實驗室資料
- `/api/lab/{lab_id}`
- post
- 建立新實驗室資料
- `/api/lab`
- put
- 更新單一實驗室資料
- `/api/lab/{lab_id}`
- 更新單一實驗室部分資料
- 更新實驗室負責人資料
- `/api/lab/{lab_id}/manager`
- delete
- 刪除單一實驗室資料
- `/api/lab/{lab_id}`
- ref
- https://igouist.github.io/post/2021/05/newbie-2-webapi/
- 圖書館的系統,會把 email serialize 出來,但不知道為什麼會這麼慢?
- 列出可能的原因,並探討可能的解決方法。
- mock spy stub 之間的差異是什麼 ?
- function 和 arrow function 之間的差別?
- SEO (search engine optimization)
- 有其他連結連到此網站
- 把 HTML 弄單純一點
- CSS
- Specificity 優先順序 : `!important` > element inline > id > class > element > `*`
- 相同優先權,看後者(較下面的 code
- 
- 通常不會一開始就用 id 而是 class,因若之後要擴充不容易(id 權限比 class 大)
- 針對 atrribute (只要 element 有這個 attribute)
- 
- 這邊若是 `<input/>` : 沒有設 `value` 的 attribute 就不會是紅色
- 其它自己取的 attribute 也可以
- 針對 attribute 的 value (attribut 值須相同)
- 
- selector
- `A, B` : 有 A or B
- `A B` : A 之後(裡面)的所有 B
- 
- 
- 
- `.A.B` : 同時有 class A and class B
- `<div class = "A B">123</div>`
- 這個 element 同時有兩個 class
- class 可多個,但只能有一個 id
- `A > B` : A 之後的下一層 B,可用來指定哪一層
- 
- 
- 
- 但要注意的是裡面的所有 element color 也會被改到
- `A + B` : A 後面的一個 B (A B 同層)
```html=
<div class = "a">
test
</div>
<div class = "b">
被選
</div>
<div class= "b">
沒被選
</div>
```
- `A ~ B` : A 後面的全部 B (A B 同層)
- 
- 
- 
- SCSS
- 巢狀結構的 CSS
- display:none vs visibility:hidden
- none : 直接讓 element 位置消失
- hidden : 只會讓 element 內容消失
- display
- `block` : 會佔一整區塊 ex. `div`
- `inline` : 只佔自己元素的大小 ex. `span`
- `flex` : 便於排版
- position
1. `static` : 預設的
2. `relative` : 可以設定元素相對位置,用 `top`, `bottom`,`left`, `right` 可以調整 element 位置
3. `absolute` : 自己從文件中抽離而不佔位置,而是會去佔有定位(relative, absolute, fixed)的上層 element 的位置,若上層沒有就一直往上找直到有就停,若都沒有最後就會定位在 window,也就是初始的視窗(不會定位在整份 html,所以滾動視窗不會像 `fixed` 一樣會跟著滾動)
- https://www.youtube.com/watch?v=JOdZdHnuGmM
4. `fixed` : 隨著 scroll 移動位置
- `z-index` : element 顯示的前後順序
- css box model
1. `margin`
- element <b>之間</b> 的距離,隔開相鄰的 element 多少
- 
- 
2. `padding`
- element <b>內部</b> 內容(可能是其他 element)的距離,把 element 往裡面擠多少,同時會把外面的 element 撐大
```html=
<html>
<style>
#test2 {
color : hsl(120, 100%, 25%);
padding: 100px 50px 50px 500px; // 上右下左
}
</style>
<body>
<div id = "test2">
<div id = "test3">test3</div>
</div>
</body>
```
- 
3. `border`
- 邊線本身的設定
- bootstrap
- grid system
- 一行總共可以切 12 格,可以不同大小的裝置設定
- 
- 常用的套件
- sidebar
- pagination
- button
- drop-down 下拉選單
- rowspan
- 
- :before 跟 :after 用途?
- 把 content 放在元素前後面
- JS
- handler 處理 event 發生後的事情. ex. `onclick`
- bun : 一樣是 runtime, 但比 nodejs 快
- `__proto__` vs `prototype`
- `__proto__`
- 念作 dunder prototype
- 每一個 object 都會有一個隱藏屬性 `[[Prototype]]`,可以透過 `__proto__` 存取
- 如果 object 的 attribute 中找不到對應的 attribute,就會去 `__proto__` 找直到找到或到達終點 `null` 為止,期間可能會經過多個 `__proto__`,途中經過的就是這個 object 的 protoype chain
- 
- 
- 指向繼承的 `function.prototype` (原型鏈)
- Object 就是任何東西原型鏈的頂點,再上去就是 `null`
- 所以基本上就只有 object, null, undefined 3 種類型
- ex.
- 
- `prototype`
- 只有 function 有,包含此 function prototype 的 attribute,代表用此 function new 的 instance 的 `__proto__` 會指向 `function.prototype`
- `prototype` 的屬性(variable, function)都是共享的,可以減少資源浪費
```js=
function DOG(name){
this.name = name;
}
DOG.prototype = { species : '犬科' };
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
console.log(dogA.species); // 犬科
console.log(dogB.species); // 犬科
DOG.prototype.species = "貓科";
console.log(dogA.species); // 貓科
console.log(dogB.species); // 貓科
```
- 但若有 object 把設自己的屬性且和共享屬性相同,就會覆蓋掉共享屬性(因為先找到自己的)
- 
- 
- function 建立時會被加一個 `prototype` 且 constructer 為自己
- 
- 若用 `new` 去 create instance,該 instance 的 `__proto__` 就會繼承 fcuntion 的 `prototype`
- 
- 
- new operator 跟 Object.create 都做了繼承物件
- 先建一個空 object
- `const obj = {};`
- 再把 function 的 `this` 的 property(屬性) 給這個 object
```js=
function User(name) {
this.name = name;
pig = "456";
this.hello1 = function() {
console.log("hello1", pig);
}
}
const a = new User("test");
console.log(a.name); // test
console.log(a.pig); // undefined
a.hello1(); // hello1 456
```
- `this` 的值是動態改變的, 會隨著呼叫 function 的 object(function 需綁訂於 object) 的 property 而變
```js=
function User(name) {
this.name = name;
pig = "456";
this.hello1 = function() {
console.log("hello1", pig);
}
}
User.prototype.hello = function () {
return this.name;
};
function speak() {
console.log('Hello world! My name is ' + this.name);
}
const user = new User('Peter');
user.speak = speak;
user.speak(); // Hello world! My name is Peter
```
- https://www.shubo.io/javascript-this/#this-%E7%9A%84%E5%80%BC%E6%98%AF%E5%8B%95%E6%85%8B%E6%B1%BA%E5%AE%9A%E7%9A%84
- 直接呼叫 function 的 this 會指向 global
- 
- https://chupai.github.io/posts/2008/js_this/
- https://www.shubo.io/javascript-new/
- 再把 `obj.__proto__` = `function.prototype`
- 差別在於會不會執行 constructor
- 
- no new
- 不用 new 代表不會建立一個新的 object,而是單純 call function 而已
- 如果 function return `this`
- textContent vs innerContent vs innerHTML vs innerText
- textContent : 選取所有字元,包含空白
- innerContent : 選取所有字元,不包含空白
- innerHTML : 選取所有字元,包含 html 標籤
- XSS 漏洞 : 雖然 innerHTML 不會去執行內容的 `<script>` tag,但可用 button, img 等方式執行 script。但有時候要幫內容加 tag 還是要用 innerHTML
- sol : 用 textContent 過濾再放入使用者內容,再用 innerHTML 加上 html tag
- innerText : 選取所有字元,僅適用於 IE
- 同步 vs 非同步
- 同步 : 等一行執行完才會到下一行
- ex. nodejs : `fs.readFileSync('./README.md')`
- 非同步 : 可先去執行別行,等之前的執行完再執行 callback function
- ex. `fs.readFile('./README.md',readFileFinished);`
- callback
- 一個 function 裡面在 call 別的 function,可確保執行順序,目前這個 function 跑完,才去 callback 下一個 function
- 常見於封裝 XMLHttpRequest 的 ajax
- 
- callback hell
- 如果要連續呼叫多個有上下關係的 api,就會變成巢狀不易維護和閱讀
- 
- promise
- 為了解決 callback hell 的問題而產生的 object
- 
- 3 種狀態
- pending : 等待中
- fullfilled : 成功,執行 `then` 裡面的 code
- rejected : 失敗,執行 `catch` 裡面的 code
- ex.
```js=
myName()
.then((res) => console.log('成功:'+ res))
.catch((error) => console.log('失敗:' + error));
```
- 多種寫法
1. `Promise.all` : 一次等待所有 promise 完成才做 `then`,且多隻 api 是同時執行(不是一個執行完換下一個),適合打多隻 api 的,但是若有一隻 error 就不會顯示其他正確的 api 的訊息
- 
- `resolve` : 回傳成功的訊息
- `reject` : 回傳錯誤的訊息
- 如果上下 api 沒有關係,可以用此來加快速度
- 
- 需一個一個等待,較慢
- 
- 一起做,較快
2. `Promise.allSettled` : 和 `all` 差別在,會回傳所有 api 的訊息(就算有 error),回傳時會多帶一個 `status` 代表成功 or 失敗
- 
3. `Promise.race` : 只做先完成的
- 
- 教學
- https://israynotarray.com/javascript/20211128/2950137358/
- async/await
- 解決 promise 還要寫 then, catch,還是有可能變巢狀結構,更易讀
- async
- function 加上 async,代表會回傳一個 promise 的 object
- await
- await 會等其後的 promise object 結束(resolved or rejected),再繼續執行
- await 需在 async function 內用
- axios vs ajax vs fetch
- ajax
- 不支援 promise,要用 callback
- fetch
- 基於 promise,所以可以用 then、async+await,js 原生支援,不須引入
- request body 的 data 需先 json.stringify
- axios
- 基於 promise,所以可以用 then、async+await
- 較 fetch 易用
- 可直接 `axios.post`,fetch 需寫在裡面
- 
- 不須 stringify,直接送 json
- call stack
- js runtime 是單執行緒的(只有 js engine 的 main thread),所以一次只會執行一行程式
- browser js engine 把要執行(有呼叫(call))的 function 和它的環境(ex. return address, local variable) push 到 stack 再做執行,當 function 結束會從 stack 中 pop 掉
- 不限於 js,其他語言也會
- stack 的容量是有限的,因此無止盡的 call function 會導致 stack overflow
- 
- js engine 會把 js 內容包成一個 `main()`,再去 call 這支 main function
- 
- 
- 
- 但除了 js engine main thread,可以利用 browser 提供的 WebAPIs(ex. DOM(ex. onclick)、setTimeouts、AJAX) 來達成非同步,也就是讓 webapis thread 去做。等它做完後會把 event 對應的 callback function 加到 task queue,而 event loop 會持續檢查<b>當 call stack 為空</b>且 task queue 有 task,就會把 task queue 最前面的 task push 到 callstack
- 
- main 代表檔案本身,檔案最初就會載入,所以 setTimeout 的 callback function 會等到 main 結束後(callstack 為空)才會被 push 到 stack 執行
- 所以這是為啥 `setTimeout(0, callback)` 會等到整個 js 執行完才會做,因為要等 stack 中的 main 執行完 event loop 才會把 task queue 中的 setTimeout push 到 call stack
- browser 每 16.6 ms 會 render 一次頁面,但若是 callstack 有其他的 js 在跑 ex. for loop,就無法 render,此時頁面就會卡住不能動,因只有一 thread
- 
- 但如果是用非同步的作法 ex. 每個 loop 都加上 setTimeout,就會被放進 callback queue,和 render queue 輪流執行
- ref
- 教學 : https://www.youtube.com/watch?v=8aGhZQkoFbQ
- 測試工具 : http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D
- strong vs weak type
- weak type lanaguage
- 會自動轉換 type
- ex. js
```js=
let a = 1;
let b = "1";
console.log(a+b); // 11
```
- ex. c/c++ : int 會被轉換成 boolean
- strong type language
- 不會自動轉換 type
- ex. java, c#
- dynamic vs static type
- static
- 需先宣告 vairable type
- dynamic
- 不須,交給編譯器判斷
- 
- javascript = dynamic + 超級 week type
- js 的 `+` 有 2 個意思:數學、字串
- 當兩方其中有一方是字串,js 就會設定此 `+` 是字串相加
```js=
111 + 222 + "333" + 111 + 222; // 333333111222
```
- 但 php 會分開來 : `+` = 數學, `.` = 字串
```php=
<?php
$v1 = 111;
$v2 = 222;
$x = $v1 + $v2; // 333
$x = $v1 . $v2; // "111222"
?>
```
- js 中,幾乎所有東西都可以相加
- 
- 但 php 雖然是弱型別,但還是有限制
- 
- 解法
- `typescript` : 可讓 code 的型別更嚴謹(雖然不嚴謹還是可以被編譯成 .js),更易讀、維護
- function 加入回傳 type
- function 限制傳入參數 type
- 有 `private`, `public`, `protected`
- scope
- 作用域
1. function scope : variable 只在 function 裡有用
2. global scope
- 在 html 是 window object
- 
`window.aaa; // 5`
- 可以在 function 內宣告全域 : 不宣告(`var`, `let`, `const`)變數就給值
```js=
function test() {
aaa = 5;
}
test();
console.log(aaa);
```
- 在 nodejs 是 global object
3. block scope
- 只作用在大括號之中
- 
- var vs let vs const
- var 缺點
1. 同 scope 內可以重複宣告
- 且不會初始化原本的值
- 
- 可能會不小心改到之前有宣告過的同名的變數
2. 不支援 constant variable(不能被改的)
3. 不支援 block scope
- 
- 因為 var 是 function scope
- 
4. 會產生 global variable
- 
- let 優點
1. 同個 scope 內不可重複宣告
2. 支援 block scope
- 
- 
4. 不會產生 global variable
- 
- 原因
- 
- 這樣的好處是進一步管控了 Global 變數,只能透過 Global Object 去存取,提升程式的嚴謹性和安全性。
- const 優點
- 除了 let 優點再加上 : 定義時必須初始化 (Initialization),且之後不能再更改
- ref
- https://something-about-js-book.onejar99.com/day08
- hosting
- js 會將 variable, function 的 declare 拉到 scope 最上面
- varaiable
```js=
console.log(x);
var x = 5;
// 會變成
var x; // declare
console.log(x);
x = 5; // 這邊是 init
```
- function
```js=
sayHi();
function sayHi(){
console.log('Hi');
}
// 會變成
// 整個 function 都被拉到最上面
function sayHi(){
console.log('Hi');
}
sayHi
```
- function in variable
- 
- 因會變成
- 
- 只有 `var` 會有 undefined 的效果,`let` 和 `const` 雖然也有 hosting,但不會初始化為 undefiend,所以會顯示 `reference error : is not defined`
- strict mode
- 在程式最上面加上 `"use strict"`
- 會讓一些錯誤的語法變成 error
- 未宣告變數
- 
- function 相同參數
- 
- function name 不能和保留名稱重複
- function 常用寫法
1. 一般 : 有 hosting
```js=
a("test");
function a(name) {
console.log(name); // test
}
```
2. anonymous function : 無 hosting
```js=
a("test"); // error : a is not a function
var a = function (name) {
console.log(name);
}
```
3. arrow function
- 語法更為簡潔
- 
- 主要和傳統 function 差在 `this`
- 傳統 function 的 this 是指向呼叫他的 object
- 
- ref
- https://ithelp.ithome.com.tw/articles/10207992
- arrow function 的 this 是指向外圍一層的 function(如果一直往外找沒有的話就會指向 window)
- 適合用在 `setTimeout`, `setInterval`
- object 寫法
- 
- 
- function 寫法
- 
- 
- 不為此 scope 產生新的 `arguments`
- 傳統可用 `arguments`,不用管帶入的參數
- 
- 改成這樣
- 
- self-invoked function
- 自己呼叫自己的 function
- 用 `()` 包起來,再用 `()` 執行
```js=
(function () {
console.log('Hello');
})();
```
- 若此段 code 只須執行一次,可以這樣封裝起來避免影響到 global scope(只會影響 function scope)
- 
- 所以很適合當作初始化資料的用途
- js is pass by value or reference ?
- pass by value
- 當變數是 primitive(原生型別時)
- string
- number
- boolean
- undefined
- pass by reference
- 當變數是 object
- object
- array
```js=
let a = [1];
let b = a;
b[0] = 2;
a; // [2]
```
- 但是若是完全重新賦值,就不是用 reference,而是會重新建一個 object,所以不會互相影響
```js=
let a = [1];
let b = a;
b = [2];
a; // [1]
```
```js=
let aa = [1]
function test(aa) {
aa = [2];
}
test(aa);
console.log(aa); // [1]
```
- closure
- 若直接用 global,可能會在其他地方被改到,所以要限制只有在相同 function 可以被改(private 概念)
- 且因為變數只要有持續被引用,就不會從記憶體砍掉(會存到引用它的 object 的 scope,讓變數繼續存活),所以只要在 function 中引用,就可以繼續用
- 
- 模擬 class 的 private variable 用法
```js=
function testClosure() {
let count = 1;
this.a = function (num) {
count += num;
}
this.getCount = function () {
return count;
}
}
let t = new testClosure();
t.a(8);
console.log(t.getCount()); // 9
console.log(t.count); // undefined
let t1 = new testClosure();
t1.a(100);
console.log(t1.getCount()); // 101
```
- 
- 如果 `this.a`, `this.getCount` 沒有引用 `count`,那 `count` 初始化以後就會被砍掉了(因只作用在 function scope 且也沒有用 `this` 指派給 object)
- cookie vs localstorage vs sessionstorage
- cookie
- 由 server 決定失效時間
- 容量小
- 每次 request 都會一起帶過來 server
- localstorage
- 永久存留在 browser,不因關掉頁面刪除(除非手動刪
- 容量大
- 不會隨 request 帶過來
- sessionstorage
- 關掉頁面會刪除
- 容量大
- 不會隨 request 帶過來
- js 遵守 ECMAScript 的規範
- ex. ES6
- ref
- https://ithelp.ithome.com.tw/articles/10209656
- `instanceof`
- check constuctor 的 `prototype` 是否在 object 的 prototype chain 上
- 
- DOM (document object model)
- 定義:提供網頁元素的樹狀結構的表示方法和存取的 api
- DOM tree : browser 接收到 Web Server response 的 html,會才從頭開始解析遇到的 html element 和其 attribute 成不同種類的 `Node` 的 object 而建立 DOM tree
- ex. `HTMLDivElement`, `HTMLScriptElement`
- CSSOM tree (CSS Object Model) : 等 DOM tree 建完,瀏覽器會從所有來源(包含外部引用、內嵌、行內以及瀏覽器的預設樣式等等)讀取 CSS 並建立 CSSOM tree,其 node 由依據 selector 將指定的樣式套用在指定的 element 上而建立,而像是 color 等樣式若沒有被定義就會繼承父 element 的
- Render tree : 結合部分 DOM, CSSOM tree 的 node(但像是 `<script>`, `display:none` 這種不會顯示在畫面上的就不會結合)。
- 當 render tree 建立完成,browser 依 render tree 中的 node 把 element 做 layout/reflow (排版),將 node 相對位置改為絕對位置。
- browser 有了 layout 的絕對位置後再計算 element 的 z-index 建立不同 layer(有前後順序),最後會再依 layout 和 layer 將排版結果利用 GPU paint(顯示)在畫面上
- visibility: hidden 會出現在 Render Tree,但 display: none 不會,因其不屬於版面中會出現的 object
- 
- 只要當有觸發特定 event. ex, 滾動螢幕、調整視窗大小、call DOM API 都會再做 reflow
- 所以要避免一直觸發否則會很耗效能 : Layout Thrashing
- ex. 不要一直抓 element style
- 
- 
- ref
- https://cythilya.github.io/2018/07/19/styles-and-layout/
- root 為 document : 這份文件的開頭
- 接下來會從最開始的 `<html>` 開始 parse(解析),將剩下的 element parse 成 tree node。element 可以有自己的 attribute
```html=
<html>
<head>
<title>example</title>
</head>
<body>
<h1 class="txt">Hello World</h1>
</body>
</html>
```
- 
- node 關係
- 上下層
- Parent Node ,下層為 Child Node
- 同層
- Previous 以及 Next
- ex.
- `<tr>` 之間為同層
- `<tr>` `<td>` 之間為上下層
- DOM API
- `document.getElementById('idName')`
- 回傳相對應的第一個 element
- `document.getElementsBytagName('tag')`
- DOM 中所有 tag 相符,集合為 HTMLCollection
- `document.getElementsByClassName('className')`
- 找尋 DOM 中符合此 class 名稱的所有元素,集合為 HTMLCollection
- `document.querySelector('selector')`
- 利用 selector 來找尋 DOM 中的元素,並回傳相對應的第一個 element 。
- 不侷限 id, class. 都可以用,還可以用其他 atrribute 選擇,使用較彈性
- 
- `document.querySelectorAll('selector')`
- 利用 selector 來找尋 DOM 中的所有元素,集合為 NodeList 。
- HTMLCollection vs NodeList
- HTMLCollection : 只回傳 element node
- 
- Nodelist : 還會回傳 attribute, text, 註解等 node
- 
- 阻塞
- `<script>`
- 嵌入和引入式都會阻塞 main thread 而是會直接去下載並執行後才繼續 DOM 的 parse
- 但是瀏覽器會分段 render,所以可能會先印出前面的 element
- 所以通常會放在 `<body>` 之後
- `<link>`
- 會阻塞 render
- 否則頁面會從一開始沒有 css 樣式跳成有 css 樣式(ex. 從黑色字跳成黃色),所以需一次載入,不能邊讀邊 render
- 所以通常會放在 `<body>` 之前
- `<img>` : 背景載入,不會阻塞
- `DOMContentLoaded` : DCL
- 代表 parse 完 html 的 event
- `window.onload`
- 已將所有載入事件完成(element 都被 parse 完)後
- 避免 DOM tree 還沒建好就改可能會有點問題
- `src`
- 引入資源
- `href`
- 一般 `<a>` 會用 `href` 的方式做超連結
- 而會用 `<link href = "xxx.css" rel="stylesheet"/>` 將 css 引入
- `rel="stylesheet"` 可以讓 browser 知道這是 css
- 我的理解是 `href` 屬性在 `<a>` 和 `<link>` 是不同東西
- 在 `<a>` 是做超連結
- 在 `<link>` 是做引入資源
- json
- 
- 
- throttle(節流)
- 某個事件觸發後,接下來的 x 秒內不再處理該事件觸發
- ex. 限制 scroll event 不要太頻繁觸發
- debounce(防抖)
- 從最後一次觸發開始,x 秒後才會處理事件
- ex. 搜尋輸入框的推薦關鍵字功能,節約查詢推薦關鍵字動作的觸發次數
- MQTT
- 適合網路狀況差、需低耗電的設備
- 封包小:header 最小只有 2 byte
- keep connection,不需重新建
- 支援多種語言的實作
- 可以有帳號密碼機制
- 
- 但 mqtt 是預設明文傳輸,所以可以搭配 ssl 加密
- ref
- https://officeguide.cc/mqtt-broker-server-mosquitto-installation-tutorial-examples/
- 有 3 種 qos
0. 可能會沒收到
1. 一定會收到, 但可能重複
2. 保證收到且不重複
- why use kafka
1. 因為 mqtt 沒有暫存機制,如果中間斷線資料會收不到
2. 在壓力測試中,consumer 出現漏接封包的狀況
- python
- logging
- 有 5 level,可以設只要看到哪個 level 以上,通常開發用 debug,實際在跑就用 warning, error
- debug
- info
- warning
- error
- critical
- 有一些方便的東西,ex. 印出錯誤行數、檔名
## NodeJS
- sso
- 
- middleware
- 在 request 到 response 中,借助許多 middleware 溝通,用 next 引發下一個 middleware
- ex. express routing
- 
- request
- 預設只能拿到 get 的 query string,所以要拿到 post 的 request.body,就要用 `body-parser` 來 parse request 的 body
- `bodyParser.urlencoded()`
處理 UTF-8 編碼的資料,常見的表單(form)提交
例如:application/x-www-form-urlencoded
- `bodyParser.json()`
處理 JSON 格式的資料
例如:application/json
- `bodyParser.text()`
處理 type 為 text 的資料
例如:text/html, text/css
- `bodyParser.raw()`
處理 type 為 application 的資料
例如:application/pdf, application/zip
- ex.
```js=
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
```
- MVC
- 
- controller 就是 express router
- controller 可以再細分成 routing 和 邏輯處理的 middleware
- 所以 `index.js` 引入 `api` 中 routing 檔案只負責路由回傳由 middleware 處理完的資料
- `index.js`
- 引入 routing 的檔案
- routing
- 
- middleware
- 
- cors (cross origin resource sharing)
- express 預設不能跨域
- 
- 和 csp 是不同的
- cors 是網站 A 不允許網站 B 存取網站 A 的資料
- csp 是不允許客戶載入不合規定的網站的資源
- pm2
- 可以用 multithread 分散流量
- 
- ref
- https://ithelp.ithome.com.tw/articles/10253083
## nginx
- better than apache
- 大量連線時存取效能較好
- 佔較少 ram
- 一般 proxy
- 就是把服務開在 80 port
- reverse proxy
- 藉由不同 domain name 導向不同的服務
- 優點
- load balance
- 統一在 nginx 設定、過濾 request(就不會影響到真正的 server)
- 設定 policy 過濾(白名單、黑名單)
- header, body
- 含有 sql 字句
- ip, 網段
- 黑名單:`deny 123.123.123.0/28;`
- 常用於外部服務
- 白名單:`allow 123.123.123.0/28;`
- 常用於內部服務
- ex. 學校 vm
- 防止 DDOS
- 同個 ip 最大連線數
- `limit_conn conn_limit_per_ip 100;`
- 同個 ip 每秒最多 request number
- `limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=50r/s`
- 最大上傳
- ssl
- cache
- conf 設定
- `proxy_hide_header X-Powered-By;`
- 關掉 `X-Powered-By` : 會顯示 server 用甚麼框架
- `proxy_set_header`
- nginx reverse proxy 就是轉發 request 封包,所以要對轉發的封包的 header 設定
- `proxy_set_header Host $host;`
- 設定 header 的 host 是原本的 request 的 目的地 的 host
- 
- 若沒加,轉送的封包目的地就會變成 localhost,而不是 klab.tw,然後因為 wordpress site 有設定 domain name,它就會要求 broweser redirect 到 klab.tw,成為 redirect loop
- `X-Forwarded-Proto`
- 用來代表此封包是 https or http,因為內部會轉成 http
- `proxy_set_header X-Real-IP $remote_addr;`
- 讓後端 server 可以取得 <b>上一個封包</b>的 source ip
- ex. wordpress 的 wordfence 就可以去判斷 ip 黑名單等
- `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`
- 可以取得<b>最初的</b>封包的 source ip + 上一個的
- 因為可能中間有經過其他的 proxy,所以此可以看最初的用戶端的 ip
- ref
- https://blog.csdn.net/qq_34556414/article/details/106634895
- https://klab.tw/2022/05/nginx-proxy-pass-wordpress/
- upstream
- 
- round-robin : 輪巡
- weight round-robin : 有權重的輪巡
- 
## NoSQL
- 相較 RDBMS
- 特點
- 通常不會正規化,加快存取速度
- 優勢
- 當資料太大時會須擴充 DB,可水平擴充,降低擴充成本
- 不須固定 schema,更改欄位較方便
- 因每筆 data 是獨立的 document 儲存
- 缺點
- 不適合存重要的資料
- 只保證 eventually consistency (最終一致性),cluster 中不同 DB 在相同時間存取的資料可能會不同(因還在同步)
- 成熟度不像 RDBMS 一樣
- 所以重要資料或少量資料較適合用 RDMS 存,因可確保交易的 ACID,也較好看出 entity 間的關係,更易維護
- MongoDB
- 可以自由定義資料結構 (BSON)
- 不用像傳統的 RDBMS (Relational Database Management System) 要先規劃 schema
- BSON : 可以放 binary value 的 JSON
- 結構
- 
- shard
- 水平擴充
- RDBMS 只能垂直擴充、replication(ex. master-slave)
- 但 slave 會完全複製 master 資料,所以在效能上還是會比較慢,雖然可以分 request 到不同 replication
- 可切割不同資料放在不同 mongodb server
- Redis
- 用 ram 儲存
## 前端做 or 後端做
- hash password
- 以 https 傳 + backend hash
- 因若在前端 hash,我只要知道後端的 hash value 就可以直接送此 hash value 給 server 做比對(可改前端的 js
- pagination
- 後端做
- 減少 request 的 data 量
- 需頻繁送 request
- 前端做
- 減少 request 數量
- 之後每次換頁比較快
- 一開始會載入比較久
- 通常不會把全部的頁面都看完,全部載入會有點浪費
- 都做
- 一開始將最常用的頁面(可能是前面幾頁 or 後面幾頁)載入
- 之後若有沒有的頁面去後端重新撈沒有的頁面的前後幾頁
- 驗證
- 都做
- 前端做可以增加 user 體驗、減少 server 負擔
- 後端做防止改前端 js 或是直接送
## 備份
- 能還原才是好備份
- 要有異地備份
- 地方 type
- on-site backup( 本地備份 )
- off-site backup (異地備份 )
- 備份 type
- full backup
- 完整備份,不管是否只修改一部分,每次就是全部重新備份
- incremental backup 增量備份
- 一開始會完全備份,之後每次只備份和 <b>第一次完全備份檔</b> 不同的地方
- 還原速度較快
- Differential backup 差異備份
- 一開始會完全備份,之後每次都只備份和上一次備份檔不同的地方
- 還原速度較慢
- GFS 原則
- 
- 備份 db
- mysqldump
- 手動備份
- mysqldump 會 lock database
1. 可以調成不要 lock(用 readview 的概念)
- `mysqldump --single-transaction --skip-lock-tables --databases my_db1 my_db2 > my_database.sql`
- `--single-transaction` : 只會從開始 dump 的時候的資料開始拿
- 會幫剛開始的資料做 snapshot (readview 的概念)
- https://medium.com/@martin87713/mysql-lock-55ca187e4af2
- `--skip-lock-tables` : 不要 lock table
2. 用 galera 去備份其中一個 db
3. 選在沒人用時備份
- automysqlbackup
- 可設定每天固定備份
- rsync
- 可做 incremental backup,只傳有差異的地方
## transaction
- 當有連續幾筆有相關的 sql,如果只有其中幾個有成功執行就有點尷尬了
- ex. 有繳費紀錄、欠債紀錄這兩個 table。有人來繳費並還清債務,但繳費紀錄 table 沒有成功紀錄資料,但欠債紀錄 table 有成功還債。
- 指令
- `beginTransaction`
- 開始此次交易
- lock table
- `commit`
- 當此次交易所有 sql 都正確執行,提交此次交易執行的 sql 內容到 db
- unlock table
- `rollback`
- 取消此次交易所有 sql 執行的內容
- unlock table
- 特性 : ACID
- Atomicity 原子性
- 不可切割,要馬都完成、都不完成
- Consistency 一致性
- 交易後,db 仍是正常
- Isolation 隔離性
- 交易之間要隔離,不能有 A 交易時 db 的內容是 B 交易到一半的內容
- 等級
1. Read Uncommitted
- 同個交易中,query 結果會因其他交易的改動(其他交易不須 commit)而改變
3. Read Committed
- 同個交易中,query 結果會因其他交易的 commit 而改變
4. Repeatable Read (RR)
- mysql InnoDB 的 default
- 同個交易中,相同的 query 結果會一樣(即使其他 transaction commit),因交易開始時會做 snapshot
5. Serializable
- 交易 sql 開始執行時,會 lock 住 table,其他交易不能讀寫,直到此交易 commit, rollback 才 unlock
- 效率極差,同時只能有一交易,不適合搶票系統等
- lock
- share mode : 一個 transaction 鎖了 share mode 後,其他 transaction 在其 commit 前只能 select 不能改。可以解決非序列化的交易錯誤問題,但較有可能 deadlock。
- `lock in share mode`
- ex.
```sql=
start transaction;
select * from test where id = 1 lock in share mode;
```
- 避免非序列化問題
- 
- 實做 : https://medium.com/dean-lin/%E7%9C%9F%E6%AD%A3%E7%90%86%E8%A7%A3%E8%B3%87%E6%96%99%E5%BA%AB%E7%9A%84%E6%82%B2%E8%A7%80%E9%8E%96-vs-%E6%A8%82%E8%A7%80%E9%8E%96-2cabb858726d
- exclusive mode : 一個 transaction 鎖了 exclusive mode 後,其他 transaction 在其 commit 前啥都不能做(堵塞),可避免同時執行有 query 結果和實際上不同的情形(因預設 RR 會讓相同 transaction 的 query 保持一樣)。
- `for update`
- ex.
```sql=
start transaction;
select * from test where id = 1 for update;
```
- lock 可解決同時查詢的問題
- 
- 若沒有 lock,且票數只有一張,兩個交易就會都認為當下是還有剩的,然後都可以買了
- deadlock 問題
- 若 `for update` 指向空的資料,會是用 gap lock 把查詢條件的範圍 lock 起來(防止寫入,還是可以讀)
- 這樣其實就變成 share lock(可讀不能寫)
- 
- 沒有 `show_id` = 5 的 data
- 第三步交易 3 gap lock `show_id` 4 ~ 6
- 第四步交易 4 gap lock `show_id` 4 ~ 6
- 第五步交易 3 被 交易 4 阻塞
- 第六步交易 4 被 交易 3 阻塞,deadlock 被踢掉而 rollback
- sol
1. Serializable : 但太慢
2. 不要讓 where 找到空的值
3. optimistic lock : 交易更新時判斷更新期間 value 是否有被別的交易更動,有就更新失敗
- 總結
- share lock 比較容易 deadlock
- exclusive lock 會降低吞吐量
- 但他們都可以解決非序列化的問題
- Durability 永久性
- 一旦 commit,永久存在 db
- ref
- https://notes.andywu.tw/2021/select-for-update%E5%86%8Dinsert%E9%80%A0%E6%88%90deadlock%E7%9A%84%E9%99%B7%E9%98%B1/
- https://mgleon08.github.io/blog/2017/11/01/optimistic-locking-and-pessimistic-locking/
- ex. nodejs
```js=
let conn = await pool.getConnection();
// Start Transaction
await conn.beginTransaction();
try {
// update record's paid to 1
const rId = req.body.reason.split(',')[1];
await conn.batch('update records set paid = 1 where `no` = ?', rId);
// update turned = 1
var update_debt = await conn.batch('update debt set `turned` = 1 where `no` = ?;', req.body.no);
// insert into financial
var fId = await conn.batch('insert into financial(`aId`, `reason`, `money`) values(?, ?, ?);', [req.body.aId, req.body.reason, req.body.money]); // 新增到總帳務
// insert into financial_today
await conn.batch('insert into financial_today(`aId`, `reason`, `money`, `fId`) values(?, ?, ?, ?);', [req.body.aId, req.body.reason, req.body.money, fId.insertId]); // 新增到今日帳務
// commit
await conn.commit();
}
catch(e) {
console.log(e);
// 還原
await conn.rollback();
}
conn.end();
```
## 加快網頁載入速度
- 前端
- 盡量不要發太多次 request,因為建連線很花時間,可以一次拿多點資料
- 若要 call 多支無上下關係的 api,可以用 promise.all,而不是 await 一個完再 call 下個
- `<script>` 用 `defer` or `async`
- 若 html parse 到 `<script>`,就會暫停 parse 和 redner 並去載入後再執行裡面的內容(所以傳統會把 `<script>` 寫在最下面,避免 html node 還沒 parse 出來,但在 code 中可能會需要 call DOM API 使用的狀況)
- defer
- 可以在背景載入(不是用 main thread
- 載入完成會等全部 html parse 完後才會執行裡面的 code(可確保 html node 都已經建立)
- async
- 可以在背景載入(不是用 main thread
- 載入完成後暫停 html parse 馬上去執行裡面的 code
- 總結
- 
- 可以把 `<script defer>` 在 html 中初始引入,加快載入速度
- ref
- https://ithelp.ithome.com.tw/articles/10216858
- 利用診斷工具(ex. lighthouse),檢查網頁的載入是被哪邊 block
- lighthouse 還會給建議,也有 .js 的 code usage
- https://pagespeed.web.dev/
- ex.
- 
- 若要使用 js library 用 cdn 版本
- client 通過 dns server 會先去找該 cdn 的 load balancer,它會回傳比較適合的 server,再去找他拿真正資源
- jquery
- `https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js`
- 而不是
- `https://code.jquery.com/jquery-3.6.0.js`
- 資源做 lazy load
- ex. image lazy load
- 只有用到的圖片才去動態載入,而不是一開始就全部載入
- ex. https://imgur.com/
- Code Minimize
- 把變數或函式名稱改成很簡短,且去掉空白
- 減少傳輸及瀏覽器解析時間
- 後端
- 若 table 的 data 每天都會新增很多,可以區分最新的資料的 table 和以前的資料的 table,加快查詢速度
- 部分小量的數值可以用 redis
- ex. 當日總支出
- 可以用 sql 完成的事情就不要拉出來再做
- ex.
- 不用拉出來再算平均
- `SELECT avg(price) FROM Products;`
- procedure
- 可以寫一些判斷邏輯
- 會被編譯,效能較好
- 但缺點是不易閱讀及修改
- sql 盡量不要 `select *`,挑要用的欄位就好
- db table 常用的查詢欄位使用 index
- index 會幫此欄位建一張 index table,可以藉由此 index table 對應到原本 table 的 record
- 優 : 速度快
- 且 InnoDB 的 index 使用 b+ tree 儲存,所以只要用 index column 當查詢條件,時間複雜度是 O(logn),否則就會變 full table scan 的 O(n)。而且 index(non leaf node)是存在 mem 裡面,data 才是存在 disk
- 此狀況若要用 ssn 去 query 需要 full table scan
- 
- 
- 若發現 ssn 很常被用來 query,可以幫它建 index
- 
- 實際就會變這樣查詢
- 
- 缺 : 佔空間
- 需額外存 index table,每次新增資料 index table 也都需要新增
- note
- 如果 `where` 用了 `!=`, `<>`, `or` 或找的資料是不同 type(會被做型別轉換)或用 `sum` 等 function 就會變回 full table scan
- mysql 會直接把 pk 建 index
- 選擇 index 是要看是否常用此 column 查詢
- ref
- https://www.jyt0532.com/2021/01/30/index/
- https://wenwender.wordpress.com/2022/08/26/%E8%B3%87%E6%96%99%E5%BA%AB%E7%9A%84%E7%B4%A2%E5%BC%95index%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/
- 建立方法
- 
- `unique` 是只有當 index 的 column 是唯一辨識才能用
- 
- 
- ex.
- student 的 pk 是學號,但因為很常需用 email 去查詢特定學生,所以可以把 email 的 column 設為 unique index
- 需大量存取的資料可以用 redis
## webpack
- 可以 uglify 與 minify
- 可以打包後端模組或套件(ex. npm)讓前端 js 用
- 
- 雖然在 ES6 後,可以在瀏覽器上用 import 的方式使用後端套件
- 
- 需 type = module
- 缺點是若是要用 npm 套件,路徑就要寫成 `'./node_modules/pad-left/index.js'`,不易維護
- 也可以打包 圖片, css 讓前端 js 用
- 
- note
- 為什麼很多專案(例如說 React)在部署前都要先 build?這個步驟在幹嘛你知道嗎?
- 因為原始碼沒辦法直接放上去瀏覽器(會沒有辦法執行),所以一定要經過 webpack 打包處理,打包完的檔案才能讓瀏覽器執行。
- 你知道 require/module.exports 與 import/export 的差別嗎?
- require/module.exports 是一套叫做 CommonJS 的規範,Node.js 有支援,瀏覽器沒有。
- 可以用 `require` 引入內建 module
- 用 `module.exports` 製作 module
- import/export 是 ES6 的規範,Node.js 部分支援(副檔名需 = `.mjs`),瀏覽器也是部分支援(type=module)。
- ref
- https://blog.huli.tw/2020/01/21/webpack-newbie-tutorial/
## react
- virtual DOM
- 透過比對 virtual DOM,避免直接改動到 DOM,會需要全部 node 重新 render,performance 較差,而是針對部分改動的 node 修改
- 優
- 模組化:react 中很容易就可以把各功能模組化
- 減少 code reuse
- 提升可讀性
- 效能提升
- 利用 virtual dom 優化效能
- 傳統會用 browser api 或透過 jquery 對整個 DOM 重新 render,virtual dom 是 React 自身的 diff 演算法去計算出最小更新,進而去最小化更新真實的 DOM。
- 可以用 state 處理動態 value 改變比較方便,不用自己寫 innerHTML 那些
- 
- 所以 react 被稱為 State Machines:component 依據 state 改變重新 render component
- state and props
- state
- 把資料(通常是 user input, request api data, 會變化的)放自己 component 中
- 
- 可以在 constructor 中用 `this.state` 宣告 state 物件
- 如果是不會變化的通常就不會放在 state,因若 state 改變還須再定義一次
- 當 state 內的資料更新, 會重新 render 該 component 內容(重新執行每個 component 都有的 `render` function),針對 component 做重新渲染
- 要用指定的 `setState` function 去改變 state, react 才會執行 render, 否則不會重新 render
- 
- 
- 
- props
- 繼承父 component 的 atrribure or method
- 父傳給子
- `<Test2_Child father="Test2" name={this.insert_text} />`
- 子接收
```js=
class Test2_Child extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
UpdateData = () => {
//console.log(this.props.name)
this.props.name("child")
}
render() {
console.log(this.props.father)
return (
<div>
<button onClick={this.UpdateData}>test2 child {this.props.father}</button>
</div>
);
}
}
```
- result
- 
- 子 component 是 read-only, 若要更改就需用父的 function 去更改
- component 的 2 種寫法
- 
- `public/index.html`
- 唯一一個 html file,裡面有一個 `<div id = "root"></div>` 的 element
- 其他的 element 都是用 js 去動態生成於 root element 之下
- 
- JSX
- 可以直接嵌入 DOM element, 也可以用大括號代入變數的語法
- 
- 用 react 的困難
- 觀念和以前一個網頁就是三支 html, js, css 的 template 的方式不太一樣
- 需要把不同小功能切開來(才不會每次都需 render 沒有更動到的)然後互相串接,還會需要繼承各個 component 的 value
- life cycle
1. Mount: 元件被渲染到畫面上
- component 從 constuctor 的初始 到 render 執行完
3. Update: 元件的內容因為資料的更動而重新渲染
- component 因為 state 的變動而重新 render
5. Unmount: 元件從畫面上消失,移除這個元件
- component 被移除
- hook
- 也可以用來宣告 state,且語法更嚴謹
- ref
- https://sweeteason.pixnet.net/blog/post/42910964-react-%E5%88%9D%E5%AD%B8%E8%80%85%E7%AD%86%E8%A8%98%E8%88%87%E6%95%99%E5%AD%B8-%28%E4%B8%89%29---state-%E7%9A%84%E4%BB%8B%E7%B4%B9%E8%88%87%E4%BD%BF
- https://medium.com/%E6%8A%80%E8%A1%93%E7%AD%86%E8%A8%98/react-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-0-%E5%89%8D%E8%A8%80%E8%88%87%E6%96%87%E7%AB%A0%E7%B5%B1%E6%95%B4-44603bc6bdc5
## oop vs fp
- object oriented programming
- 將不同 component 抽象化,封裝在不同類別,降低 component 間的耦合,且不需要知道元件的實作細節(所以可用 private)
- 特色
- 抽象化
- 封裝
- 封裝成 public, protected, private
- 盡量設 private,降低耦合
- 繼承
- code reuse
- 問題
- name confilction
- 重複繼承
- 多型
- 依不同子 class 做不同行為
- 優點
- 低耦合
- 若其中一個 component 改動一些實作細節也不須改動到別的 component
- 易測試單個 component 功能
- 高內聚
- 易讀、維護,因元件所需用到的屬性、function 會在相同元件
- SOLID
- S = Single-responsibility principle (SRP) = 單一職責原則
- 定義:一個模組應只對唯一的一個角色負責(就一個類別而言,應該只有一個引起它變化的原因)
- 例子
- 錯誤用法 1:一個模組的改動會改動多個角色的功能
- 
- 錯誤用法 2:把不同功能寫在同個 class,不易更新新功能及維護,且會有大量重複 code
- 
- sol
1. 直接依不同角色拆成不同 class 封裝好
- 
2. 拆成不同 class 封裝好並實作 interface,抽象化程度較高,比較好
- 
- 第二個例子的 sol:提高內聚、降低耦合
- 
- O = Open–closed principle (OCP) = 開放封閉原則
- 定義:面對擴展時是開放的,且擴充新功能時不應修改到原有的 code。
- 原因
- 未區分核心邏輯與附加的商業邏輯
- 
- https://wadehuanglearning.blogspot.com/2019/12/blog-post_11.html
- sol
- 開放擴充點(像是透過繼承)
- 
- 用介面減少針對單一物件的依賴,就可以適應傳進來的不同物件,而不用改動核心邏輯(做好 DIP)
- 
- https://igouist.github.io/post/2020/10/oo-11-open-closed-principle/
- 區分好每個模組的角色和任務,就不會需要互相更改(做好 SRP)
- 像是一開始就先設定好擴充點
- 例子
- chrome 不會因為新增 plugin 而導致修改到原有的程式
- L = Liskov substitution principle (LSP) = 里氏替換原則
- 定義:子類別要能夠替代父類別,且替換後可以正常做父類別的內容(輸出要相同)
- 就是在處理繼承問題
- 原因
- 子類別無法做出和父類別相同的內容
- 
- 不要只因為 code reuse 就繼承,能不要繼承就不要,而是使用介面
- 因為介面可以要求子 class 要完成 interface 中的 method
- ref
- https://igouist.github.io/post/2020/11/oo-12-liskov-substitution-principle/
- 例子
1. 簡易
- 
- https://wadehuanglearning.blogspot.com/2017/08/php-oo-lsp.html
2. 正方形問題
- 
- 
- 
- 驗證長方形面積是否正確的 function
- 
- 驗證結果不合預期,因為正方形沒辦法回傳和長方形一樣的結果
- I = Interface segregation principle (ISP) = 介面隔離原則
- 定義:不應該強迫用戶依賴它們未使用的方法
- 原因
- 如果介面設定太多太廣的方法,造成實作的 class 無法全部都實作(或只能實作空方法)
- sol
- 最小化介面的職責(單一介面不要做太多事),如果有很多不同職責的功能就拆成很多介面
- 一個合理的介面設計是能夠符合單一職責原則的,反過來說,我們可以用單一職責原則來檢視我們的介面設計是否良好。
- 例子
- 交通工具的介面要求能開關車門,結果電動機車無法實作。
- ref
- https://igouist.github.io/post/2020/11/oo-13-interface-segregation-principle/
- D = Dependency inversion principle (DIP) = 依賴反向原則
- 定義:high level module 不應該直接依賴於 low level module,兩者要依賴於 interface
- 原因
- 因為 high level module 會用到很多種 low level module,所以不希望有 low level module 更新連帶 high level module 也要改
- 例子
- 
- sol
- 
- 可以用建構子的方式把 interface 的 object 帶入 hight level module
- ref
- https://igouist.github.io/post/2020/12/oo-14-dependency-inversion-principle/
- ref
- https://igouist.github.io/series/%E8%8F%9C%E9%9B%9E%E8%88%87%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91/
- 屌
- https://medium.com/%E7%A8%8B%E5%BC%8F%E6%84%9B%E5%A5%BD%E8%80%85/%E4%BD%BF%E4%BA%BA%E7%98%8B%E7%8B%82%E7%9A%84-solid-%E5%8E%9F%E5%89%87-%E7%9B%AE%E9%8C%84-b33fdfc983ca
- 高內聚、低耦合
- 良好的內聚應該只關注在一件事情上,並適時地將不屬於自身職責的工作交給別人,達到所謂「該內聚而內聚,該耦合而耦合」。
- function 寫法
- 
- 不好的寫法
- 
- 
- 好的寫法,封裝邏輯
- 
- https://ithelp.ithome.com.tw/articles/10206839
- functional programming
- 盡量將邏輯都包成 pure function,主要邏輯用 declarative 方式寫,且 1 function 盡量做一件事情就好
- imperative : 命令式,一行一行做指令
- ex. 用 `for` 一行一行寫邏輯
- declarative : 宣告式,將步驟封裝好,只需宣告想要的結果
- ex. 用 `map` 宣告
- 優
- 增加重用性
- 增加可讀性
- debug 容易,易找哪邊改到 state
- pure function
- 會回傳 value、不會更改到 function 外部的變數 (no side effect),所以相同參數有相同輸出
- ex.
- `array.slice(start_index, end_index-1)`
- 為 pure funtion : 不會更改 array 值
- 回傳切割的範圍
- 
- `array.splice(start_index, end_index-1)`
- 不是 pure function : 直接更改 array 值
- 回傳切割的範圍,原 array 剩切割完的
- 
- array 可以這樣帶入參數,或用 deepcopy
- 
- 目的
- 並不是要讓全部 function 都變 pure function,而是要降低 function 間的耦合性。才不會專案一大,容易搞不清楚哪一行導致 state 改變
- 解決賦值問題
- 
- 原則
1. Task // function只做一件事,所以會有大量的 function,但可以增加可讀性
2. return Statement // 會回傳結果狀態
3. Pure //純函式
4. No shared state //兩個函式不共用同個狀態
5. Immutable State //不可變的
6. Composable //可被組合
7. Predictable // 可預期的
- 兩者不衝突,可同時使用
- 兩者都是將實作概念抽象化,但 oop 在不同種類的物件間可以比較快速的看出互相的關係(因抽象化的範圍較廣),而 fp 可讓物件中實作的細節被再被更封裝起來。所以如果沒有 oop 而只用 fp 會讓不同種類物件之間的關係很複雜。
## concurreny programming
- 定義:process 可以一次執行多件事情(multi threading),最大化 cpu usage
- Concurrent Programming 為什麼比 Object Oriented Programming 好
- Concurrent object-oriented programming is a programming paradigm which combines object-oriented programming (OOP) together with concurrency. `- wiki`
- 應該可說 concurrent programming 可以在 oop 的基礎上再加上 concurrency,讓 process 一次執行多件事情來最大化 cpu usage
- Multi-processing (多處理程序/多進程):
- parallelism
- 可以利用多顆 cpu 的效能
- multiple threads at the same time run across multiple cores
- 資料在彼此間傳遞變得更加複雜及花時間,因為一個 process 在作業系統的管理下是無法去存取別的 process 的 memory
- 適合需要 CPU 密集,像是迴圈計算
- cpu loading 會持續很高的狀況
- 
- Multi-threading (多執行緒/多線程):
- concurreny
- 可以最大化使用單顆 cpu 週期
- 當 thread 在 wait io 時可以執行別的 thread
- 資料彼此傳遞簡單,因為多執行緒的 memory 之間是共用的(因同個 process),但也因此要避免會有 Race Condition 問題
- multiple threads at the same time are generated by a single process
- 適合需要 I/O 密集,像是處理 user request 再寫入 db 需大量讀寫 memory, disk、爬蟲需要時間等待 request 回覆
- 等待時可以做別的 thread
- ex. 聊天室:收發訊息用不同 thread
- 
- 總結
- multi-processing
- 可以在相同的時間內完成較多的工作
- 比較耗資源, 不同 process 間資源不共享
- 適合 cpu bound
- 如果 cpu bound 用 multi-threading 效能可能會下降,因為 cpu loading 很滿還需要輪不同 thread
- multi-threading
- 可以讓相同工作可能可以在更短時間完成
- 比較不耗資源, 共用 memory space
- 適合 io bound
- 最大化 cpu 週期
- ref
- https://www.maxlist.xyz/2020/04/09/concurrency-programming/
- https://www.geeksforgeeks.org/difference-between-multithreading-vs-multiprocessing-in-python/
- https://www.maxlist.xyz/2020/03/15/python-threading/
## virtualization
- vm
- 硬體層級的虛擬化
- 可將單台 host 的硬體資源切割並最佳化利用
- 不一定想讓整台都在同一個環境作業
- 同時管理多台 vm 較管理多台有自己硬體的 host 容易
- 安全性高:不同 vm 環境以 os 隔離開來
- 缺點:抽象化層級太高,不易切割應用程式(ex. 只是要切割不同 web application 環境卻要切割 os,太浪費資源)
- hypervisor(vm 管理軟體) type
- bare metal
- 直接在 host 上架 hypervisor
- 較快:少了透過 host 的 os 轉送到硬體
- hosted
- 透過 host 的 os 下載 hypervisor application
- 較慢
- container
- os 層級的虛擬化
- 把不同應用程式以容器隔離
- 優點
- 啟動快速:共用 host 的 os kernel
- 可移植性:將 application 會用到的檔案、環境都包在 container,不用管不同 os 的設定
- 安全性高:用 namespace 將容器間的環境隔離開來(ex. network, storage)、用 cgroup 可以限制單個 container 的資源使用量(記憶體、檔案快取、CPU 使用、磁碟 I/O),ex. 所以有一個 container 被駭掉不會完全影響到整個 vm 的流量
- 可隔離開發和測試環境(且部屬快速
- 但相較於 vm 安全性低:因共用 os kernel
- 同時多個 container:容器啟動或關閉,在硬體資源充足的情況下不互相影響
- 擴展性高:可以很輕易的開多個 container 分散流量
- 缺點
- 管理複雜:因將一個 application 切成多個微服務的容器需要管理、互相溝通、如何讓外部存取、存取其他服務的權限、可以使用的資源,尤其是在部屬了多種不同的 application 在多台 VM 上(導致學習成本高)
- ex. 要部屬資管系、學校要用的一堆系統
- 安全性疑慮:共用 os kernel,若 kernel 有問題 所有 container 甚至 host 都會受影響
- image
- 可以在 image 上疊加 image(ex. 在 php image 上加 CI image),使用 overlay2 讓整個 image 看起來只有一個 file system
- overlay2
- 一種 UnionFS (聯合檔案系統)
- Dockerfile 指令
- `from`
- 要從哪一個 image 作為 base image 再修改
- `env`
- 環境變數
- `run` vs `cmd` vs `entrypoint`
- 兩種 type
- Shell form command param1 param2
- 是以 `/bin/sh -c` 的方式執行
- Exec form ["executable","param1","param2"]
- 以執行檔方式執行,所以會無法取得環境變數
- 但可以用 `["/bin/sh", "-c"]` 執行就有了
- 所以也可以用不是此預設的 shell 來執行
- run
- 建立 container 時會執行的指令
- 通常用來 install package
- cmd
- container 建立完成時執行
- 若有多個,只有最後一個 `cmd` 有效
- 若 `docker run` 有帶參數就不會執行
- entrypoint
- container 建立完成時執行
- 若有多個,只有最後一個 `entrypoint` 有效
- 一定會執行
- 總結
- run 是用來安裝一些套件(ex. `apt install nodejs`)
- `cmd`, `entrypoint` 是 container 建完要執行的(ex. `node index.js`)事情
- 如果是可能會被取代的事情,可以用 `cmd`,否則就用 `entrypoint`
- ref
- https://hackmd.io/@tienyulin/docker3
- `expose`
- 開放 port
- ex.
- 
- docker network
- 
- default 用 bridge
- bridge 讓同台 host 上不同 network 的 container 可以用 ip 溝通
- docker swarm
- ingress
- 讓 cluster 中就算是沒有部屬該 task container 的 node 也可以用同個 port 連到
- 
- 可以用 worker2 的 ip:80 連到 service
- ingress routing mash
- 
- 可以發現 node 的流量會轉送到 172.18.0.2 (ingress-sbox, 它是一個 network namespace)
- 
- `sudo iptables -nvL -t nat`
- 它會把流量轉送到有 task container 的 node 上
- overlay
- deploy stack 時如果沒有自己設定要用哪個 network,會幫 stack 自動建一個 overlay network
- 
- 這樣同個 network 下,不同 node 的 container 就可以溝通了
- 總結
- VM 簡化了硬體資源配置與作業系統安裝的工作
- Container 簡化了介於作業系統與應用程式 (App) 之間的環境建置工作
- ref
- https://hackmd.io/@ncnu-opensource/book/https%3A%2F%2Fhackmd.io%2F%40qtNgFtaqR4Or_CLAuotXjw%2FB1EpbbGk3
## ORM (Object Relational Mapping)
- 避免直接寫 raw sql,而是可以用 object 方式操作 sql
- 因為每個人的 sql 寫法不同,用此較好維護
- 切換不同資料庫不須更新
- mysql, mssql
- 避免 sql injection
- django
- 基本可以用 filter, all 去 select
- php ORM vs query builder
- 都是避免直接寫 raw sql,而是可以用 object 方式操作 sql
- 因為每個人的 sql 寫法不同,用此較好維護
- 切換不同資料庫不須更新
- 但某些複雜情況還是得用 raw sql
## unit test 單元測試
- 藉由檢查每一個 function 回傳的物件,可以確定程式是不是有跑在預期的結果裡面
- 可以避免之後改版不小心改動到原本正常的 function
- 主要是測試 function 的邏輯
- 固定 data 是否可以得到相同輸出
- 3A
- Arrange
- 準備測試會用到的 object
- Account
- 用 object 做一些邏輯得出結果
- Assert
- 確認結果是否和預期一樣
- SUT(System Under Test)
- 待測的系統,通常是 class
- 每個 function 個別測試
- 若有 function 中用了另一個 function (ex. call library),就兩個 function 分開測試
- 降低耦合 : 避免測試 A function 時呼叫 B function,但 B function 是有問題的
- mock vs stub vs spy
- stub
- 當要驗證的 function 需要第三方的回傳資料(沒有帶參數)
- ex. call db data
- 就製作假資料取代第三方的回傳資料
- 實際方法
- 可以用物件導向的方式,把會使用到的第三方物件用建構子的方式帶入 class
- 測試時再將帶入的第三方物件改為自己設定的
- 
- 
- mock
- 當要驗證的 function 需要引入第三方 function 並依照驗證 function <b>帶入的參數</b>做事情
- 偽造第三方 function 並檢查帶入第三方物件的參數是否正確(檢查互動的方式是否正確)
- 最後<b>是檢查第三方 function</b> 的資料是否正確
- ex. call function 寫入 log 檔
- 
- 原本的第三方物件
- 
- 偽造假的第三方物件
- 偽造的要可以回傳需驗證 function 的 value
- stub vs mock
- 差別在於驗證原本的物件 or 假的第三方物件
- 
- ref
- https://hackmd.io/@AlienHackMd/HycK5pEpo
- https://ithelp.ithome.com.tw/articles/10263479
- spy
- 驗證原本物件對其他物件的改變是否正確
- 
- ref
- https://medium.com/starbugs/unit-test-%E4%B8%AD%E7%9A%84%E6%9B%BF%E8%BA%AB-%E6%90%9E%E4%B8%8D%E6%B8%85%E6%A5%9A%E7%9A%84dummy-stub-spy-mock-fake-94be192d5c46
- 總結
- stub
- 偽造 SUT 所需的第三方資料
- 驗證整個 SUT 的回傳結果
- mock
- 偽造 SUT 傳入參數的第三方模組
- 驗證偽造的第三方模組收到的資料
- spy
- 驗證被 SUT 改動到的模組被改動後的資料
- NodeJS
- repo : 可用 `mocha` + `supertest` + `chai`
- `index.js`
```js=
process.env.NTBA_FIX_319 = 1;
const express = require('express')
const app = express()
const http = require('http');
const server = http.createServer(app);
// 要測試的 api
app.get('/test', async function (req, res) {
res.json({suc:'test suc'});
res.end;
return;
});
app.get('/test/good', async function (req, res) {
res.json({suc:'test good'});
res.end;
return;
});
server.listen(3001, () => {
console.log('listening on *:3001');
});
module.exports = app // 這邊要 export 給 test 的 script
```
- `test.js`
```js=
// supertest 可以讓專案不用另外跑起來也可以測試 api,它會幫你跑
const request = require('supertest')
const app = require('./index')
const { expect } = require("chai")
// dedscribe 為一個測試區塊,通常裡面會有多個 it,代表實際要測試的更小的測試區塊
describe('GET /test', () => {
it('should respond "test good"', (done) => {
request(app)
.get('/test/good')
.end((err, res) => {
const text = res.text;
// chai 的語法,讓我們比較好描述要測試的東西
expect(text).to.be.equal('{"suc":"test good"}')
done();
})
}),
it('should respond "test suc"', (done) => {
request(app)
.get('/test')
.expect(200, '{"suc":"test suc"}', done)
})
})
```
- chai 用法
- 
- `package.json` 要加上啟動 test
```json=
"scripts": {
"start": "node index.js",
"test": "mocha test --exit "
}
```
- `--exit` : 測試完就結束 script,最好都要加
- 開始測試 : `npm test`
- 
- gitlab ci
- `.gitlab-ci.yaml`
```yml=
image: node:latest
stages:
- build
- test
- deploy
default:
tags:
- linux
before_script:
- now_time=$(date)
- echo "${now_time}"
- echo "started by ${GITLAB_USER_NAME}"
- chmod 0777 ./node_modules/.bin/mocha
build-job:
stage: build
script:
- echo "running scripts in the build job"
- npm install
test-job1:
stage: test
script:
- echo "running scripts in the test job 1"
- npm test
dependencies:
- build-job
test-job2:
stage: test
script:
- echo "running scripts in the test job 2"
deploy-job:
stage: deploy
script:
- echo "running scripts in the deploy job"
```
- pipeline
- 
- test-job1
- 
- ref
- https://medium.com/@stupidcoding/%E5%9C%A8node-js%E5%AF%AB%E6%B8%AC%E8%A9%A6-mocha-chai%E6%96%B7%E8%A8%80%E5%BA%AB-supertest%E6%A8%A1%E6%93%AC%E9%80%A3%E7%B7%9A-sinon%E6%9B%BF%E8%BA%AB-nyc%E7%B5%B1%E8%A8%88%E8%A6%86%E8%93%8B%E7%8E%87-f736c423b893
- https://medium.com/cubemail88/node-js-%E7%94%A8-mocha-%E5%81%9A%E5%96%AE%E5%85%83%E6%B8%AC%E8%A9%A6-16dd9125e632
### 整合測試
- 整合和其他模組間的測試
- ex. db 和 web application
### end-to-end 測試
- 以客戶角度對整個系統測試
- 可以是人工也可以自動
### CI/CD
- Continuous Integration (持續整合) 與 Continuous Deployment (持續部署)
- CI
- 系統 環境安裝 + 單元測試
- CD
- 系統 環境安裝 + 單元測試 + 自動部屬
- 作用
- push code 之後將剩下作業自動化
- 通常包含:建置(安裝環境)、測試、部屬
- 優點
- 自動化單元測試、部屬
- 可以快速更新新版本(有小問題可以馬上更新),減少環境安裝、部屬時間
- 降低操作錯誤性
- 流程通常會分 3 個 `stage` (也可以有多個)
1. `build`
- 環境安裝
- 也可以省略此直接用該環境的 image
- ex. `image: lorisleiva/laravel-docker:latest`
2. `test`
- 單元測試
- 利用 `dependencies` 取得 `build` 安裝的環境
- 所以可以測試不同環境(ex. php5, php6)
3. `deploy`
- 部屬到正式或測試環境
- 只要有其中一個有問題,就不會往下執行
- 建置成功才跑測試,測試成功才做佈署,這樣的先後關係在 GitLab CI 稱之為 `pipeline`
- 
- 
- 如果不分 `stage` 就會由上而下執行且不管上面有沒有成功下面的還是會執行
- `.yml`
- 實際在哪個 stage 要執行那些指令是以 `job` 為單位
- 每個 `job` 都是獨立的 container
- 所以如果要 `job` 間要共用一些環境,就要用 `dependencies`
- `runner` : `job` 的執行器
- 預設每個 job 的 runner 各自獨立
- 可以用 gitlab 上提供的,也可以用自己架的
- 自己架
- 要提供 ssh 金鑰登入
- gitlab 上提供的 shared runners
- settings -> CI/CD
- 
- runners -> expand
- gitlab 提供的 runner, 每個 runner 都有 tag, 去區分不同環境的 container
- 
- 
- `tag` : 指定哪一種類的 runner
- `only` : 指定 branch
- 通常 `deploy` stage 會指定 master branch(只有 master branch 的 code 會被 deploy 到正式環境)
- `before_script` : `job` 執行 `script` 前會執行的指令
- `script` : `job` 真正執行的指令
- ex.
- 
- https://ithelp.ithome.com.tw/articles/10187654
- 
- https://nick-chen.medium.com/%E6%95%99%E5%AD%B8-gitlab-ci-%E5%85%A5%E9%96%80%E5%AF%A6%E4%BD%9C-%E8%87%AA%E5%8B%95%E5%8C%96%E9%83%A8%E7%BD%B2%E7%AF%87-ci-cd-%E7%B3%BB%E5%88%97%E5%88%86%E4%BA%AB%E6%96%87-cbb5100a73d4
```yml=
stages:
- build
- test
- deploy
default:
tags:
- windows
before_script:
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
build-job:
stage: build
script:
- echo "running scripts in the build job"
test-job1:
stage: test
script:
- echo "running scripts in the test job 1"
test-job2:
stage: test
script:
- echo "running scripts in the test job 2"
deploy-job:
stage: deploy
script:
- echo "running scripts in the deploy job"
```
- https://editor.leonh.space/2022/gitlab-ci/
- 查看此次 commit 的 pipeline
1. 點進 repo 的 branch
2. Build -> Pipelines (此 repo 的所有 branch 的 pipeline 都會在這)
- 
- 查看單一 pipeline 中各別 job 執行狀況
1. 點進 repo 的 branch
2. Build -> Jobs
- 
- 點進 job 看 log
- 
- ex. nodejs
```yml=
image: node:latest
stages:
- build
- test
- deploy
default:
tags:
- linux
before_script:
- now_time=$(date)
- echo now_time
- echo "started by ${GITLAB_USER_NAME}"
build-job:
stage: build
script:
- echo "running scripts in the build job"
- npm install
- node index.js
test-job1:
stage: test
script:
- echo "running scripts in the test job 1"
- curl 127.0.0.1:3001
dependencies:
- build_job
test-job2:
stage: test
script:
- echo "running scripts in the test job 2"
deploy-job:
stage: deploy
script:
- echo "running scripts in the deploy job"
```
- ref
- https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml
## python
- oo
```py=
# 物件導向的最基礎,我們定義class,然後用它宣告我們的物件/實體(instance)
# class 可以定義attribute(成員)、method(方法),還有一些既定方法像是初始化__init__
# class裡的method,跟function基本上是一樣的,但第一個參數是self,會指向此實體本身
# 定義一個class叫做Person
# 我們習慣把class的名稱定義成UpperCaseCamelCase,字的開頭大寫
class Person:
# 定義class attribute,所有實體會共用它
kind = 'Person'
# 初始化,當宣告實體的時候這個方法會被呼叫
# 用self.xxx來定義實體的 attribute,每個被宣告的實體會有自己的attribute
def __init__(self, name, age):
self.name = name;
self.age = age;
# 定義實體的方法
def say_hi(self, test):
print(f'Hi, my name is {self.name}, I am {self.age} years old {test}')
# 使用定義好的Class宣導實體(instance)
mike = Person('Mike', 5) # 帶進去的參數Mike與5分辨對應__init__裡的name與age
john = Person('John', 10)
# 呼叫實體的方法
mike.say_hi('god') # output: Hi, my name is Mike, I am 5 years old
john.say_hi('good') # output: Hi, my name is John, I am 10 years old
# 印出實體attribute
print(mike.name) # output: Mike
print(john.name) # output: John
# class attribute是共用的,不管是class本身或是instance都能使用
print(mike.kind) # Person
print(john.kind) # Person
Person.kind = "pig"
print(mike.kind) # pig
print(john.kind) # pig
```
- set
- 過濾重複資料
```py=
a=[1,1]
list(set(a)) # [1]
```
- 帶入 variable
- `print(f"{a}")`
- 相反字串, list
- `a[::-1]`
- `for i in range(5)`
- 一開始會先建 [0~4] 所以每次 loop i 會照順序被改
- 所以在 loop 改 i 的值(ex. `i=500`), 下次 loop 還是會變成 0~4 中的順序的值
- `a[:3]` = `a[0:3]`
- a index = 0~2
- link list 資結
```py=
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
```
- 參數可設 default
- class 是共用 address
```py=
a = test(5)
a.printN() # 5
c=a
c.n = 8
a.printN() # 8
class test :
def __init__(self, n) :
self.n = n
def printN(self) :
print(self.n)
```
- linked list
- 變數是 object 的話,變數內容是指向 object 的 address
```py=
a = [1,2,3]
b = a
a[1] = 8 # a, b 指向相同 object 的 address, 所以 a 改 b 會改
print(b) # [1,2,8]
a = [1,2,3]
b = a
a = [1,2,8] # a 指向其他 address, 而 b 還是指向原本 object 的 address
print(b) # [1,2,3]
```
- 錯誤想法
```py=
# head = head of a linked list
# new head, it's value = head.val
new_head = head
temp_new_head = new_head
head = head.next # 一定要加這行
# 找全部不是 val 的 node
while head != None :
if head.val != val :
new_head.next = head # 不然這邊一開始 new_head = head, 所以這樣的意思等於 head.next = head, 就會無限迴圈
new_head = head
head = head.next
return temp_new_head
```
- heap
```py=
import heapq # 需引入
a = [2, 1, 4, 0, 8]
# 轉為 min heap(若要轉為 max heap 可以先將 data 都 * -1)
heapq.heapify(a)
print(a) # [0, 1, 4, 2, 8]
# 以下 pop, push 都須先轉為 heap
# pop min
heapq.heappop(a) # 0
print(a) # [1,2,4,8]
# push
heapq.heappush(a, 3)
print(a) # [1, 2, 4, 8, 3]
```
- 題目
- https://leetcode.com/problems/kth-largest-element-in-a-stream/
- https://leetcode.com/problems/last-stone-weight/
- https://leetcode.com/problems/top-k-frequent-words/
- dfs bfs
1. 
- https://leetcode.com/problems/path-sum/description/
- dfs
```python=
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
# dfs
# leaf node's sum
leaf_sum = []
# dfs traversal
self.dfs(root, leaf_sum)
# 最後檢查是否 target in leaf node's sum
if targetSum in leaf_sum :
return True
else :
return False
def dfs(self, root, leaf_sum) :
if root != None :
# left and right child
if root.left != None :
# add parent value
root.left.val += root.val
# 繼續往左做到底
self.dfs(root.left, leaf_sum)
if root.right != None :
# add parent value
root.right.val += root.val
# 繼續往右做到底
self.dfs(root.right, leaf_sum)
# 是 leaf node, 加到 leaf node's sum
if root.left == None and root.right == None :
leaf_sum.append(root.val)
```
- bfs
```python=
def bfsSol(self, root: Optional[TreeNode], targetSum: int) -> bool:
# bfs
# leaf node's sum
leaf_sum = []
# init queue, put the root
queue = [root]
# bfs traversal
while len(queue) > 0 :
# dequeue
node = queue[0]
queue.remove(node)
# 因可能 root is None
if node != None :
# left and right child
if node.left != None :
# add parent value and enqueue
left = node.left
left.val += node.val
queue.append(left)
if node.right != None :
# add parent value and enqueue
right = node.right
right.val += node.val
queue.append(right)
# check is leaf
if node.left == None and node.right == None :
leaf_sum.append(node.val)
# 最後檢查是否 target in leaf node's sum
if targetSum in leaf_sum :
return True
else :
return False
```
2. https://leetcode.com/problems/average-of-levels-in-binary-tree/description/
- dictionary
- 撈 key, value
- `for key, value in dict.items() :`
- 撈 key
- `for key in dict.keys()`
- 撈 values
- `for val in dict.values()`
- 確認 key 是否存在
- `key in dict`
- True or False
- list
- 可以相加不能減: `a = [1,2] + [3] # [1,2,3]`
- 放到最前面 : `a.insert(0, 4) # [4,1,2,3]`
- 相乘 : `[1,2] * 2 # [1,2,1,2]`
- permutation 排列(n! 種)
```python=
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
output = [] # pass by reference
self.perm(nums, [], len(nums), output)
return output
# deep copy of list
def copy(self, obj) :
n_obj = []
for i in obj :
n_obj.append(i)
return n_obj
# 排列
def perm(self, nums, cur, total_len, output) :
# 長度到了就結束
if len(cur) == total_len :
# 是結果,所以需 deepcopy
output.append(self.copy(cur))
else :
# 每個點都做所有 value 的排列
for i in range(len(nums)) :
# 這個點這一次要用的 value
temp = nums[i]
# 加到目前 list
cur.append(temp)
# 從原本 nums 中移除讓下個點不能用
nums.remove(temp)
# 下個點以剩下的 nums 繼續做
self.perm(nums, cur, total_len, output)
# 這個點做完此 value,換一個 value 所以要移除
cur.remove(temp)
# 把這次用的 values 插回去原本 nums 的 index
nums.insert(i, temp)
```
- combination 組合(C n 取 k)
```python=
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
output = []
self.dfs(n, k, [], 1, output)
return output
# 遞迴 k 次
def dfs(self, n, k, cur, now, output) :
# 長度 = k, 結束
if len(cur) == k :
output.append(cur.copy())
else :
# 每個點只能用(起點是)前面點用過以後的 value(i+1)
for i in range(now, n+1) :
cur.append(i)
self.dfs(n, k, cur, i+1, output)
cur.remove(i)
```
- note
- `//` : 沒小數
- `/` : 有小數
## dns
- 一個 dns server 可以有多個 dns zone
- 每個 dns zone 會有自己的 SOA
- SOA (Start of Authority) : zone file 的第一筆record,主要記載這個 zone 相關的資訊,例如管理員信箱、用來辨別是否有更新的序號、refresh、TTL(超過時間就要向 dns server 重要一次 dns record)
- serial 序號
- 讓 secondary name server 知道有更新過
- refresh
- secondary name server 多久要來和 primary 確認是否有更新資料
- 可以一個 dns zone 裡面放很多筆 dns record,但可能有些 domain 是想要有特殊設定(可能是網路內部才能存取的 domain)
- DNS load balance
- 可以設定多個 NS 的 dns server,就可以 load balance
- zone transfer
- 為了讓資訊同步,將 primary nameserver 的 zone file 複製一份到 secondary nameserver 的這個溝通過程,就稱為 zone transfer。
- DNS cache poisoning (DNS spoofing)
- dns resovler 要 dns query 時,偽造錯的 dns 紀錄給它
- 只要偽造的 server 可以比正確的 server 還要早傳紀錄給 resolver 及包含一些正確資訊可能就可以
- 知道要回傳給 resolver 的 port (可以大量送)
- 應用於中國的防火牆
- DNSSEC (DNS security)
- 將 dns record 用非對稱式加密
- 可以確保 dns record 完整性,不會被竄改
- dns recon
- 取得 dns server 的所有 dns record
- 一般來說不能亂給別人看到所有紀錄,因為可能有些是內部使用的 domain
- type
- passive recon
- 透過 browser search engine 找 subdomain
- 但只能找到網頁的服務(ftp 就找不到)
- active recon
- 利用 dns server 漏洞直接向 dns server 拿完整資料
- ref
- https://tech-blog.cymetrics.io/posts/crystal/dns-hacking-3/
- rndc (remote name daemon control)
- 可用 rndc command 遠端或本地控制管理Bind,並以加密方式來傳送資料
- 當網管時管理 dns 哪邊沒做好
- 之後可以加上 dnssec
- 不安全的 DNS 服務會遭什麼攻擊,有什麼辦法可以解決?
- 被竄改 dns record
- DNSSEC
- 不存在的 dns record 是否真的不存在
- NSEC
- 將 dns record 依首個字的字母大小排序,若找不到 record 會回傳下一筆(依字母排序的)存在的 dns record
- 這樣就無法偽造下一筆的 dns record 不存在
- ex.
- 
- deploy bind9 on ubuntu
- https://magiclen.org/ubuntu-server-dns/
## 前後端分離
- 不分離
- server side render
- 前端改,後端可能需要重跑
- 分離
- client side render
- 讓 SEO 評分降低,因只能爬到一點資料
- 降低前後端耦合
- 增加開發效率、分配工作
- 更易維護、找問題
- path traversal
- hacker 利用 `../` 存取其他 path 的檔案 or api
- 
- 
- 
- sol
- api 都要驗證 token
## 基因演算法GA (Gene Algorithm)
- 因為要找出最佳解會花太久時間,此可以快速找出近似最佳解
- 如果是要找出一個外送員要送到 n 個客戶的最佳路線問題就是 O(n!)
- 步驟
1. 將 data 編碼成基因
- 我們基因是由 city + car 組成
- `c3 c2 v1 c1 c8 v2 ...`
- `vx` : 第 x 個 car
- `cx` : 第 x 個 city
3. 產生 init population (隨機的初代群集)
4. 幫 population 中的個別染色體做 fitness 評分
- 一般會有多個情況影響到 fitness
- 單一 car 超過最大最小限制公里 fitness 要加倍懲罰
- 單一 car 跑的城市數量超過最大最小也要懲罰
- 跑太少懲罰比要跑太多高
- 所有 car 跑的公里數的標準差太大也要懲罰
- 而且這個懲罰倍數要很高,因為跑的公里數要平均最重要
- 要算哪個條件要懲罰幾倍較麻煩
- 所以 fitness 算出來異常也比較麻煩, 要過濾掉多個條件
5. 對 population 做天擇,保留 fitness 較好的染色體
- selection tournament
- 天擇方法
1. 從 poplation 隨機選幾個染色體
2. 從幾個染色體中挑 fitness 比較好的染色體做為新的 population
6. 將 population 中部分的染色體做兩兩的交配,利用他們的基因產生新的下一代的染色體的基因,這些新染色體會組成下一代的 population
- 採雙點交配法
- 會隨機切一半是 parent1 的基因的順序,剩下另一半會是 parent2 基因的順序
- ref
- https://hackmd.io/@labNote/HyPHPwsRj
- https://hackmd.io/9Os2l76XTem9u9eUx_ZmBA?view#Travelling-salesman-problem%EF%BC%88TSP-%E6%97%85%E8%A1%8C%E6%8E%A8%E9%8A%B7%E5%93%A1%E5%95%8F%E9%A1%8C%EF%BC%89
## terraform
- 和 ansible 一樣都可以用來實現 IaC
- 若想要新開一個測試環境,需要手動
- 去 aws 租一台 server, 並設定環境(ex. 網路)
- install docker, db ... 並設定
- 把 repo 放上去並跑起來
- 但 terraform 可以把這些流程用腳本自動化
- 
- provider : 雲端服務的提供者, ex. aws, azure
- ref
- https://www.huanlintalk.com/2023/07/terraform-overview.html
## galera
- synchronous replication
- 採用兩階段的 commit 可以確保所有 replicas 的 commit 都是同步,只可能是全部都有或是都沒有
- 優
- 不管啥時 failover 所有 replicas 資料都會是同步的
- 可以用 haproxy 做 load balance 增加 performance
- 可以用 haproxy 做 failover
- 速度慢
- 但隨著 cluster 中的 replicas 增多,交易 delay 會大幅增加,因須同步更多台 replicas
- 且因每一台 replicas 都可以被寫入,所以須處理多個 replicas 的 confict
## master-slave
- asynchronous replication
- master 會直接完成交易並將 event 寫入到自己的 log,slave 再定期去和 master 拿新的紀錄
- 所以 master 不會知道 slave 有哪些紀錄,也不能保證任何 slave 和 master 的資料是同步的
- 所以當 master 有問題要 failover/failback(故障自動轉移) 時可能會有已經 commit 的紀錄卻沒有同步到 slave 的問題
- 速度快
- 不用處理 commit 同步問題
- 只有 master 能被寫入,不須處理多個 replicas 的 confict
- ref
- https://galeracluster.com/library/documentation/tech-desc-introduction.html
- master-slave + keepalived
- 
- 先將 A B 做戶為主從的設定
- 再 A B 都安裝 keepalived 並設定一個虛擬 ip
- 客戶就連這個虛擬 ip 就可以
## vue.js
- 概念滿像是把 html 裡面會用到的內容(屬性, value)都分出來元件化
- html 放在 `<template>`, code 放在 `<script>`
- 用 MVVM declarative render 概念帶入變數 : `{{變數}}`
- MVVM
- model
- 處理資料
- view
- 畫面呈現
- viewModel
- 將 model 資料即時轉換給 view 做呈現
- html 用 v-for(for loop) 取出元素
- 
- 
- computed : 可以做依些運算再回傳值
- 
## ref
- https://hackmd.io/@splitline/BkALfYY5r