---
# System prepended metadata

title: 前端其他

---

# 前端其他

## 基本知識
### 瀏覽器
> - Code 通過瀏覽器內核渲染出畫面
#### 瀏覽器內核
> - 瀏覽器內核可分為[渲染(排版)引擎](https://zh.wikipedia.org/wiki/%E6%8E%92%E7%89%88%E5%BC%95%E6%93%8E)與JS
> - 渲染引擎: 
>   負責取得標記式內容（如HTML、XML及圖像檔案等等）、整理資訊（如CSS及XSL等），並將排版後的內容輸出至顯示器或印表機。
>   - Trident: 舊IE
>   - EdgeHTML: Edge(Win10)
>   - Gecko: FireFox
>     - 開源
>   - webkit: Safari
>   - Bink: Chrome, 新Opera
>     - Gecko的分支
>   - Presto: 舊Opera
> - JS: 解析與執行Javascript
### Web 標準
> - 由 W3C 與其他組織制定
>   - `結構` : 
>     - 對網頁元素整理分類
>     - `XML` `HTML`
>   - `表現` : 
>     - 外觀
>     - `CSS`
>   - `行為` : 
>     - 互動
>     - `DOM` `ECMAScript`

------------------------------------
## Sublite
> - 指令版 `Command+Shift+P`
### Package control
> - 安裝: 打開指令版輸入install, 點擊Install Package Control
> - 安裝插件: 
>   - 打開命令版輸入PC, 點擊Package control: Install Package
>   - 輸入插件名
> - 插件
>   - ConvertToUTF8: 自動轉編碼為UTF-8
>   - emmet:
>     - \>: 層級
>     ```Html
>     <!-- div>ul>li*3 + tab -->
>     <div>
>		<ul>
>			<li></li>
>			<li></li>
>			<li></li>
>		</ul>
>	  </div>
>     ```
>     - +: 同級
>     ```html
>     <!-- div+p+ul +tab -->
>     <div></div>
>     <p></p>
>     <ul></ul>
>     ```
>     - (): 分組
>     ```html
>     <!-- div>(p>ul)+(ul>li) +tab -->
>     <div>
>	  <p>
>	      <ul></ul>
>	  </p>
>	  <ul>
>		  <li></li>
>	  </ul>
>     </div>
>     ```
> - 刪除插件:
>   - 命令版> PC> Package control: Remove Package> 插件名
>   
### HTML
> - 開頭輸入!或html:5後按Tab會自動補齊Html框架
> - 右鍵可以直接開啟網頁瀏覽
> 

### Markdown
> - 安裝Markdown各式插件
>   - Extended
>   - Preview
>   - SlideShow
>   - Editing
> - md檔按 command+b 生成 Html 檔

-------------------------------------------
## 環境
- 編輯器
  - Atom
  - VS Code
- 集合服務器
  - Window: wamp, 
  - Mac: xampp
  - Linux: Nginx 
- 虛擬機
  - VMware
  - putty(Mac), WinSCP(win), ssh(Linux)
- 版控
  - git for window, git-scm(mac), yum/apt(linux)
  - tortoisegit(window), snailsvn(Mac), yum/apt(linux)
- Node JS
  - [nodemon](https://nodemon.io/)
    - 用來開應用程式用的, 更新不用重開服務器, 他會熱更新
    - 安裝 `% npm i nodemon -g`
    - 使用 `% nodemon <your node app>`
- API
  - phonegap-cli, dcloud 
  - APICloud Studio(中國專用)
  
- 數據庫
  - Navicat
  - MonogoDB, mySQL,... 
  - sequelize
      - 操作數據庫的一個框架
- WebPack ( 這東西下面三個種都能幹 )
  - 編譯
    - babel -> 專門編譯 js
  - 封裝
    - browserify
  - 同步
    - browser-sync 
- webGL
  - three.js 

- 其他
  - [JWT](https://jwt.io)
      - 實現狀態保持的一種工具
      - 能夠模擬類似於 session 的登入記錄功能

## NPM
> - 更新 NPM
```shell
node -v                    # 查看版本
npm -v
sudo npm cache clean -f    # 清快取
sudo npm install npm@latest -g  # 升級到最新穩定版
```
> - 查看與安裝模塊
```shell
$ # 查詢 npm 服務器有啥模塊
$ npm search <模塊名>        # 查看模塊名(ex. express)相關模塊  
$ npm info <模塊名>          # 查看某個模塊的所有資訊
$ npm view <模塊名> versions  # 查看某個模塊的版本訊息

$ # 安裝模塊
$ npm init [-y]                            # 拿到 package.JSON
$ # -y : 全部問題都用預設
$ npm i <模塊名>[@latest] [模塊2] [模塊3] [-D||-S] [-g]
$ npm i <模塊名>[@<版本>] [-D||-S] [-g]
$ # -D : --save-dev 的簡寫, 會寫入 package.json 的 devDependencies(開發環境) 裡
$ # -S : --save 的簡寫, 會寫入 package.json 的 dependencies(生產環境)裡
$   # -D 跟 -S 對一些打包用的工具會有使用上的區別，例如 webpack
$ # -g : 將模塊安裝到全局</usr/local/lib> (容易有權限問題)
$ # i  : install 縮寫
$ # 一次裝多個模塊: 用空格隔開即可
$ npm install # 依照環境清單安裝模塊



$ # 解除安裝模塊
$ npm uninstall <模塊名> [-D||-S] [-g] 
$ # 看你要不要砍 package 裡面的東西, 還有看你裝哪來決定要不要加 -g


$ # 查看安裝過的模塊
$ npm ls [-g] [--depth 0] 
$ npm list [-g] [--depth 0]     # ls == list
$ # --depth : 限制輸出層級; 
$   # 0: 只輸出第一層; (否則很亂)
```
> #### 安裝全局的權限問題
> - 如果開發者只有自己的話 
>   - 就把 `/usr/local/lib/node_modules` `/usr/local/bin` `/usr/local/share` 
>     權限改成自己就好了
```
$ sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
```
> - [官方文件解法](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally)
>   - 要馬節點版本
>   - 要馬創一個資料夾, 將 npm 改預設, 並改 `.profile` 讓大家都可以用
```shell
$ mkdir ~/.npm-global                    # 創建一個執行資料夾
$ npm config set prefix '~/.npm-global'  # 新增 npm 的頭

$ vi ~/.profile        # 修改 profile

export PATH=~/.npm-global/bin:$PATH     # 這句話丟進去

$ source ~/.profile  重新讀取一下

$ # 之後 npm install -g 就會改裝到 ~/.npm-global 下了
```
> #### 缺少XCODE 套件問題(No Xcode or CLT version detected!)
```shell
sudo rm -rf /Library/Developer/CommandLineTools
xcode-select --install
```


> - 延伸: 查看權限
>   - [Linux命令大全](https://man.linuxde.net/ls)
```shell
$ # Linux 的 ll [指定檔案或資料夾] == ls -alF , 不指定就是當前
$ # -a : 所有檔案及目錄 vs -A : 不含 ./ 跟 ../(預設)
$ # -l : 單列顯示 vs -C : 多列顯示(預設)
$ # -F : 檔名顯示標示符
$   # `/` : 文件;  `*` : 可執行文件; `@` : 連接符; 
$   # `|` : FIFO管道; `=` : sockets套接字;
$ ls -alF /usr/local/bin/
```
> - 延伸: 新增別名
>   - 有些 Linux 有的縮寫指令, 在MAC沒有
>     直接去 `.bash_profile` 新增別名就好了
```shell
$ vi ~/.bash_profile


alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF

$ source ~/.bash_profile
$ # source <FileName> : 在當前環境下讀取並執行某個可執行檔, 與 `.<FileName>`是一樣的
```
> - 延伸: 修改權限
>   - https://www.jianshu.com/p/d42c435729fc
```shell
$ # chmod  : 修改文件的讀寫執行
$ # chown : 修改文件的用戶權限 
```

> - 其他相關常見管理工具
>   - nrm : 可以快速切換源
>     - 安裝： `$ npm install -g nrm`
>     - 版本： `$ nrm --version`
>     - 查看源: `$ nrm ls`
>     - 切換源: `$ nrm use <registry>` ( registry: 源名 )
>     - 添加源：`$ nrm add <registry> <url> [home]` ( home 為源的主頁 )
>       - `nrm add test http://npm.test.com/ http://www.test.com/`
>     - 刪除源：`$ nrm del <registry>`
>     - 測試源速度: `$ nrm test <registry>`
>     - 測試所有源速度: `$ nrm test`
>     - 訪問源主頁: `$ nrm home <registry>`
>       - 添加時如果沒有添加主頁，這就沒有屁用
>   - [yarn](https://classic.yarnpkg.com/zh-Hant/) : FB 出的包管理工具，為了解決 npm 各種問題而生
>     - 基本上用法跟 npm 差不多，關鍵字不太ㄧ樣而已，然後有更多的功能可以用



## 服務器
### wamp
> - 將要給別人用的東西放到 www/ 文件夾裡
> - 路徑轉換: `C:\wamp\www\你的文件` => `http://localhost/你的文件`

### xampp
> - 資料要放在 htdocs/ 文件夾裡
> - 路徑的話, 直接點 Go to Applocation 就好了
>   ![](https://i.imgur.com/z2krukG.png)
>   - 但是這個路徑是直接從 IP 連出去的, 
>   - 如果要走 localhost 需要設定, 還沒研究成功
>   - 另外預設是連到 dashboard 資料夾, 怎麼改預設資料夾我也還沒成功
>   - 不過先不管, 反正用 IP/檔案就能跑了, 將就點, 測 ajax 要緊


## 數據交互
### 表單
> - 最基本, 簡單
> - http 數據請求都是表單
> - 事實上 ajax jsonp 對於後端來說, 也都是表單,
>   ajax 就是模仿 form 完成的, form 怎麼傳, ajax 就怎麼傳

> ### 屬性

> - action
>   - 提交到哪
> - method
>   - GET, POST, PUT, HEADER, DELETE, ..., 自定義
>   - 大小寫不限, http 是有規定要大寫
> - name
>   - 必須
>   - 可重複, 服務器會收到一組陣列

> - submit 按鈕 : 提交
```htmlmixed=
<body>
  <form action="" method="GET">
    <input type="text" name="user"/>
    <input type="password" name="pass"/>
    <input type='submit'/>
  </form>
</body>
```

> ### GET POST

> - GET:
>   - 提交後, 數據會放在 url 裡, 亦即 head 中
>     ![](https://i.imgur.com/vyCANbg.png)
>   - 因為在 head 中, 數據容量小
>   - 看得見
>     - 利於分享收藏, 例如 
>       https://www.youtube.com/watch?v=Ynpe9FIgUCM
>       還是需要有看得見的參數傳給別人, 才能讓人快速到達目標
>   - 有緩存

> - POST:
>   - 提交後, 數據會在 body 中
>   - 因為在 body 中, 數據容量大
>   - 如果數據需求大, 一般會使用額外的客戶端或插件來傳送
>   - 看不見
>     - 看不見不等於比較安全, https 才是相對安全
>   - 沒緩存


> - #### 表單驗證範例
```javascript=
<head>
  <meta charset='utf-8'>
  <script>
  /* 獲取元素酷炫寫法1.
  const $ = function (...args){
    return document.querySelectorAll(...args);
  }
  */

  // 獲取元素酷炫寫法2.
    // 必須 bind this, 
    // 否則如果只有 querySelectorAll 附址到 $ 後, 
    // $ 是綁定在全局變量(window), 不是 #document
  const $ = document.querySelectorAll.bind(document);

  window.onload = function () {
    // let f = document.getElementById('f');
    let f = $(`#f`)[0]; // 酷炫寫法
    let u = document.getElementsByName('user')[0];

    f.onsubmit = function () {
      if (u.value == '') {
        alert('給我寫帳號');
        return false;
      }
    }
    console.log(f);
  }
  </script>

</head>
<body>
  <form action="" method="GET" id='f'>
    <input type="text" name="user"/>
    <input type="password" name="pass"/>
    <input type='submit'/>
  </form>
  <script src='test.js'></script>
</body>
```

> - #### 如何避免重複提交表單?
>   - 設定當提交後 `disable` 為 true, 確認收到之後再打開就行了



### ajax
> - 不用刷新頁面, 可以跨域, 最常用
> - 用 ajax 送 get 也不會顯示在 url

> #### 演示一下
> - 創建三個檔案
>   - `ajax.html` : 以 `ajax` 送資料
>   - `form.html` : 以 表單 送資料 => 拿來對比 ajax 傳資料的不同
>   - `1.php` : php 接口
```shell
$ pwd # 註1.
/Users/...略/htdocs/test

$ ls
1.php  ajax.html  form.html  jquery.js
```
> - 註1. 我用 terminal cd 到路徑的方法, 直接開到那個資料夾, 右鍵複製路徑
>   <img src='https://i.imgur.com/IWWdExM.png' style='width: 300px;'/> => <img src='https://i.imgur.com/yhzRYvI.png' style='width: 200px;'/>

> - #### ajax 送資料
>   - ##### ajax 小筆記
>     - type 沒寫默認也是 GET
>   - dataType
>     - 在後端有聲明數據類型的前提下, 可以省
>     - 為了避免後端沒處理, 還是寫一下好
>     - dataType 種類: text, xml, json, jsonp
> - ##### php 小筆記
>   - echo 是 php 輸出的意思
>   - 前端傳 GET 就用 $_GET 收, 
>     傳 POST 就用 $_POST 收,
>     - GET 跟 POST 事實上區別不大

`$ vi ajax.html`
```htmlmixed=
<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>
    <script src='jquery.js' charset='utf-8'></script>
    <script>
    $(function () {
      $(`#btn`).click(function () {
        $.ajax({
          url: '1.php',
          data: {a: 10, b:5},
          type: 'get',      //=> 沒寫默認也是GET
          dataType: 'text', //=> 在後端有聲明數據類型的前提下,  
                            //   dataType可以省, 保險起見還是寫一下,
                            //   dataType 有幾種: text, xml, json, jsonp
          success(str){
            alert(str);
          },
          error() {
            alert('請求失敗')
          }
        })
      })
    })
    </script>
  </head>
  <body>
    <input type='submit' id='btn'/>
  </body>
</html>
```
`$ 1.php`
```php
<?php

echo $_GET['a'] + $_GET['b']; 
// echo 是 php 輸出的意思
// 前面傳GET, 所以這裡用 $_GET 收
// 如果前面傳 POST, 這邊當然就用 $_POST 收
?>
```
> - 開啟 xampp 地址 = `http://IP/檔案` 或直接點 Go to Applocation 改後面資料處
> - 結果演示
>   - `http://192.xx.xx.xx/test/ajax.html`
>     <img src='https://i.imgur.com/QOSV3vy.png' style='width: 100px;'/> => 點擊後 => <img src='https://i.imgur.com/O4aFCbu.png' style='width: 300px;'/>
>   - 網頁都沒有刷新就跳出結果了! 當然url 也沒有出現參數

> - #### 對照: 同一個 php 用 form 送資料
`$ vi form.html`
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <!-- IP 我改掉了, 當然不可能是 xx.xx -->
  <!-- action: 要送去哪-->
  <!-- method: 怎麼送-->
  <form action="http://192.xx.xx.xx/test/1.php" method='get'>
    <input type="text" name='a'> <!-- 必須要有name-->
    <input type="text" name='b'>
    <input type="submit"> <!-- 還要有送出鈕 -->
  </form>
</body>
</html>
```
> - 開啟 xampp 地址
>   - `http://192.xx.xx.xx/test/form.html`
>   <img src='https://i.imgur.com/4U3hA4R.png' style='width: 300px;'/> => 填值後送出
>   - 畫面跑出 <img src='https://i.imgur.com/KWZwLZy.png' style='width: 50px;'/> 且
>     網址刷新過變成 `http://192.xx.xx.xx/test/1.php?a=100&b=200`

> - #### 小結: 表單與 ajax 主要差別
>   - 會不會跳轉
>     - 表單會
>     - ajax 不會
>   - 某些傳送方法會不會把參數寫在url上
>     - 表單的 GET 會
>     - ajax 的 GET 不會
>   - 穩定性不同
>     - 表單相對穩定, 因為是瀏覽器的功能
>     - ajax是 js 的功能, 相對沒表單穩定, 但用戶體驗較好

### XMLHttpRequest
> - ajax 的本體, 所以有些人會叫 ajax 為 xhr
> - IE6 不兼容, IE6 使用 `ActiveXObject('Microsoft.XMLHttp')` 
>   - `ActiveXObject()` 是 IE 伺服器的插件

```htmlmixed=
<!DOCTYPE html>
<html lang='en'>
	<head>
		<meta charset='utf-8'>
		<script src='ajax2.js'></script>
	</head>
	<body>
		<input type='submit' id='btn'/>
	</body>
</html>
```
```javascript=
window.onload = function () {
  let btn = document.getElementById('btn');
  btn.onclick = function () {
    let xhr = new XMLHttpRequest();
    /* 兼容IE6 寫法 
    let xhr
    if (window.XMLHttpRequest) {
      xhr = new XMLHttpRequest();
    } else {
      xhr = new ActiceXObject('Microsoft.XMLHttp')
    }
    */

    console.dir(xhr);              // XMLHttpRequest 實例對象
    console.dir(xhr.readyState);   // 0
    xhr.open('GET', '1.php?a=3&b=2', true);
    //xhr.open('GET', '2.php', true); // 我測試錯誤用的, 傳資料給不存在的檔案
    console.dir(xhr.readyState);   // 1

    xhr.send();

    xhr.onreadystatechange = function () {
      console.log(xhr.readyState, xhr.status);
        /*
            2 200
            3 200
            4 200
         */
      if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
          console.log('OK', xhr.responseText); 
            // OK 5
              // xhr.responseText 拿到回來的資料
              // xhr.responseText 送資料給 1.php a=3, b=2 所以回來 5
        } else {
          console.log('No OK');
        }
      }
    }
  }
}
```
```php=
<?php

echo $_GET['a'] + $_GET['b'];

?>
```


### xhr.open()
`xhrReq.open(method, url, async, user, password);`
> - `async` 參數寫 Boolean, true 表示異步, false表示同步
>   - 開同步會被瀏覽器罵, 不過沒有不能開~只是說爛東西別用而已
>     Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience.

### xhr.send()
> - 傳 body 的東西
> - 如果要傳 `{a:1, b:{c:2}}` 要怎麼處理?
>   - JSON
>     - `{"a": 1, "b": {"c": 2}}`
>     - content-type: application/json
>   - 變相
>     - 編碼
>       - `data="%7B%22a%22%3A1%2C%20%22b%22%3A%20%7B%22c%22%3A2%7D%7D"`
>     - content-type: urlencoded
>     ```javascript
>     // 空格可以刪掉, 省空間
>     encodeURIComponent(`{"a":1, "b": {"c":2}}`)
>     "%7B%22a%22%3A1%2C%20%22b%22%3A%20%7B%22c%22%3A2%7D%7D"
>     decodeURIComponent("%7B%22a%22%3A1%2C%20%22b%22%3A%20%7B%22c%22%3A2%7D%7D")
>     "{"a":1, "b": {"c":2}}"
>     ```
### xhr.onreadystatechange
`XMLHttpRequest.onreadystatechange = callback;`
> - 當準備狀態(通信狀態)改變時, 執行回調,
>   - xhr.readyState :
>     - 純粹紀錄通信狀態, 跟成功失敗與否無關
>     - `0` : UNSENT -> 剛剛創建, 初始狀態, 還沒調用 `open()`
>     - `1` : OPENED -> `open()`以調用已連接
>     - `2` : HEADERS_RECEIVED -> `send()` 方法已被呼叫, 且可取得 header 與狀態
>     - `3` : LOADING -> 回應資料下載中，此時 `responseText` 會擁有部分資料(head)。
>     - `4` : DONE, 完成下載操作(body也搞定)

### xhr.status : 
> - [HTTP 狀態碼](https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81)
>   - `1` : 資訊
>   - `2` : 成功
>   - `3` : 重定向
>     - `301` : 永久重定向
>     - `302` : 臨時重定向
>       ```
>       General
>         Request URL: https://yahoo.com/    # 請求 yahoo.com
>         Status Code: 301                   # 重定向到
>       Request Header
>         location: https://www.yahoo.com/   # www.yahoo.com
>       ```
>     - `304` : 重定向緩存
>       ![](https://i.imgur.com/kLv6sas.jpg)
>       - 如果再次向服務器要同一個檔名, 服務器會判斷時間版本是否最新
>         - 如果是最新的, 返回 304
>         - 如果是舊的, 重傳內容跟 202
>     - 重定向 (redirect) vs 轉發 (forward)
>       - 重定向: 
>         - 對瀏覽器下命令, 讓瀏覽器訪問新的 loaction
>         - 網址會變
>       - 轉發:
>         - 服務器內部把請求交給其他 module 處理
>         - 網址不變
>       
>   - `4` : 請求失敗(客戶端)
>     - `404`
>   - `5` : 服務器錯誤
>     - `500` : 反正有錯都算 500
>     - `503` : 服務器維護或擠爆了
>   - `6` : 自定義(6以後)

### xhr.responseText
> - 回應文本

### xhr.responseXML
> - 回應 xml
> #### xml
> - xml 與 html 最大區別在於 html tagName 是固定的
```xml
<!-- json 一句話就搞定, 體積小, 又方便, 所以現在比較少見到 xml 了
let people = {name:'GodJJ', age: 20};
-->

<!-- xml 寫法 -->
<people>
  <name>GodJJ</name>
  <age>20</age>
</people>

<!-- 操作xml
xml = getElementsByTagName('name')[0];
-->
```


### ajax POST 問題
> #### 前情提要
> - 如果我把上面的案例的 GET 改成 POST, 參數改到 `send()` 裡 ( 用 body 傳 )
```javascript=
window.onload = function () {
  let btn = document.getElementById('btn');
  btn.onclick = function () {
    let xhr = new XMLHttpRequest();
    xhr.open('POST', 'g.php?', true); // 把 GET 改成 POST
    xhr.send('a=3&b=2'); // 把 資料參數用 send() 傳
    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4) {
        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
          console.log(xhr.responseText);
        } else {
          console.log('ERROR');
        }
      }
    }
  }
}
```
```php=
<?php
echo $_POST['a'] + $_POST['b']; # 接收 POST
?>
```
> - 運行的結果: 找不到 a b 
```
<br />
<b>Notice</b>:  Undefined index: a in <b>/opt/lampp/htdocs/test/g.php</b> on line <b>2</b><br />
<br />
<b>Notice</b>:  Undefined index: b in <b>/opt/lampp/htdocs/test/g.php</b> on line <b>2</b><br />
0
```
> - 但是用 form 可以送到
```htmlmixed=
<head>
  <meta charset='utf-8'>
</head>
<body>
  <!-- GET 改成 POST -->
  <form action='http://192.xx.xx.x/test/g.php' method='POST'>
    <input type='text' name='a'>
    <input type='text' name='b'>
    <input type='submit'>
  </form>
</body>
```

> - 查看報文

```
// FORM POST - request //
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-TW;q=0.8,zh;q=0.7
Cache-Control: max-age=0
Connection: keep-alive
Content-Length: 11
Content-Type: application/x-www-form-urlencoded   #=> 問題在這行
Host: 192.168.64.2
Origin: http://192.168.64.2
Referer: http://192.168.64.2/test/gForm.html
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36


// AJAX POST - request //
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-TW;q=0.8,zh;q=0.7
Connection: keep-alive
Content-Length: 7
Content-Type: text/plain;charset=UTF-8    #=> 問題在這行
Host: 192.168.64.2
Origin: http://192.168.64.2
Referer: http://192.168.64.2/test/gAjax.html
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
```
> - Content-Type 的用意在於, 他是什麼類型, 服務器就怎麼解析
> - FORM POST 傳給服務器的 Content-Type 依照 enctype 屬性有三種, 
>   - application/x-www-form-urlencoded
>     - 預設
>       ```html
>       <form action='' method='POST'></form>
>       ```
>     - urlencoded: `a=1&b=2&c=3`, 反正就是用 & 分隔數據的方法
>     - 常用於處理簡單數據
>   - multipart/form-data; boundary=----WebKitFormBoundaryWBQBnwHG6EAMjPQA
>     - 通常是上傳檔案時用的
>       ```html
>       <form action='' method='POST' enctype='multipart/form-data'></form>
>       ```
>     - 以 delimiter 分隔數據
>   - text/plain
>     - 純文本
> - Ajax 預設的 Content-Type 是 `text/plain`, 用純文本解析 `'a=3&b=2'`, 當然拿不到值

> #### 解決辦法: 模仿 FORM 傳一樣的 Content-Type

### setRequestHeader()
`myReq.setRequestHeader(header, value);`
> - 設置 HTTP 請求頭部的方法, 
> - header: 屬性名
> - value: 屬性值
> - 必須放在 `open()` 跟 `send()` 之間
>   - 放在 `open()` 前面: 都還沒連接, 是要設置什麼?
>   - 放在 `send()` 後面: 都送出請求了, 是要設置什麼?

> - 當然 Content-Type 也不用制式的跟著標準走, 隨便你怎麼設, 但是後端要寫處理方法 

```javascript=
window.onload = function () {
  let btn = document.getElementById('btn');
  btn.onclick = function () {
    let xhr = new XMLHttpRequest();
    xhr.open('POST', 'g.php?', true);
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
    xhr.send('a=3&b=2');
    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4) {
        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
          console.log(xhr.responseText);
        } else {
          console.log('ERROR');
        }
      }
    }
  }
}
```


### 練習-模仿JQ: 封裝 GET 與 POST 成 ajax 函數

> - 版本一: 
>   - 先把函數拆出來, 並引入
>   - 把需要的東西傳一傳
>   - 由於 GET 跟 POST 有些差別, 必須做些處理
>     - 參數:
>       - GET 的參數跟 URL 是綁在一起的 
>         - `xhr.open('1.php?a=2&b=3')`;
>       - POST 的參數是單獨丟到 `send()` 裡
>         ```
>         xhr.open('1.php');
>         xhr.send('a=2&b=3');
>         ```
>     - 改 content-type
>       - POST 要特別寫, GET 不用
>   - 數據:
>     - 數據一般都是 JSON 格式, 傳進來要處理成參數的樣子
>     - 成功時, 使用 callback 把結果傳出來
```shell
$ tree
.
├── ajax.js     #=> 拆出來可複用的 ajax 函數
├── g.php       #=> 接收GET數據的 php
├── g2.js       #=> 點擊調用的 js
├── gAjax.html  #=> 開啟的頁面
└── p.php       #=> 接收POST數據的 php
```
```htmlmixed=
<head>
  <meta charset='utf-8'>
  <script src='ajax.js'></script> # 引入
  <script src='g2.js'></script>
</head>
<body>
  <input type='submit' id='btn'>
</body>
```
```javascript=
function ajax(url, type='GET', data={}, success, error) {
  let xhr = new XMLHttpRequest();


  // - 數據處理
  let arr = [];
  for (let name in data) {
    // 傳進來的參數 => {a:1, b:2}
    // 想變成的形式 => 'a=1&b=2'
    arr.push(`${name}=${data[name]}`) // arr.push(`a=1`)
  }
  console.log(arr); // ["a=1", "b=2"]
  let dataStr = arr.join(`&`);
  console.log(dataStr); // a=1&b=2


  // - GET POST 判斷
  if (type == 'POST') {
    xhr.open('POST', url, true);
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
    xhr.send(dataStr);
  } else if (type == 'GET') {
    xhr.open('GET', url+`?`+dataStr, true); // 1.php?a=2&b=3
    xhr.send();
  }


  xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
      // 只是輸出, 沒有任何意義, 所以要傳兩個 callback 把數據傳出去
      if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
        console.log('ajax: ' + xhr.responseText);
        success && success(xhr.responseText) // 如果有 callback 近來就調用
      } else {
        console.log('AJAX: ERROR');
        error && error(); // 如果有 callback 近來就調用
      }
    }
  }
}
```
```javascript=
window.onload = function () {
  let btn = document.getElementById('btn');
  btn.onclick = function () {
    ajax('p.php', 'POST', {a:3, b:4}, 
    function (x) {  // 成功時, 把php拿到的東西傳出來處理
      console.log('回調:' + x);
    }, 
    function () {
      console.log('回調: error');
    })
  }
}
```
```php=
<?php
echo $_GET['a'] + $_GET['b'];
?>
```
```php=
<?php
echo $_POST['a'] + $_POST['b'];
?>
```
> - 版本二: 參數太多了
>   - 直接傳一個 JSON 進來
```javascript=
function ajax(option) {
  let xhr = new XMLHttpRequest();
  
  // 參數默認值處理
  option = option || {};
  option.data = option.data || {};
  option.type = option.type || 'GET';

  // 使用參數全部要改成從 option 找
  let arr = [];
  for (let name in option.data) {
    arr.push(`${name}=${option.data[name]}`)
  }
  let dataStr = arr.join(`&`);


  if (option.type == 'POST') {
    xhr.open('POST', option.url, true);
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
    xhr.send(dataStr);
  } else if (option.type == 'GET') {
    xhr.open('GET', option.url+`?`+dataStr, true);
    xhr.send();
  }

  xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
      if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
        console.log('ajax: ' + xhr.responseText);
        option.success && option.success(xhr.responseText)
      } else {
        console.log('AJAX: ERROR');
        option.error && option.error();
      }
    }
  }
}
```
```javascript=
window.onload = function () {
  let btn = document.getElementById('btn');
  btn.onclick = function () {
    ajax({
      url: 'p2.php',
      type: 'POST',
      data: {a:30, b:20},
      success: function (x) {
        console.log(`回調: ${x}`);
      },
      error: function () {
        console.log(`回調: error`);
      }
    })
  }
}
```
> - 版本三: 我想要 JSON 形式的數據
>   - 傳出來的數據是從 php 返回的, 所以要改 php
>   - 把資料轉成 json
```php=
<?php
$res = $_POST['a'] + $_POST['b'];
echo '{"resault": '.$res.'}';  # php 用 . 拼接字符串
?>
```
```php=
<?php
$res = $_GET['a'] + $_GET['b'];
echo '{"result": '.$res.'}';
?>
```
```javascript=
window.onload = function () {
  let btn = document.getElementById('btn');
  btn.onclick = function () {
    ajax({
      url: 'g.php',
      type: 'GET',
      dataType: 'json', // 資料類型 json
      data: {a:1, b:2},
      success: function (data) {
        console.log(data);
      },
      error: function () {
        console.log(`error`);
      }
    })
  }
}
```

```javascript=
function ajax(option) {
  let xhr = new XMLHttpRequest();

  option = option || {};
  option.data = option.data || {};
  option.type = option.type || 'GET';
  option.dataType = option.dataType || 'text'; // 預設類型為 'text'

  let arr = [];
  for (let name in option.data) {
    arr.push(`${name}=${option.data[name]}`)
  }
  console.log(arr);
  let dataStr = arr.join(`&`);
  console.log(dataStr);


  if (option.type == 'POST') {
    xhr.open('POST', option.url, true);
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
    xhr.send(dataStr);
  } else if (option.type == 'GET') {
    xhr.open('GET', option.url+`?`+dataStr, true);
    xhr.send();
  }

  xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
      if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
      
        // 處理資料
        let data = xhr.responseText;
        
          // 如果是 json
        if (option.dataType == 'json') {
          if (window.JSON && JSON.parse) { // 用 JSON.parse 轉
            data = JSON.parse(data);
          } else {
            data = eval('{'+data+'}') // 相容處理
          }
        } else if (option.dataType == 'xml') { // 如果是 xml
          data = xhr.responseXML;  // 資料獲取就在 responseXML 裡 
        }


        option.success && option.success(data);

      } else {
        console.log('AJAX: ERROR');
        option.error && option.error();
      }
    }
  }
}
```
> #### JSON.parse
> - `JSON.parse(text[, reviver])`
>   - `text` : 要解析的字串
>   - `reviver` : 這是一個 function, 會把解析出來的物件或數值當參數傳進去處理
> - 把字串轉成 JS 可執行的物件或數值, 並 return
> - IE8+
> - JSON 必須雙引號
```javascript=
JSON.parse('[]') // []
JSON.parse('1') // 1
JSON.parse('"dd"') // "dd"


JSON.parse('{\'a\':1}') // Unexpected token ' in JSON
JSON.parse('{"a":1}') // {a: 1} //=> 必須要雙引號



JSON.parse('undefined') // Unexpected token u in JSON
JSON.parse('null') // null
JSON.parse('true') // true


// 不允許尾部有逗號
JSON.parse('[1,2,3]') // [1, 2, 3]
JSON.parse('[1,2,3,]') // Unexpected token ] in JSON


JSON.parse('{"a":1, "b":2}')  // {a: 1, b: 2}
JSON.parse('{"a":1, "b":2, }') // Unexpected token } in JSON


// JSON 必須雙引號
JSON.parse("{'error': 1, 'msg': 'GIVE ME PASSWORD!'}"); // Unexpected token ' in JSON
JSON.parse(`{"error": 1, "msg": "GIVE ME PASSWORD!"}`); // {error: 1, msg: "GIVE ME PASSWORD!"}
```

```javascript=
JSON.parse('{"a": 1, "b":2}', function () {
    console.log(arguments);
})
// Arguments(2) ["a", 1, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// Arguments(2) ["b", 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// Arguments(2) ["", {…}, callee: ƒ, Symbol(Symbol.iterator): ƒ]

  //=> JSON.parse() 會把 解析出來的值放在 [k,v] 傳進 reviver 參數裡 


JSON.parse('{"a": 1, "b":2}', function (k,v) {
    if (typeof v == 'number') {
        return v * 2
    }
    return v;
})
// {a: 2, b: 4}
```

> #### JSON.stringify
> `JSON.stringify(value[, replacer [, space]])`
> - 把JS的值變成JSON格式的字串, 主要拿來轉物件的
```javascript=
// 主要用途
JSON.stringify({a:1, b:2});  // "{"a":1,"b":2}"
JSON.stringify({'a':1, b:2});  // "{"a":1,"b":2}"

// 其他也能轉字串, 不過主要不是拿來幹這事的
JSON.stringify([1,2,3]);  // "[1,2,3]"
```


### jsonp (少)
>   - 可以跨域, 安全性差
>   

#### 先偷個數據下面要用

> - 打開搜尋引擎, 輸入關鍵字
>   <img src='https://i.imgur.com/BjIt3Px.png' style='width: 300px;'/>
>   - 此時會看到Google 推薦的關鍵字
>   - 你每打一個字, 就會像服務器請求數據
>     <img src='https://i.imgur.com/MYq6OZO.png' style='width: 200px;'/>
>     我打了apple 五個字, 所以請求了五次

> - 研究一下請求, 把請求copy出來
>   - 方法很多, 可以直接看報文
>     <img src='https://i.imgur.com/OukqUF0.png' style='width: 200px;'/>
>   - 也可以右鍵 copy
>     <img src='https://i.imgur.com/z7CoNFZ.png' style='width: 200px;'/>


```
# Google 搜 apple 的請求
https://www.google.com/complete/search?q=apple&cp=5&client=psy-ab&xssi=t&gs_ri=gws-wiz&hl=zh-TW&authuser=0&psi=tf8PXtGxI66xmAX88pCoCA.1578106806267&ei=tf8PXtGxI66xmAX88pCoCA

q=apple&
cp=5&
client=psy-ab&
xssi=t&
gs_ri=gws-wiz&
hl=zh-TW&
authuser=0&
psi=tf8PXtGxI66xmAX88pCoCA.1578106806267&
ei=tf8PXtGxI66xmAX88pCoCA

# 不過因為我看不懂 Google 的 callback 參數在哪, 所以去 yehoo 拿一份
https://tw.search.yahoo.com/sugg/gossip/gossip-tw-ura/?output=fxjsonp&callback=YUI.Env.JSONP.yui_3_12_0_3_1578107249520_1530&command=apple&nresults=12

output=fxjsonp&
callback=YUI.Env.JSONP.yui_3_12_0_3_1578107249520_1530& # 我要的東西
command=apple&                                          # 我要的東西
nresults=12

# 把攏長的網址整理一下
  # 我只是把 callback 名字改成 show, 其他都沒動
https://tw.search.yahoo.com/sugg/gossip/gossip-tw-ura/?output=fxjsonp&callback=show&command=apple&nresults=12
```
> - 搜一下網址, 會得到一份文檔, 有沒有整理都會拿到

```txt
# Google 搜 apple 的數據
)]}'
[[["apple",0],["apple id",0,[67],{"zl":40009}],["apple watch",0,[67],{"zl":40009}],["apple id 是什麼",0,[67],{"zl":40009}],["apple store",0],["apple tv 是什麼",0,[67],{"zl":40009}],["apple tv",0],["apple watch 5",0],["apple pay",0],["apple music",0]],{"k":1,"q":"roKR-j4OGoRF1fbiF_2KERX9cFU"}]

# yahoo 搜 apple 的數據(整理前)
YUI.Env.JSONP.yui_3_12_0_3_1578107249520_1530(["apple",["apple","apple store","apple watch","apple daily","apple tv","apple id","apple pay","apple pencil","apple ipad","apple airpods","apple music","apple iphone 11"],[],[]])

# yahoo 搜 apple 的數據(整理後)
  # 數據都一樣, 只是改了 callback 名
show(["apple",["apple","apple store","apple watch","apple daily","apple tv","apple id","apple pay","apple pencil","apple ipad","apple airpods","apple music","apple iphone 11"],[],[]])
```

#### jsonp 原理
> - 準備一個回調函數讓人來調, 調用的過程一定會傳參數進來, 此時就拿到數據了
> - 如此就達到跨域效果
> - 導致缺點就是安全性太低(過於開放)
```html=
<head>
  <meta charset='utf-8'>
  <script>
  // 準備一個回調函數, 等別人來調
  function show(json) {
    console.log(json);
  }
  </script>
  <!-- 本地調用 -->
  <script src='appleData.js'></script>
  <!-- 給 yahoo 調用 -->
  <script src='https://tw.search.yahoo.com/sugg/gossip/gossip-tw-ura/?output=fxjsonp&callback=show&command=apple&nresults=12'></script>
  
  <!-- 結果都一樣
  ["apple", Array(12), Array(0), Array(0)]
  ["apple", Array(12), Array(0), Array(0)]
  -->
</head>
```
```javascript=
// 調用函數
show(["apple",["apple","apple store","apple watch","apple daily","apple tv","apple id","apple pay","apple pencil","apple ipad","apple airpods","apple music","apple iphone 11"],[],[]])
```
#### 運用
> #### 版本一
> - 輸入搜尋字, 賦予 script 函數 src 值
```html=
<head>
  <meta charset='utf-8'>
  <script>
  function show(ajax) {
    console.log(ajax);
  }
  window.onload = function () {
    let txt = document.querySelector('#txt');
    
    // 當我輸入時, 讓 script 賦予 src
    txt.oninput = function () { 
      let s = document.querySelector('#s');
      s.src = `https://tw.search.yahoo.com/sugg/gossip/gossip-tw-ura/?output=fxjsonp&callback=show&command=${txt.value}&nresults=12`;
        // 賦予之後 yahoo 會調用上面的 show 函數來執行
    }
  }
  </script>
  <!-- 寫一個script 標籤 -->
  <script id='s'></script>
</head>
<body>
  <input type='text' id='txt'>
</body>

<!-- 問題:
     假設我輸入 apple, 但是結果只會產生一個陣列, 
     理論上應該每打一個關鍵字就會像服務器請求一次啊?
     而且產生的陣列不是 apple 的相關 , 而是 ["a", Array(12), Array(0), Array(0)] !!!
     
     原因:
     當我打入第一個 a 時就已經調用一次 show()了
     
     亦即 <script id='s'></script>
     => 變成 <script src='https://tw.search.yahoo.com/.../...&callback=show&command=a&...'></script>
     => 調用 show(["a", Array(12), Array(0), Array(0)]);
     
     執行完了, 你再改他的 src 一萬次, 他也不會往回跑去執行上面的程式碼, 
     也就是說 script 標籤執行完就廢了, 跟 img 不同
-->
```
> #### 版本二
> - 解決上述問題
> - 解決編碼問題
>   - 編碼:
>     - 如果搜尋的是中文, 那`${txt.value}`變中文, url 地址就有中文, 
>       有些瀏覽器並不會幫你編碼而會掛掉
>     - 甚至如果搜尋的東西在 url 有特殊含意, 例如 `&` `=`..., 那肯定會出問題
>   - `encodeURIComponent()` 
>     - 這東西就是編碼用的, 返回編碼後的結果
>       ```javascript
>       encodeURIComponent('中文') // "%E4%B8%AD%E6%96%87"
>       encodeURIComponent('a&b') // "a%26b"
>       ```
```htmlmixed=
<head>
  <meta charset='utf-8'>
  <script>
  function show(ajax) {
    // console.log(ajax);
      // ["apple", Array(12), Array(0), Array(0)]
      // Yahoo 調用回調的參數是陣列, 且數據在 [1]
    console.log(ajax[1]);

    // 額外寫個 ul li, 把數據呈現出來
    let box = document.querySelector('#box');
    box.innerHTML = '';
    ajax[1].forEach(v => {
      // console.log(v);
      let li = document.createElement('li');
      li.innerText = v;
      box.appendChild(li);
    })
  }
  window.onload = function () {
    let txt = document.querySelector('#txt');
    txt.oninput = function () {
      // 上面的改版, 每次輸入都創建一個 script 標籤, 這樣就不會有標籤廢了的問題
      let s = document.createElement('script');
      // s.src = `https://tw.search.yahoo.com/sugg/gossip/gossip-tw-ura/?output=fxjsonp&callback=show&command=${txt.value}&nresults=12`;
      // 編碼
      s.src = `https://tw.search.yahoo.com/sugg/gossip/gossip-tw-ura/?output=fxjsonp&callback=show&command=${encodeURIComponent(txt.value)}&nresults=12`;
      document.head.appendChild(s);
    }
  }
  </script>
</head>
<body>
  <input type='text' id='txt'>
  <ul id='box'></ul>
</body>
```





### WebSocket
>   - 性能高 vs ajax(相較差)
>   - 雙向 vs ajax(單向: 客戶端向服務器)
>     - ajax 只能開定時器一直看服務器變化通知
>     - WebSocket 比較接近 callback, 一有變化馬上調用來通知
>   - 直接跨域 vs ajax(跨域麻煩)

### 跨域
> - `google.com` -> `yahoo.com` 拿東西
> - 跨域有風險,例如 [XSS](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC), 所以瀏覽器會有所限制
> - 雖然有風險,但有時又是必須的
>   - 有些網站有多個域名
>   - 有些功能需要第三方數據, 例如登入, 需要第三方告訴你帳密對不對
> - 上面四種都能跨域, 差別在於方便與安全與否

### http
> - http vs https
>   - s: security
> - 版本
>   - http1.0 一次性連接
>   - http1.1 保持連接 
>   - http2.0 
>     - 強制 https (提升安全)
>     - 自帶雙向 (提升效能)
>     - 多路復用 (提升效能)
> - [rfc](https://zh.wikipedia.org/wiki/RFC) : 
>   - 這是一套編碼, 
>   - 用來記錄各種網路協議規範
>   - [http1.1](https://www.ietf.org/rfc/rfc2616.txt)
> - 三次握手
>   <img src='https://i.imgur.com/14rIOGD.jpg' style='width: 300px;'/>
> - http 數據(消息)
>   - head <= 32k
>   - body <= 1G
> - [OSI](https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B) 七層模型
>   - 實體層     
>     - 通信工程, 物理實體方面的
>   - 資料連結層
>     - 內網尋址 
>     - ARP, ICMP(ping)
>   - 網路層 
>     - 外網尋址
>     - IP
>   - 傳輸層
>     - 通信穩定
>     - TCP: 傳輸控制協議
>       - 保證到達(丟失重傳)
>       - 保證正確(錯誤重發)
>       - 保證順序
>       - 文件下載, 聊天
>     - UDP: 用戶數據協議
>       - 三大不保證
>       - 要求低延遲
>       - 直播, IP電話
>   - 會議層
>     - 統一網路結構
>     - 被傳輸層取代
>   - 表達層
>     - 紀錄狀態
>       - vs. 無狀態: 服務器不會記得客戶端
>     - 應用層取代 
>   - 應用層
>     - 應用細節
>     - HTTP, FTP, SMIP, POP3
>     - HTTP 還是無狀態, 只是運用 coockie 紀錄狀態



## 進程與線程
### 進程
> - 進程擁有獨立的執行與存儲空間 (php node)
>   - 進程執行成本高(慢)
>   - 進程比較安全(隔離)
>   - 進程間通信麻煩
>     - 兄弟進程通信
>       - 管道
>       - 共享內存
>       - socket
>   - 書寫簡單
### 線程
> - 進程內所有線程共享一套空間 (java c)
>   - 線程執行成本低(快)
>   - 線程比較不安全(一個掛了, 如沒特別處理, 整個進程就可能掛了)
>     - java 的每個線程都在虛擬機上執行, 虛擬機可以選擇只關掉一個線程
>   - 線程間通信較容易
>   - 書寫麻煩
### 其他
> - 拹程, 在線程裡面再切
> - 堵塞IO: 整個程序卡住
> - 非堵塞IO: 不卡住, 繼續往下執行



## 暫放
### 深拷貝與淺拷貝
> - 淺拷貝
```javascript=
let arr = [{a:1, b:2}, 2,3];
let arr2 = arr.concat([]);


arr2.push(4);


console.log(arr); // [ { a: 1, b: 2 }, 2, 3 ]
console.log(arr2); // [ { a: 1, b: 2 }, 2, 3, 4 ] //=> 淺拷貝成功
```
```javascript=
let arr = [{a:1, b:2}, 2,3];
let arr2 = arr.concat([]);


arr2.push(4);
arr2[0].a++;  // 對 [0] 的那個對象的 a ++


console.log(arr);  // [ { a: 2, b: 2 }, 2, 3 ]  //=> 他也跟著變了
console.log(arr2); // [ { a: 2, b: 2 }, 2, 3, 4 ] 

// 淺拷貝只拷貝一層, 如果是對象, 拷貝址
```
> - 深拷貝
```javascript=
let arr = [{a:1, b:2}, 2,3];
let arr2 = JSON.parse(JSON.stringify(arr));


arr2.push(4);
arr2[0].a++;


console.log(arr);  // [ { a: 1, b: 2 }, 2, 3 ]   //=> 沒變
console.log(arr2); // [ { a: 2, b: 2 }, 2, 3, 4 ]

// 深拷貝把整個對象也都拷貝了一次, 兩個不同址
```






## 垃圾回收
> - Node, JS, java, python 等的 GC 核心邏輯:
>   - 只要內存不在使用即釋放
>   - 引用技術
> - C 自己手動釋放
>   - 缺點: 內存泄露(忘記釋放)




## 小技巧
### Q. 如何做到 f5 的效果跟 ctrl + r 一樣, 強迫瀏覽器每次都向服務器從新獲取資料?
> - 1. 後台設置
> - 2. `<script src='1.js?v=202001011115'></script>`
>      - 在引用後面加參數, 每次都改後面參數就行了
>      - 讓 url 不同, 瀏覽器就會訪問伺服器
>      

### Q. 服務器緩衝池?
> - 把最常用的放在內存


### 常見效果: 滑鼠移動, 背景移動
```htmlmixed=
<head>
  <meta charset='utf-8'>
  <style>
    div {
      height: 150px;
      width: 150px;
      position: absolute;
    }
  </style>
  <script>
    window.onload = function () {
      let oDiv = document.querySelectorAll(`div`);
      let divPos = Array.from(oDiv).map(div=>{
        console.dir(div);
        return {
          left: div.offsetLeft,
          top: div.offsetTop
        }
      })
      console.log(divPos);

      document.onmousemove = function (ev) {
        // console.log(ev.clientX);
        Array.from(oDiv).forEach((div, index)=>{
          div.style.left = divPos[index].left + ev.clientX*div.style.zIndex/100 + `px`;
          div.style.top = divPos[index].top + ev.clientX*div.style.zIndex/100 + `px`;
        })
      }
    }
  </script>
</head>
<body>
  <div style='left: 100px; top: 100px; background: red; z-index:1;'></div>
  <div style='left: 50px; top: 30px; background: blue; z-index:2;'></div>
  <div style='left: 200px; top: 70px; background:yellow; z-index:3;'></div>
  <div style='left: 250px; top: 200px; background: green; z-index:4;'></div>
</body>
```



## Q&A 筆記
> - Q. 頁面渲染
>   #### 最早本意
>   - 渲染就是把頁面的東西顯示出來, 跟繪製這個詞的意義差不多
>   - 頁面渲染慢的原因
>     - 頁面東西多
>     - 圖片
>       - 圖片還需要解碼等雜事
>     - 經常做DOM操作
>       - 每次操作都會改變頁面狀態而重新渲染
>   #### 衍伸
>   - 組裝頁面
>     - 後台渲染 ejs
>     - 前台渲染 vue
> - Q. 服務端渲染(server side rendering) 常用？
>   - ejs
>   - pug
>   - vue 主要用來前端渲染
> - Q. 如何找回系統刪除的文件
>   - 關鍵字: 磁盤分區表置回
>   




> - Q. 如何調適
>   - Debugger
>     - 以網頁來說可以用開發者工具
>       ![](https://i.imgur.com/wosUZRW.png)
>       壓縮過的文件可以點擊左下那個 {} 來顯示
>       - 這東西可以設置斷點啦之類有的沒的, 想要看某個東西的詳情,
>         可以選取後右鍵 add watch
>         <img src='https://i.imgur.com/GWIe2wc.png' style='width: 250px'/><img src='https://i.imgur.com/3Sv3xfa.png' style='width: 250px'/>
>     - NodeJS Debugger
>       - 官方有說明, 這裡只寫開跟關
>       ```shell
>       % node inspect test.js 
>       < Debugger listening on ws://127.0.0.1:9229/a776ebd1-ebdc-4935-9b1f-37078d8b0d43
>       < For help, see: https://nodejs.org/en/docs/inspector
>       < Debugger attached.
>       Break on start in test.js:1
>       > 1 let a = 100;
>         2 console.log(a);
>         3 
>       debug> .exit
>       ```
>   - 打日誌, console, alert...

> - Q. 預編譯?
>   - 前台預編譯
>     - 系統自帶
>     - polyfill
>   - 後台預編譯
>     - webpack



> #### Q. addEventListener 與 on 的區別
> - 首先 on 只能綁定一個事件, 而 addEventListener 可以綁定多個事件
> - 再來是因為 DOM2 事件(DOM Level 2)與 DOM3 事件(DOM Level 3) 的版本問題
>   - DOM 既是對象(Document), 同時也是個標準
>   - 早期事件都是 DOM1 & DOM2
>   - HTML5 的標準都是 DOM3 & DOM4
>   - 官方要求所有的 DOM3 事件 (HTML5) 都必須綁定, 
>     這也是為什麼有些人稱 DOM3 為 HTML5 事件
>   - 所以如果可以還是以 addEventLister 來綁定, 
>     否則哪天瀏覽器廠商們遵照官方要求, 那新版本用 on 如果失效, 可能會很麻煩
>     


> #### Q. blob
> - binary large object
> - 存放二進制數據的東西, 可以想像成一個大陣列, 每位是一個字節 `[Byte, Byte, Byte, ...]`

> #### Q. 推薦 發佈 NodeJS 的程序 的模塊
> - [forever](https://www.npmjs.com/package/forever)

> #### Q. 擴展名
> - 對電腦來說, 擴展名事實上沒有任何意義, 不可能音檔給flv就變影片
> - 所以有些人會選擇不加擴展名, 而是利用資料夾來分類
> 


> #### Q. AJAX 刷進去的頁面性能比刷新頁面高嗎?
> - 不會, 如果要改善性能, 最好的版法是後台渲染
> - SPA 或 AJAX 主要的作用在於用戶體驗較好(一直刷新很煩)
> - SPA 主要用於 APP


> `<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />`
> - 用 IE7 的兼容模式來顯示頁面




> #### Q. 社群演算法如何完成
> - 最簡單的就是擷取你搜尋的關鍵字來推薦
> - 複雜的就是利用機器學習
>   - 依據你使用的行為來加減權重
>     - 例如你把某個東西看完，我就加幾分
>     - 例如你看幾秒就關掉，我就減幾分
>     - 例如你按檢舉，那我就爆扣
>     - 例如你按收藏，那我就爆加
>   - 你在網路上的一切行為都是在幫他訓練屬於你的權重機器人
>   



> #### Q. vue-cli 構建的項目都是對 static 目錄下的資源引用(都是相對於服務器的根路徑)，如果我不想在這個路徑下寫，怎辦?
> - nginx/ apache 目錄映射
> - webpack 目錄映射



> #### Q. 學習查看文檔？
> - 接觸一個新的東西時，先看第一頁，看他幹嘛的，運行環境，運行限制等
> - 接著看案例，把東西搞出來可以用 ( 沒有案例就別看了 )
> - 然後再回頭細看每個東西的細節