Hsieh Ya Chu
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # JavaScript - Scope 作用域 & Closure 閉包 ## Scope 作用域 ### 什麼是作用域 > 「作用域就是一個變數的生存範圍,一旦出了這個範圍,就無法存取到這個變數」。 當我們把變數 `a` **宣告在 function 中**,function 之外的地方都無法取用這個變數: ```javascript // 把變數宣告在 function 中 function test1() { var a = "hello" } console.log(a) // Uncaught ReferenceError: a is not defined ``` 但當我們把變數 `b` **宣告在全域**,function 裡面也可以取用外面的變數: ```javascript // 把變數宣告在全域 var b = 123 function test2() { console.log(b) // 123 } test2() ``` 再看看這個例子: ```javascript // 宣告函式 function outer() { var x = 5 // 在函式裡面宣告另一個函式 function inner() { var y = 4 console.log(x+y) // 9 } inner() console.log(y) // Uncaught ReferenceError: y is not defined } // 呼叫函式 outer() ``` - `y` 不能被 `outer` 取用:`y` 不在 `outer` function 的作用域中因此無法被 `outer` 取用 - 但 `x` 可以被 `inner` 取用:`inner` 在自己的作用域中找不到 `x` ,便開始向外尋找,最後在 `outer` 的作用域中取用 `x` :::info 結論: - 外面存取不到裡面的,但內層可以存取到外層的 - 倘若一直向外查找到全域都找不到變數,就會出現的 `ReferenceError: variable is not defined` 錯誤訊息 ::: ### 靜態作用域 / 語法作用域 Javascript 的作用域是採用「靜態作用域(static scope)」,也稱作「語法作用域 (lexical scope)」(也有人翻成「詞法作用域」、「語彙作用域」)。「靜態作用域」是相對於「動態作用域(dynamic scope)」而來。 #### 靜態作用域 靜態作用域決定作用域的方式是根據**函式在哪裡被宣告**,與函式在哪裡被呼叫無關,查找變數的流程不會因為函式實際執行的位置不同而改變。 #### 動態作用域 動態作用域決定作用域的方式是以呼叫堆疊 (call stack) 為準,查找變數的流程是執行時才決定的。 來看看這個例子: ```javascript var name = "Peter"; // 全域變數 function init() { var name = "Amy"; // 局部變數 function displayName() { console.log(name); // "Peter" or "Amy"? } displayName(); } init(); ``` 答案是 Amy。 1. `init()` 函式在自己的作用域建立了局部變數 `name` 以及 `displayName()` 函式 2. `displayName()` 函式的作用域中沒有局部變數 `name` ,因此開始向外層尋找,先找到了 `init` 作用域的 `name` 變數 --> 因此答案是 Amy 再看看另一個例子: ```javascript var name = "Peter"; // 全域變數 function displayName() { console.log(name); // "Peter" or "Amy"? } function init() { var name = "Amy"; // 局部變數 displayName(); } init(); ``` 答案是 Peter。 - `displayName` 裡的 `name` 其實就是 global 的 `name`,跟 `init` 裡的 `name` 沒有關係。 - 這是因為 Javascript 是採用「靜態作用域」的關係, `displayName()` 是在全域中**被宣告**,即使是在 `init()` 中**被呼叫**,它的作用域仍與 `init()` 無關。(在某些採用「動態作用域」的程式語言中, `name` 確實有可能會是 `"Amy"`)。 :::info 這邊可以知道: - 作用域跟這個 function 在哪裡被「呼叫」一點關係都沒有 - 靜態作用域是在 function 被「宣告」的時候就決定了,而不是 function 被「執行」的時候 ::: ### 如何產生作用域 作用域有三種: - 全域 Global Scope - 函式作用域 Function Scope - 塊級作用域 Block Scope (ES6) ### 全域 Global Scope Javascript 執行的一開始就會創建一個全域環境,被定義在 function-scope 或是 block-scope 以外的變數就叫做全域變數 (global variable)。 - 不在函式或區塊內宣告的變數就是全域變數 - 可以在任何地方取用的變數 - **如果在定義變數時沒有加上宣告變數,即使在函式內也會成為全域變數**(應避免這種情形) ```javascript function hello() { name = 'Jack'; } hello(); // 先執行 hello() 才宣告了 name 這個變數 console.log(name); // Jack,即使變數是在函式中被定義,還是變成了全域變數 ``` ### 函式作用域 Function Scope 每建立一個函式就會創建一個新的函式作用域。 - 在函式中宣告的變數只能在函式(該作用域中)使用 - **不論是透過 `var`, `let`, `const` 宣告的變數,當他們在函式中宣告時都屬於函式作用域** ```javascript function foo(){ var num = 10; // function scope } console.log(num); // Uncaught ReferenceError: num is not defined ``` ### 塊級作用域 Block Scope 在 ES6 中引入了 `let` 與 `const` 變數,這兩個變數的宣告方式提供了「區塊作用域」。 - 區塊作用域的範圍只存在於大括號 `{}`(例如 if 或 for) - 區塊作用域與函式作用域相同,內部宣告的變數不能從外部取用 - **`var` 宣告的變數不會有區塊作用域,透過 `let` 與 `const` 宣告才能讓變數具有區塊作用域** ```javascript // 在 {} 中用 let 或 const 宣告的變數具有區塊作用域 function foo1() { if (true) { const user = "花爸"; // block scope } console.log(user); // Uncaught ReferenceError: user is not defined } foo1(); // 在 {} 中用 var 宣告的變數不會有區塊作用域 function foo2() { if (true) { var user = "花媽"; // function scope } console.log(user); //花媽 } foo2(); ``` 在上述的例子中, - `foo1()` 使用了 `const` 宣告變數,所以 if 的 `{}` 內為區塊作用域,`const user = "花爸"` 只存在於該 `{}` 區塊中。既便是同函式中的 `console.log(user)` 也無法取用 if `{}` 內宣告的變數。 - `foo2()` 使用了 `var` 宣告變數,不會有區塊作用域,`var user = "花媽"` 存在於 `foo2` 函式中。因此同函式中的 `console.log(user)` 可以取用該變數。 ### 作用域鏈 Scope Chain Javascript 在使用一個變數的時候,會先在當層的作用域尋找該變數。若當前的作用域找不到該變數,會再往父層作用域尋找,就這樣一層一層往上找,一直到全局作用域如果還是沒找到就會報錯。這種由內而外搜尋的行為就是「作用域鏈」。 > 事實上,每個函式在執行時都會建立一個對應的作用域鏈。(延伸閱讀:[前端中階:JS令人搞不懂的地方-Closure(閉包)](https://hugh-program-learning-diary-js.medium.com/%E5%89%8D%E7%AB%AF%E4%B8%AD%E9%9A%8E-js%E4%BB%A4%E4%BA%BA%E6%90%9E%E4%B8%8D%E6%87%82%E7%9A%84%E5%9C%B0%E6%96%B9-closure-%E9%96%89%E5%8C%85-cbb9c6a4185c)) ```javascript function outer() { var a = 10 function inner() { console.log(a) // 10 } inner() } outer() ``` 上述例子中,`inner` 函式在自己的作用域中找不到 `a` 變數,就會往上一層的 `outer` 作用域找,如果還是找不到就會再往上一層找直到最上層的 `global` 作用域為止,如果最後還是找不到就會報錯。 查找方向:【 inner function scope 】 -> 【 outer function scope 】 -> 【 global scope 】,這個過程就構成了一個作用域鏈。 對 `inner` 來說,`a` 變數並不存在於它的作用域中,但它仍能存取這個變數,這樣的變數也叫做「**自由變數 (free variable)**」。 ## Closure 閉包 ### 什麼是 closure 根據 [MDN 的敘述](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Closures): > 閉包(Closure)是函式以及該函式被宣告時所在的作用域環境(lexical environment)的組合。 ```javascript const outer = () => { const greet = 'Hi' const inner = () => { console.log(greet) // 'Hi' } inner() } outer() ``` 當我們呼叫 `outer` 時,就只是執行內部的一個 function `inner` 而已。 但如果我們不直接執行 `inner` ,而是直接把這個函式回傳: ```javascript const outer = () => { const greet = 'Hi' const inner = () => { console.log(greet) // 'Hi' } return inner } const newFunc = outer() // 建立了 newFunc 實例(instance) newFunc() ``` 原本當函式執行完成以後,裡面宣告的變數也應該要被釋放,因此當 `outer()` 執行結束時,照理來說變數 `greet` 的記憶體空間會被釋放,但緊接著在呼叫 `newFunc` 時卻仍能存取到 `greet`。 換句話說只要 `newFunc` 還在, `greet` 就依然存在。這種**在 function 裡回傳了一個 function** 導致明明函式執行完畢卻還能存取到資源的現象就是「閉包」。 我們首先在 `outer` 函式中: 1. 宣告了一個變數 `greet` 2. 宣告一個 `inner` 函式,`inner` 函式會印出 `greet` 3. 回傳 `inner` 函式 接著把 `outer` 函式指派給 `newFunc` 變數,此時: - `newFunc` 是 `inner` 在 `outer` 運行時所建立的實例 (instance) - `newFunc` 存取了 `outer` 回傳的值(也就是 `inner` 函式) - 由於 `inner` 的 instance 中保有了原本作用域的環境參照,而作用域裡含有 `greet` 變數,因此調用 `newFunc()` 時 `greet` 也能被取用 ### Closure 的特性 - 在 JavaScript 中每當函式被建立時,一個閉包就會被產生 - 當一個 function 內 return 了另一個 function,通常就是有用到閉包的概念 - 閉包是資料結構的一種,當一個函式被宣告時,函式對其語法環境 (lexical environment) 的引用在一起的組合就是閉包(可以想像成函式記住了宣告時的作用域環境) - 閉包就是可以回傳一個 function,並且使用父層 function 的變數 - 閉包可以讓函式內部的變數不受外部環境影響 - 閉包內部可以透過作用鏈獲取外部資料,內層作用域(child scope)永遠可以取得外層作用域(parent scope)的資料,反過來則不行 ### Closure 的好處 #### 節省寫入記憶體的次數 可以把程式中需要重複執行的部分透過閉包封裝起來,進一步簡化程式,或是讓變數的值給保存下來。 1. 把想要回傳的東西包到 function 裡再回傳 2. 再把 function assign 給變數 3. 讓變數去存取 function 回傳的值 假設今天這個函式需要創造一個很大的 array,並且這個函式會被呼叫很多次: ```javascript function heavyDuty(index) { const bigArray = new Array(7000).fill('something') console.log('created!') return bigArray[index] } heavyDuty(688) // created! heavyDuty(688) // created! heavyDuty(688) // created! ``` 每執行一次函式都需要重複產生一個 7000 個 index 的 array,太浪費資源了,這時就很適合使用 closure: ```javascript function heavyDutyClosure() { const bigArray = new Array(7000).fill(':)') console.log('created!') return function(index) { // 簡化寫法,直接 return function return bigArray[index] } } const getHeavyDuty = heavyDutyClosure() getHeavyDuty(688) // created! getHeavyDuty(688) getHeavyDuty(688) ``` 1. 把想要回傳的東西包到 function 裡再回傳 2. 把函式 `heavyDutyClosure` assign 給變數 `getHeavyDuty`,讓 `getHeavyDuty` 去存取 `heavyDutyClosure` 所回傳的值(也就是函式 `function(index)...`) 3. 執行 `getHeavyDuty` 時,就是在呼叫函式 `function(index)...` 值得注意的是,雖然呼叫了三次 `getHeavyDuty` 但用 closure 寫法的版本只會在第一次印出 `created!` 執行第一次 `getHeavyDuty` 時的運作流程: ``` 呼叫 getHeavyDuty -> 呼叫 heavyDutyClosure -> 創造 bigArray 並印出 created! -> 回傳函數 -> 執行回傳的函數 -> 回傳 bigArray[688] ``` 執行第一次後 `function(index)...` 就已經被存取 `getHeavyDuty` 這個變數了。後續在執行 `getHeavyDuty` 就是在執行 `function(index)...` 了。而 `heavyDutyClosure` 中沒有被 return 的部分(產生 7000 個 array、印出 created!)就只會執行一次。 接下來幾次執行 `getHeavyDuty` 的運作流程其實只剩: ``` 呼叫 getHeavyDuty -> 呼叫函數 function(index)… -> 回傳 bigArray[688] ``` #### 封裝(Encapsulation) 變數 (private variable) 封裝是物件導向程式設計(Object Oreinted Programming,簡稱 OOP) 一個非常重要的概念。 有些資料如果不想讓外部函式/方法改動它,就可以使用閉包的方式來確保只有內部函式可以改動內部資料。 ```javascript let count = 0 function increment(num) { return count += num } function decrement(num) { return count -= num } function getCount() { return count } increment(3) increment(3) increment(1) getCount() // 7 ``` 這裡直接在全域宣告 `count` 變數是錯誤的,可能會導致程式碼出現不可預期的錯誤。比如要是有人在其他地方寫了 `count = 5` 資料就亂掉了。 這種情況就可以使用閉包,寫一個 function 把 `count` 這個變數和能夠改變這個變數的 function 封裝在一起: ```javascript function createCounter (initCount) { let count = initCount return { increment: (num) => count += num, decrement: (num) => count -= num, getCount: () => count } } const counter = createCounter(0) counter.increment(3) // 3 counter.increment(3) // 6 counter.increment(1) // 7 counter.getCount() // 7 count = 5 // ReferenceError: count is not defined ``` 這樣一來除了 `.increment()` 、`.decrement()` 、`.getCount()` 之外的方法都無法改變 `count` 這個變數。 - `count` 是一個 **private variable** - `count` 只存在於 `createCounter` 作用域中 - `count` 只能被內部的函式改動資料,不會被外部環境所影響 - 能夠存取 private variable 的方法被稱作 **privileged method** #### 記憶體回收 (Garbage Collection) ## 作用域陷阱&閉包應用 ```javascript var btns = document.querySelectorAll('button') for(var i=0; i<btns.length; i++){ btns[i].addEventListener('click', function(){ alert(i+1) }) } ``` 假設頁面上有 3 個按鈕,預期的行為是點擊第一個按鈕跳出 1 、點擊第二個按鈕跳出 2 ⋯以此類推。但實際操作後會發現,無論點擊哪一個按鈕都會印出 3。 原本想像中的迴圈應該是要這樣跑的: ```javascript btn[0].addEventListener('click', function(){ alert(1) }) btn[1].addEventListener('click', function(){ alert(2) }) btn[2].addEventListener('click', function(){ alert(3) }) ``` 但實際上 JS 引擎在運作時是這樣跑的: ```javascript btn[0].addEventListener('click', function(){ alert(i+1) }) btn[1].addEventListener('click', function(){ alert(i+1) }) btn[2].addEventListener('click', function(){ alert(i+1) }) ``` 在跑迴圈的時候只是加上它的 callback function 而已還沒有執行,是要等到使用者按按鈕的時候才會去尋找 `i` 變數。 也就是說,事件發生時(使用者點擊按鈕)所引發的函式 (callback function) 是在迴圈跑完之後才執行。 加上這幾個 callback function 本身並沒有 `i` 這個變數,因此會向作用域的外層開始尋找,一直找到在迴圈中定義的 `i` (定義在全域中)作為其值。 而此時的 `i` 早就已經變成了 2 了,因此無論點擊哪一個按鈕,取用的都是同一個全域變數中的 `i`,所以都只會輸出 3。 這個問題可以透過閉包來解決: **建立一個新的函式並傳入參數:** ```javascript function getAlert(num) { return function() { alert(num) } } for(var i=0; i<btn.length ; i++) { btn[i].addEventListener('click', getAlert(i)) } ``` 透過高階函式 (Higher Order Function) 產生三個新的 function,並且因為傳入了一個參數 `i`,利用閉包的特性將 `i` 個別鎖進 `getAlert` 中。 **或者也可以利用 IIFE(Immediately Invoked Function Expression):** ```javascript for(var i=0; i<btn.length ; i++) { (function(num) { btn[i].addEventListener('click', function(){ alert(num) }) })(i) } ``` 透過 IIFE 把 function 包起來並傳入參數 `i` 立即執行。 迴圈每跑一次就會產生一個新的 function 並且立刻呼叫它,因此會就地產生新的作用域,並且利用了閉包的特性將參數 `i` 鎖住。 **也可以直接把 `var` 改成 `let` (ES6)**: ```javascript for(let i=0; i<btn.length ; i++){ btn[i].addEventListener('click', function() { alert(i) }) } ``` ES6 裡有了塊級作用域 (block scope) 之後就可以直接利用 `let` 的特性,等於每跑一次迴圈都會產生一個新的作用域,因此 `i` 就會存在在該作用域裡。可以看成這樣: ```javascript { // 塊級作用域 let i=0 btn[i].addEventListener('click', function() { alert(i) }) } { // 塊級作用域 let i=1 btn[i].addEventListener('click', function() { alert(i) }) } ... ``` ## Ref - [Javascript 的作用域 (Scope) 與作用域鏈 (Scope Chain) 是什麼?](https://www.explainthis.io/zh-hant/interview-guides/javascript/what-is-scope-and-scope-chain) - [[筆記]-JavaScript 作用域與作用域鏈是什麼?關於作用域的4個觀念](https://jianline.com/javascript-scope-and-scope-chain/#%E5%85%A8%E5%9F%9F%E4%BD%9C%E7%94%A8%E5%9F%9FGlobal_Scope) - [[JS基礎] 什麼是閉包(Closure)?](https://johnnychang25678.medium.com/js%E5%9F%BA%E7%A4%8E-%E4%BB%80%E9%BA%BC%E6%98%AF%E9%96%89%E5%8C%85-closure-61bcc1eb02ca) - [[JS] 深入淺出 JavaScript 閉包(closure)](https://pjchender.dev/javascript/js-closure/) - [所有的函式都是閉包:談 JS 中的作用域與 Closure](https://github.com/aszx87410/blog/issues/35) - [前端中階:JS令人搞不懂的地方-Closure(閉包)](https://hugh-program-learning-diary-js.medium.com/%E5%89%8D%E7%AB%AF%E4%B8%AD%E9%9A%8E-js%E4%BB%A4%E4%BA%BA%E6%90%9E%E4%B8%8D%E6%87%82%E7%9A%84%E5%9C%B0%E6%96%B9-closure-%E9%96%89%E5%8C%85-cbb9c6a4185c) - [閉包包什麼?探索JS中的作用域與Closure | Javascript鍛鍊日記](https://medium.com/%E7%8B%97%E5%A5%B4%E5%B7%A5%E7%A8%8B%E5%B8%AB/%E9%96%89%E5%8C%85%E5%8C%85%E4%BB%80%E9%BA%BC-%E6%8E%A2%E7%B4%A2js%E4%B8%AD%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F%E8%88%87closure-javascript%E9%8D%9B%E9%8D%8A%E6%97%A5%E8%A8%98-f7b1a2ac1e2a)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully