宮原将太
    • 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
    # -04- 関数 ###### tags: `TypeScript` # 関数の宣言と呼び出し ## JavaScript関数の基礎 ### 関数の宣言方法及び呼び出し ```javascript= // 基本的な関数宣言のパターン function AAA() { console.log("関数"); }; const BBB = function() { console.log("関数式(関数リテラル)"); }; const CCC = () => { console.log("関数式(アロー関数)"); }; // 基本的な関数の呼び出しパターン AAA(); // console:関数 BBB(); // console:関数式(関数リテラル) CCC(); // console:関数式(アロー関数) ``` ### 間違った関数の宣言 :::warning 🐝 基本的には、関数に名前をつけてあげないと実行または宣言できない ::: ```javascript= // 名前をつけてない関数 function() { console.log("名前がついてない関数"); //Error:関数には何かしらの名前をつける必要がある }; ``` ただし、即時実行関数という関数宣言の場合は名前付けは不要である ```javascript= // 即時実行関数 (function () { console.log("即時実行関数"); })(); (() => { console.log("即時実行関数のアロー関数記法"); })(); ``` ### JavaScript関数の拡張性 :::info 🍀 JavaScriptでは関数は第一級のオブジェクト ::: 他のオブジェクトと全く同じように扱うことができ、変数に割り当てたり、他の関数の引数に渡したり、関数からreturnしたり、オブジェクトやプロトタイプに割り当てたりすることができる。 ```javascript= const funcA = (number, functionB) => { const stateC = functionB(number); return stateC; }; function funcB(n) { return n * n; }; const stateD = funcA(10, funcB); console.log(stateD); // 100 ``` ## TypeScriptの関数宣言における型定義 ### 基本的なTypeScript関数での型定義 関数のパラメーターに明示的にアノテーションすることができる 下記のadd()は「a, bというnumber型のパラメーターを受取り、関数の返り値はnumber型の値をreturnする」と型定義している ```typescript= function add(a: number, b: number): number { return a + b; }; add(10, 20); // 30 add(30, "文字列") // Error:引数には(a: number, b: number)が定義されている add(40); // Error:引数には2つの値が必要 ``` **引数とパラメーター** - パラメーター - 関数が実行されるために必要とするデータ - 関数宣言の一部として宣言される - 仮パラメーターと呼ばれる - 引数 - 関数と呼び出す時に渡すデータ - 実パラメーターと呼ばれる ```javascript= function add(a, b) { // aとbはパラメータ(仮パラメーター) return a + b; }; add(10, 20); // 10と20は引数(実パラメーター) ``` ## オプションパラメーターとデフォルトパラメーター オブジェクト型やタプル型と同様に「?」を使って、パラメーターを省略することができる 関数のパラメーターを宣言するときは、必須のパラメータを先に指定し、省略可能なパラメーターをその後に指定する ```typescript= const log = (message: string, userId?: string) => { console.log(message, userId || "Not Signed in"); }; log("デフォルトパラメーターを使う"); // console:"デフォルトパラメーターを使う Not Signed in" log("デフォルトパラメーターを使わない", "0018318"); // console:"デフォルトパラメーターを使わない 0018318" ``` **上記の関数を別の記述で書いてみる** パラメーターにデフォルト値を付与して、「?」を使わないで記述してみる ```typescript= const log = (message: string, userId = "Not Signed in") => { console.log(message, userId); }; log("デフォルトパラメーターを使わない"); // console:"デフォルトパラメーターを使わない Not Signed in" log("デフォルトパラメーターを使う", '0018318'); // console:"デフォルトパラメーターを使う 0018318" ``` :::warning 🐝 userIdにデフォルト値を与えると、オプションのアノテート「?」がなくなる ::: デフォルト値を持たない関数のパラメーターと同様に、型エイリアスの定義の時に明示的にデフォルトパラメーターのアノテーションを加えることができる ```typescript= type Context = { appId?: string userId?: string }; const log = (message: string, context: Context = {}) => { console.log("__result__"); }; ``` ## レストパラメーター 関数の引数に配列を受け取るとき、そのリストをパラメーターとして渡すことができる ```typescript= const sum = (numbers: number[]): number => { return numbers.reduce((total, i) => { return total + i; }, 0); }; sum([1, 3, 4, 6]); // 14 ``` 上記は配列を受け取るという関数なので、配列自体の長さが変わった場合には許容できる しかし、次の関数などで引数の合計を求めたい場合、パラメーターが3つに固定されているのでそれ以上それ以下の引数を受け取りたい場合にとても許容し辛い ```typescript= const sum = (a: number, b: number, c:number) => { } sum(1, 2, 4); ``` 場合によっては決まった引数をとる固定アリティ関数の代わりに、可変長引数関数を使いたい場合が出てくる そこで便利になるのが**レストパラメーター**である パラメーターに「...」をつけることで、そのパラメーターはレストパラメーターとして扱うことができる ```typescript= const sum = (...number: number[]): number => { return numbres.reduce((total, i) => total + n, 0); }; sum(1, 2, 4, ...); // いくつ引数を受け取ってもsum()は許容できる ``` :::warning 🐝 関数は最大で1つのパタメーターを持つことができ、そのパラメーターは関数のパラメータリストの中で最後に記述しないといけない ::: ## apply, call, bind 関数の呼び出し方法には複数の方法がある ```typescript= const add = (a: number, b: number) => { return a + b; }; // すべて同じ結果を返す add(10, 20); add.apply(null, [10, 20]); add.call(null, 10, 20); add.bid(null, 10, 20)(); ``` - apply - 値を関数内のthisにバインドし、2番目の引数のパラメーターとして展開する - 上記の例では、nullをthisにバインド - call - applyと同様のことをするが、引数を展開する代わりに順番に適用する - bind - this引数と、引数のリストを関数にバインドするという点は同じだが、bindは関数を呼び出さない 上記の呼び出し方法で、もしthisを使うとしたらこのようになる ```typescript= function functionCall(a: string, b: stirng) { console.log(a + this.name + b); }; const myName = { name: 'miyasan' }; // apply functionCall.apply(myName, ['はじめまして! ', 'です!']); // call functionCall.call(myName, 'はじめまして! ', 'です!'); // bind const newGreeting = functionCall.bind(myName); newGreeting('はじめまして! ', 'です!'); ``` JavaScriptはすべての関数に対してthis変数が定義される(他の言語ではあまりないらしい) :::warning 🐝 関数をどのように呼び出したかによって、thisは異なる値を持つ ::: これにより、コードが脆弱になり、理解しづらくなる場合がある このため多くのチームではクラスとメソッドの場合を除いて、関数を呼び出す全ての場所でthisを禁止してる場合がある thisが脆弱である理由は、メソッドの呼び出し時にドット「.」の左側にあるものの値を取るから ```javascript= const XXX = { a() { return this; // ②よってこのthisはXXXを指す }; }; // ①XXXがthisとなる const a = XXX.a(); // ③aはa()となり、a()の本体内のthisはXXXとなるためundefined console.log(a); // Error ``` もうちょっと深ぼり ```javascript= function fancyDate() { // ③thisはDateオブジェクトなのでgetMonth()などが呼び出せる return `${this.getMonth() + 1}/${this.getDate()}/${this.getFullYear()} ` } // ①call()で呼び出したので、callの第一引数がthisに割り当てられる // ②この呼び出しをした時、fancyDateがthisとなる fancyDate.call(new Date); // thisに割り当ててあげればとにかく呼び出し可能!! fancyDate(); // NG!! fancyDate(new Date); // NG!! fancyDate.apply(new Date); // OK!! fancyDate.bind(new Date)(); // OK!! ``` ### thisの型付け 基本的にthisは関数に隠れているので、明示的に型付けしてあげるとわかりやすくなる ```typescript= function fancyDate(this: Date, a: number, b: string) { // thisはDate型だとアノテーションする console.log(`${this.getMonth() + 1}/${this.getDate()}/${this.getFullYear()}` + a + b); } // call()の第一引数はthisに割り当てられる fancyDate.call(new Date, 10, "引数"); // OK!! ``` ### アロー関数のthisの扱い方 **アロー関数におけるthisの特徴** - アロー関数は自身でthisを持たない - アロー関数はレキシカルスコープのthisを参照する - スコープにthisがない場合、その一つ外側のスコープでthisを探す - アロー関数を呼び出す時、call, apply, bindを使った呼び出しは不適切 > レキシカルスコープとダイナミックスコープの違い > https://wemo.tech/904 :::warning 🐝 レキスカルスコープとは関数を定義した時点でその関数のスコープが決まる ::: :::danger 🚨 大前提としてJavaScriptのグローバルオブジェクトはwindowオブジェクトのことを指し、関数の外側で使われるthisはグローバルオブジェクト(windowオブジェクト)を参照する ::: ```javascript= // thisはグローバルオブジェクト console.log(this === window); // true // 関数(名前付き関数) function functionA() { // thisはfunctionA()の呼び出し元のオブジェクトを参照 return console.log(this.data); } // 関数式(関数リテラル) const functionB = function() { // thisはfunctionB()の呼び出し元のオブジェクトを参照 return console.log(this.data); }; // 関数式(アロー関数) const functionC = () => { // アロー関数で定義したこの時点で、functionC()の呼び出し元はwindowオブジェクトになる // functionC()内では、window === thisとなる return console.log(this.data); }; // this === window this.data = 'グローバルObjがthisだよ'; const Obj = { data: 'Objがthisだよ', funcA: functionA, funcB: functionB, funcC: functionC }; Obj.funcA(); // console:Objがthisだよ Obj.funcB(); // console:Objがthisだよ Obj.funcC(); // console:グローバルObjがthisだよ const Obj2 = { data: 'Obj2がthisだよ', funcAll: Obj, funcA: functionA, funcD: function() { const functionD = () => { // アロー関数で定義したこの時点で、functionD()の呼び出し元はObj2になる // functionD()内では、Obj2 === thisとなる return console.log(this.data); }; return functionD(); } }; Obj2.funcAll.funcA(); // console:Objがthisだよ Obj2.funcAll.funcB(); // console:Objがthisだよ Obj2.funcAll.funcC(); // console:グローバルObjがthisだよ Obj2.funcA(); // console:Obj2がthisだよ Obj2.funcD(); // console:Obj2がthisだよ ``` ## 呼び出しシグネチャ 次の関数の型を表す場合、呼び出しシグネチャという記法を使う ```typescript= const add = (a: number, b: number): number => { return a + b; }; // 上記のadd()を型で表現する // 呼び出しシグネチャ add: (a: number, b: number) => number // やってしまいがちなFunction型 add: Function ``` :::warning 🐝 Function型は、それが関数ということだけしか表現できず、どのようなパラメーターを受け取り、どんな戻り値を返しているのか表現できていない ::: ### 呼び出しシグネチャが表現できるパラメーター - プリミティブ型 - thisの型 - 戻り値の型 - レストパラメーター - オプションパラメーター :::danger 🚨 デフォルトパラメーターは指定不可(デフォルト値は型ではない) ::: ```typescript= type Log = (message: string, userId?: string) => void; // log()にのLog型をアノテーション const log: Log = (message, userId = 'Not Signed In') => { console.log(message, userId); }; log("みや", '01003'); ``` ## 文脈的型付け log関数は、宣言したと同時にLog型をアノテーションしていたので、log()のパラメーターをアノテーションする必要がなかったことに注目 これを**文脈的型付け**と呼ばれる、TypeScriptの強力的な機能 ### パラメーターだけでなく戻り値の型も文脈的型付けは行われる Log型はコールバック関数の戻り値が無いのでvoid型としてされており、log()は値をコールバックしないので(処理がconsole出力)、型定義として成立する もし仮にnumber型を引数に受け取り、number型を返す関数を定義すると ```typescript= // NumberCallback型は2つのnumber型を受け取り、number型をコールバックする type NumberCallback = (a: number, b: number) => number; const numberCallback: NumberCallback = (a, b) => { return a + b; // 戻り値がnumber型 }; const result = numberCallback(10, 20); console.log(result); // 30(typeof number) ``` ### 呼び出しシグネチャの完全記法と省略記法 ```typescript= // 完全記法 type Log = { (message: stirng, userid?: string): void; }; // ----- or ----- // 省略記法 type Log = (message: stirng, userid?: string) => void; const log: Log = (message, userId = 'Not Signed In') => { console.log(message, userId); }; ``` 今回のlog()のようなシンプルな関数であれば、省略記法の方が簡潔で見やすくなるが、もし型の中に複数の関数を定義する場合は完全記法が適するケースが多い ## オーバーロードされた関数の型 オーバーロードされた関数とは、**複数のシグネチャを持つ関数** :::info 🍀 引数の値を判断し、それに応じた戻り値を返すことのできる関数の型 ::: ここではDOM APIを例にオーバーロードされた関数を宣言してみる ```typescript= type CreateElement = { (tag: "a"): HTMLAnchorElement (tag: "div"): HTMLDivElement (tag: "table"): HTMLTableElement (tag: string): HTMElement } const createElement: CreateElement = (tag: string): HTMLElement => { return // ............. } } ``` 極端な例 ```typescript= type StringOrNumber = { (param: string): string; (param: number): number; }; const StringOrNumberFunc: StringOrNumber = (params: string | number): any => { return params; }; StringOrNumberFunc('みや'); StringOrNumberFunc(100); ``` # ポリモーフィズム ポリモーフィズムは**多様性**を取り入れた型定義の方法 > ポリモーフィズムについて > https://qiita.com/tatsumin0206/items/bbe05ccc765275382e1b 前の節で、オーバーライドされた関数の極端な例を書いたが、その関数はパラメーターを(string | number)と書いて、戻り値がanyと書いたりしてスマートと言えるコードではない これを便利にするのが**ジェネリック型パラメーター**になる ## ジェネリック型パラメーター 複数の場所で、型レベルの制約を制限するために使われるプレースホルダーの型 **多相型パラメーター**とも呼ばれる ```typescript= type StringOrNumber<T> = { (param: T): T; }; const StringOrNumberFunc: StringOrNumber<string | number> = params => { return params; }; const resultString = StringOrNumberFunc('みや'); // OK!! const resultNumber = StringOrNumberFunc(100); // OK!! ``` 呼び出しが行われるまでどんな型の引数を受け取るのかわからない場合、StringOrNumber型というジェネリック型パラメーターを型定義することで、あらゆる型の引数を受け取ることができる ## ジェネリックはいつバインドされるか? **AAA型にジェネリック型をバインドする場合** ```typescript= type AAA<T> = { (param: T): T; }; // 関数の宣言時点でstring型がバインドされる const funcA: AAA<string> = (params) => { return params; }; ``` AAA型をfuncA()にアノテーションする時、AAA型は何型を受け取るのかバインドしないといけない **BBB型のシグネチャにジェネリック型をバインドする場合** ```typescript= type BBB = { <T>(param: T): T; }; const funcB: BBB = (params) => { return params; }; funcB("aaaa"); // 関数の呼び出し時点で、string型がバインドされる ``` funcB()が実行される時、引数の型を推論して具体的な型をTにバインドする ## ジェネリックはどこで宣言できるか? **複数の宣言パターン** 1. 完全な呼び出しシグネチャ & 呼び出し時バインド ```typescript= type Filter = { <T>(array: T[], f: (item: T) => boolean): T[] } const filter: Filter = (array, f) => { // ...... } filter([1, 2, 3], item => item > 0); ``` 1. 完全な呼び出しシグネチャ & 宣言時バインド ```typescript= type Filter<T> = { (array: T[], f: (item: T) => boolean): T[]; } const filter: Filter<string> = (array, f) => { // ...... } ``` 1. 省略記法の呼び出しシグネチャ & 呼び出し時バインド ```typescript= type Filter = <T>(array: T[], f: (item: T) => boolean) => T[]; const filter: Filter = (array, f) => { // ...... } filter([1, 2, 3], item => item > 0); ``` 1. 省略記法の呼び出しシグネチャ & 宣言時バインド ```typescript= type Filter<T> = (array: T[], f: (item: T) => boolean) => T[]; const filter: Filter<string> = (array, f) => { // ...... } ``` 1. 呼び出しシグネチャを直接アノテーション & 宣言時バインド ```typescript= // 関数 function filter<T>(array: T[], f: (item: T) => boolean): T[] { // ...... } // 関数式 const filter = <T>(array: T[], f: (item: T) => boolean): T[] => { // ...... } ``` ## ジェネリックの型推論 JavaScriptの標準関数のmap()を例に説明する ```typescript= const map = <T, U>(array: T[], f: (item: T) => U): U[] => { let result = []; for (let i = 0; i < array.length; i++) { result[i] = f(array[i]); } return result; }; const array = [1, 53, 64] const fnA = (item) => item > 20; // booleanを返す const fnB = (item) => item; // // itemをそのまま返す // すべて自動的に推論してくれる map(array, fnA); // OK!! // もちろんアノテーションすることも可能 map<number, boolean>(array, fnA); // OK!! // アノテーションするならすべて定義しないといけない map<number>(array, fnA); // Error:2つの型引数が必要だよ!! // 間違った型をアノテーションできない map<number, string>(array, fnA); // Error:2つ目の引数はbooleanである必要があります // これもいけちゃう map<number, boolean | string>(array, fnA); // OK!! map<number, boolean | string>(array, fnB); // OK!! // これも map<number, string>(array, fnB); // OK!! // けどこれは無理 map<number, string>(array, fnA); // Error:fnA()はbooleanを返します ``` 通常TypeScriptは関数に渡される引数をもとに、ジェネリック型に対する具体的な型を推論するので次のようなケースに出くわすことがある 次の例は、**resolveが何型なのかTypeScriptが推論してくれない一例** そのためにpromiseオブジェクトのthenメソッドの引数にどんな型が帰ってくるのか不明な場合に起きる挙動である ```typescript= const promise = new Promise(resolve => { resolve(49); }); promise.then(result => { result * 2; // Error:resultはunknown型です }); ``` これを修正するにはPromiseのジェネリックパラメーターを明示的にアノテートする必要がある ```typescript= const promise = new Promise<number>(resolve => { resolve(49); }); promise.then(result => { result * 2; // OK!! }); ``` ## ジェネリック型エイリアス 型エイリアスにジェネリック型パラメーターを割り当てる そして関数の引数に応じて、TypeScriptに推論させることができる ```typescript= type MyInput<T> = { value: T; type: 'text' | 'number'; fn: (item: T) => T; }; const func = (item) => item; // ①string型を受け取るジェネリック型エイリアス function inputText<T>(params: MyInput<T>): T { const result = params.fn(params.value); return result; }; // valueの型を推論した後、すべてのTにstring型をバインドする inputText({ value: 'みや', type: 'text', fn: func }); // ②number型を受け取るジェネリック型エイリアス const inputNumber = <T>(params: MyInput<T>): T => { const result = params.fn(params.value); return result; }; // valueの型を推論した後、すべてのTにnumber型をバインドする inputNumber({ value: 1, type: 'number', fn: func }); ``` ## 制限付きポリモーフィズム ジェネリック型に**少なくともこの型(またはそれ以上に拡張した型)でないといけない**という制限を設けることができる ```typescript= // 人間型 type Human = { name: string; body: boolean; }; // 非人間型 type NoHuman = Human & { tail: boolean; }; // ケンタウロス型 type Centaur = NoHuman & { horse: boolean; }; // 狼人間型 type WolfMan = NoHuman & { wolf: boolean; }; // 変態可能 type Transformation = { transformation: true; }; const human: Human = { name: '人間', body: true }; const centaur: Centaur = { name: 'ケンタウロス', body: true, tail: true, horse: true }; const wolfman: WolfMan = { name: '狼人間', body: true, tail: true, wolf: true }; const transformationWolfman: WolfMan & Transformation = { name: '変態可能な狼人間', body: true, tail: true, wolf: true, transformation: true }; // ①<T>は人間型以上に拡張した型でないといけない const HumanCheck = <T extends Human>(body: T): void => { console.log(body.name); }; HumanCheck(human); // OK!! HumanCheck(centaur); // OK!! HumanCheck(wolfman); // OK!! // ②<T>は非人間型以上に拡張した型でないといけない const NoHumanCheck = <T extends NoHuman>(body: T): void => { console.log(body.name); }; NoHumanCheck(human); // Error:humanは非人間型以上に拡張してないよ NoHumanCheck(centaur); // OK!! NoHumanCheck(wolfman); // OK!! // ③<T>は変態可能な狼人間型でないといけない const WolfManCheck = <T extends WolfMan & Transformation>(body: T): void => { console.log(body.name); }; WolfManCheck(wolfman); // Error:wolfmanは変態不可の狼人間 WolfManCheck(transformationWolfman); // OK!! ``` ## ジェネリック型のデフォルト値 ジェネリック型にデフォルトパラメーターは付ける意味ない気がする # 型駆動開発 - TypeScriptでコードを書くときに、気が付くとコードが「型によって先導されている」ことがよくある - これを型駆動開発という :::info 🍀 シグネチャで関数の概略を記述し、その後で値を埋め込むプログラミングのやり方 ::: そのシグネチャを見ただけで、その関数がどんな処理をするかについてある程度は直観的に理解できるようになる

    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