洪毓昇
    • 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
      • Invitee
    • 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
    • Engagement control
    • 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 Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control 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
Invitee
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
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- title: 物件導向「超」入門-簡報版 tags: YunNET slideOptions: transition: 'slide' --- # <font style="color:#00FFFF">物件導向「超」入門</font> *資管系ㄉ惡夢* *YuSheng* --- ## [JS ㄉ this](/@happy123/JSthis#/) ---- ## 延伸閱讀 ### 最好回家ㄉ時候慢慢讀 ##### [該來理解 JavaScript 的原型鍊了](https://github.com/aszx87410/blog/issues/18) ##### [淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂](https://blog.techbridge.cc/2019/02/23/javascript-this/) ##### [this 的值到底是什麼?一次說清楚](https://zhuanlan.zhihu.com/p/23804247) ##### [看完上面後 強烈 推薦 必看!!!!](http://www.objectplayground.com/) ##### [JS & DOM](/@YunNet21st/rk8vREY2B#/) --- ## 物件導向入門之概念篇 ---- ![](https://i.imgur.com/6lhcRhD.png) NOTE:首先我們先來講為什麼要物件導向呢 其實大家一般寫程式的方式也是可以 你可以不斷的寫長長的程式 然後定義很多個函數 很多個變數 那你就會發生一個情況就是你的程式到處散落 假設你在寫一個大型的遊戲 你可能有玩家的物件 然後你有敵人的物件 這些物件的屬性 雖然你可以把它包成一包 但是你有非常多的函數 這些函數有可能是主角攻擊阿 或是怎麼樣改變分數 然後或是我輸出敵人的血量等等這些動作 那當這些函數沒有辦法 跟我們剛剛所說到的資料整合在一起的時候 它們就會全部散亂在你的畫面跟程式裡面 所以當你合作溝通的時候 會變得非常麻煩 ---- ![](https://i.imgur.com/9n1mEQO.png) NOTE:你剛寫完時看你自己的程式 會看起來 好像還看得懂 然後很有規則 但是別人看你的程式就會像一團亂七八糟的義大利麵 或是你兩個月回來看自己寫的程式 如果你沒有物件導向的概念 你沒有把資料跟方法綁定在一起 你會非常的難管理自己的開發 ---- ## 什麼是物件導向? > 在控制程式的時候 > 以物件為「概念」來包裝 NOTE:所以我們先來問一件事情 什麼是物件導向 物件導向其實就是我們在控制程式的時候 我們用物件為概念 來包裝我們所有的邏輯跟操作 ---- ![](https://i.imgur.com/OpEwvVW.png) NOTE:所以我們要分為三個步驟 第一個步驟 把情境描述為物件 那接下來就是描述物件的屬性跟行為 然後接下來就是操作物件 讓它們彼此互動 ---- ## 把問題描述為物件 ![](https://i.imgur.com/evXVsBW.png) NOTE:我們現在舉一個很簡單的例子 這個例子是 我今天 A 這個藍色的人 要給 B 一根香蕉 我們如果要把問題描述為物件 我們就可以看出這個情境裡面 有兩個人在跟一根香蕉做互動 所以我們知道物件有人跟香蕉 ---- ## 描述物件的屬性跟⾏為 ![](https://i.imgur.com/Xq3gNjr.png) NOTE:那接下來呢 我們要來描述物件的屬性跟行為 我們可以看到 人有很多基礎的屬性阿 比如說名字 年齡 我現在有什麼物品 然後人也可以有很多的動作 像是打招呼 走路 或接受物品 或送出別人物品這些動作 那接下來香蕉也可以有很多屬性 比如說它的名字價格味道 或是香蕉可以被剝皮 或是攻擊別人 那這樣子我們定義完物件之間的動作之後 我們就可以去呼叫這些動作 或者讓物件呼叫彼此的動作 ---- ## 操作與讓物件彼此互動 ![](https://i.imgur.com/ZuLI8IW.png) NOTE:如果要達成讓 A 這個藍色的人 送給 B 一根香蕉要怎麼做呢 首先我們先要知道 A 跟 B 他都屬於人這個概念 所以我們可以定義人類這個類別 人類有很多事情 有很多屬性嘛 但是我們要有一個實體 那現在實體的案例就是 A 跟 B 它們兩個都屬於人這個類別 那接下來 A 這個人有一根香蕉 香蕉也是一個實體的物品 那接下來我們要做的事情就是 我們去呼叫物件上面的動作 或是去存取它的屬性 假設 A.打招呼 然後 A 就會說話 然後 A.給予香蕉 這個給予香蕉的動作 我們可以給它一個參數 那我們就傳給它 B 所以 A 就把香蕉給 B 那接下來 B 去呼叫香蕉.剝皮 因為它想要吃它 然後最後再用 B.吃(香蕉) 來讓 B 把香蕉吃掉 所以你可以注意到整個 現在描述的情境裡面 幾乎沒有出現程式碼 或是我沒有去敘述底層的說 打招呼裡面到底在做什麼 它是要輸出 hello 我的名字嗎 或是什麼之類的 或是我沒有描述說 B.吃(香蕉) 它裡面可能 它把它吃了一口 然後它去研究說香蕉是什麼味道 或是描述香蕉怎麼樣 我只把所有的動作 包裝在吃香蕉這個抽象的概念裡面 然後去呼叫 B 的行為 那你可以注意到在這件事情裡面 我們所有的概念 它都被包裝為一層物件來做互動 所以我們並不會感受到說 如果要完成一件打招呼這件事情 我們就要寫好幾行說 先定義它要講什麼事情 然後我去輸出這一串文字 而是我可以去呼叫這個類別 這個人上面類別預設的行為 那像你一般打招呼 你可能是說 hello 我是誰誰誰 那我就定義人這個類別 在打招呼的時候 它就是輸出 hello 我是 然後輸出人這個本身的名字 ---- ![](https://i.imgur.com/78wzqkr.png) NOTE:所以你可以注意到在這個動作裡面 總共做了兩件事情 第一個事情是根據類別產生物件 也就是說我們先定義了人這個類別 可以做什麼 以及它有什麼屬性 然後接下來以人為基礎產生了 A 跟 B 這兩個實體的人 然後我們就稱為兩個物件 所以抽象的部分是類別 實體產生出來的東西是物件 而物件是根據我們給它的初始資料 做出來的 但它具有我們類別裡面 所有相關的屬性跟可以執行的動作 那接下來我們就去做操作物件的動作 不管是我們去直接呼叫 A 跟 B 的屬性 或方法 或是我可以讓物件呼叫彼此的動作 那像剛剛的例子裡面 我們定義了人的類別 產生了 A 跟 B 然後也讓 A 跟 B 彼此呼叫像打招呼 或者讓 A 呼叫香蕉的剝皮等等 ---- ## 案例:票務系統 ![](https://i.imgur.com/nAbRRHS.png) NOTE:那我們來講一個實體一點的案例 剛剛大家可能會覺得有點抽象 假設我今天在賣演唱會的票 那這張演唱會的票 實際上可能會有它是什麼票種 然後它的價格是多少 折扣是多少 手續費是多少 然後接下來它的方法 也就是這個物件的操作方法 我可能需要取得它的售價 要用它的總價乘以折價來算 那或者我會需要購買 購買可能就是把它的所有人填上你的名字 那退票後續可能會有一卡車的手續 但是我知道它用退票這個方法可以呼叫 那或者我列印狀態 把這張票券的類似綜合資訊列印出來 ---- ## 案例:票務系統 ![](https://i.imgur.com/UYIinzi.png) NOTE:我們想要一下我們之前會怎麼做 之前我們可能會定義票務 那票務的話 我們有票的價錢 票的種類 票的 discount 票的折價 以及票的 fee 它的手續費 所以我們會注意到在這個情況裡面 舊的方法我會去定義 ticket 開頭的一堆變數 然後我們去說 有個 function 是 get_ticket_price 我們把四個變數傳進去 讓它計算最後的結果給我 或者 get_ticket_description 去有點像綜合敘述這一個 那我去輸出一個 根據這些資料算出來的一串文字 那接下來我們之前也有講到說可以把 ticket 包成一個物件 所以你可以注意到阿 前面這些是它的屬性 然後以及下面這些是它的方法 一個物件有屬性跟方法組成 ---- ## 案例:票務系統 ![](https://i.imgur.com/jj5C9cg.png) NOTE:那接下來第二階段 我們之前學過物件的打包 所以現在我們把 price type discount 跟 fee 都包在 ticket 這個物件裡面 那接下來我們就可以直接在方法的部分 把 ticket 傳進去 所以我們就不會像剛剛一樣 分了好多個變數 我們還要去每次都要記得要傳哪些 我就把這個物件傳進去 而裡面這個方法就自動呼叫 A 這個物件上面的價格 折價等等來做計算 這個是第二階 那第三階也就是我們剛剛說的 我們要做到物件導向 所以起碼做的事情是什麼 我們需要把 function 也就是方法 跟這一個資料這一包整理在一起 所以我們必須要產生一個物件 它裡面有屬性 有這些資料 也有方法可以呼叫 有這些 function 可以呼叫 那這個 ticket 我們最理想最理想的情況就是 有沒有辦法讓它直接吃到本身的屬性 所以我就不用再給它任何的參數給它計算 我直接讓它呼叫自己的參數就好 ---- ![](https://i.imgur.com/NX2KWO9.png) NOTE:所以我們看一下怎麼抽象形容這個問題 我們希望有一個票券的物件 那這個票券的物件我們會事先定義說 物件有哪些屬性 像剛剛的價格 接下來我們定義說這個物件上面有哪些可用方法 那我們會希望這個方法 可以去呼叫自己想要的屬性來做計算 或者做其他的事情 那接下來第三個 怎麼樣去產生這個物件 所以這一個動作就叫做定義類別 ---- ![](https://i.imgur.com/G9szKqI.png) NOTE:那我們根據類別來產生物件的時候 它的基礎語法是這樣子 我們 var 一個 Ticket 那這個 Ticket 通常習慣第一個字大寫 來當作物件的類別 這就是抽象的概念 那接下來我們等於一個 function 那我們可以傳初始值進去給它 那接下來這個 function 執行的時候 它就會回傳自己 而這個自己就是我們剛剛所說的 有屬性 有方法的物件 那接下來我們可以用 this.屬性 來設定這個物件上面的初始值 那以及 this.方法 來設定它可以執行的方法 那在這個方法裡面呢 我們一樣可以使用 this.屬性 this.方法 來存取自己的屬性 或是它可以做的事情 雖然這樣講有點抽象 ---- ## JS中的函數打包 - 類別定義 ![](https://i.imgur.com/WsOWxwQ.png) NOTE:我們直接看一個實體的程式案例 在 js 裡面函數打包的類別定義 這邊我們已經做了一個完整的 Ticket 物件 那產生這個票券物件的時候 我們這個 Ticket 是類別 我們會等於一個 function 有點像產生器去產生物件 那產生物件的時候 我必須要給它的是它的價格以及它的折價 那接下來產生價格 有了價格跟折價之後 我在初始化的時候 也就是設定這個物件的時候 我們會設定 this.price this.type 一些它上面應該有的屬性 我們屬性可以直接指定給 現在傳進來初始的價格以及折價 那接下來我們去用 this.get_price 跟這個定義說這個物件可以執行什麼動作 那這個動作裡面呢 假設我們 get_price 我們是要去算說它的總價 我們就可以直接存 this. 總價乘以它的折價 再加上這個物件上面的手續費 所以我們就達成了說 把方法 跟數值整合在一起這件事情 ---- ## JS中的函數打包 - 產⽣物件 ![](https://i.imgur.com/gW9ODlA.png) NOTE:那接下來怎麼樣產生一個物件呢 就像剛剛人這個類別 我們需要產生 A 跟 B 這兩個實體的人 那它們才能做互動 所以我們剛剛用了 function 來定義物件嘛 接下來如果我們要產生一個實體的物件 我就 var 一個 myticket 這一張票會等於 新的 Ticket 我們用新的類別來把它定義出來 所以它意思其實就是 我執行了 Ticket 這個函數 然後傳進去我們初始值 然後 new 一個 所以我們現在最後得到的就會是 一個新的物件 然後它具有 ticket 上面所有的屬性 但是它現在在 ticket 的 price 跟 discount 部分 填的是我傳給它的初始值 ---- ## JS中的函數打包 - 操作物件 ![](https://i.imgur.com/Lxaip3O.png) NOTE:那接下來我們就可以讓這個物件做互動了 比如說我們讓 myTicket,price 我們就會得到 500 myTicket.discount 就會得到 0.5 然後 fee 之類的也是 那接下來我們可以呼叫這一個 myTicket 實體出來的物件上面的方法 比如說我們 .get_price 它就會回傳 530 然後我們 .get_description 就會回傳我們剛剛那一串文字 或者說我上面有定義如何退票的話 我可以直接傳給它我需要退幾月幾號的票 然後會拿回多少的錢 那這樣子的話 我就不用再去碰這個物件裡面複雜的邏輯 所以這樣子呢 我就可以輕易地達成說 如果這個物件是由其他人建立的 我可以不用管退票這件事情裡面怎麼做 不管它有沒有呼叫伺服器 然後有沒有去計算它應該退回多少金額等等 我只要傳給它 它需要的東西 比如說它到底要退哪張票 然後它就會傳給我一些 比如說退票的結果 ---- ![](https://i.imgur.com/7LE70Ck.png) NOTE:所以我現在就有一個票券物件 可以完整的操作 那我們這個動作就稱為封裝 那我們就不用再碰裡面的邏輯 那這樣子不僅很方便維護 而剛剛那樣子的寫法 比如說我們呼叫物件上面的動作或屬性 也很好的 很直觀的能夠理解 --- ## 物件導向入門之繼承篇 ---- ![](https://i.imgur.com/hPtIpTu.png) NOTE:物件導向入門的繼承篇 在前一個單元裡面我們有提到 如果我們今天把方法以及屬性 包在同一個物件裡面 我們就可以通過物件的互動 來寫更抽象簡潔的程式 那這邊會有個小小的問題 因為我們的方法是在物件產生的時候 加在這個物件上的 即使我們可以使用這些方法 ---- ![](https://i.imgur.com/W6Ph0m2.png) NOTE:但是假設我們今天用 people 人類這個類別產生了三個人 叫做 Mark Marry 跟 John 那比如說我有一個方法叫 say hello 那它會輸出 Hello I'm 誰誰誰 那在這個問題就會存在說 因為我們是在產生物件的時候 放在上面的函數 所以這些函數都是獨立存在於物件內的 也就是這些黃色的部分 那當我們 比如說我們想要統一改動 人類打招呼的方式 從 Hello I'm Marry 變成 Hi I'm Marry 的時候呢 我們就必須要一個一個改物件 這樣子其實就會有點麻煩 所以我們的問題是 函數存在於產生的物件內 而無法共用 ---- ![](https://i.imgur.com/zKMHuAr.png) NOTE:所以如果我們想要共用函數的話呢 我們必須要先使用 prototype prototype 是什麼呢 它其實是 js 中物件導向的一種實現方式 那也就是說我們不在裡面說 this.方法 等於什麼 因為這些方法都一樣 我們不希望重複的放在產生的物件內 那接下來我們就會希望說 如果我們想要呼叫某些特定的方法 就可以定義在 Ticket.prototype 的上面 定義在 prototype 的上面的時候 原型它不會被複製進去物件 所以它不會產生很多份放在新產生的物件裡面 而是物件它會有個參考 連結到這個原型 在找不到屬性的時候 就會來這個 prototype 上面看看有沒有 那我們來看一下圖解的部分 ---- ![](https://i.imgur.com/vSXVXiK.png) NOTE:我們剛剛所新增的 prototype 其實就是一個共同參照用的原型 那這個參照用的原型它會獨立存在在外面 那比如說當我們根據人類的類別 產生了這三個人之後呢 裡面並不會有這些原型的 function 比如說 say hello 我們就不會放在這個地方 而是它會有個連結連到 prototype 所以比如說我們 Mark.sayHello 的時候 它就會說 Mark 上面沒有 sayHello 耶 怎麼辦 它去找它的 prototype 上面有沒有 然後它就找到 sayHello 那這個 prototype 裡面 你一樣可以使用 this. 誰誰誰的屬性 和 this. 方法 也都呼叫的到這個物件上面的東西 所以我們就達到了 可以統一管理這些函數的目的了 那既然我們可以統一管理這些函數 ---- ![](https://i.imgur.com/BuXB1pu.png) NOTE:我們要怎麼樣去看更進階的物件導向呢 剛剛其實我們之前的單元都在講說 怎麼樣讓物件有自己的屬性 跟方法 然後封裝在一個物件裡面 但是我們接下來要講的是繼承 繼承其實有點像 prototype 的概念 它是去延伸概念與物件 比如說我想要把生物延伸成植物與動物 因為植物跟動物都是生物 ---- ![](https://i.imgur.com/jewMwVc.png) NOTE:所以我們現在要做的事情是 如果我可以把類別延伸成新的類別 會有什麼優勢呢 第一就是減少重複地程式 我們可以用抽象的概念做進一步的延伸 比如說生物延伸成人或狗 因為人或狗都是生物 可能都有它生命的長度阿 今年幾歲阿 或是它會吃東西等等 那這些我們就不應該重複的寫在人跟狗裡面 所以我們應該怎樣去做繼承呢 ---- ![](https://i.imgur.com/uieLXEL.png) NOTE:比如說我今天生物體 有名字 生命長度這兩個屬性 還有移動跟吃東西的方法 假如我想要定義一個狗怎麼辦 你可以想像啦 這個繼承它有點像包一層殼在外面 所以我基於生物體這個類別 我再包一層狗 然後狗的話有自己的品種 喜歡吃的東西 那也會有一些狗有的動作 像吠叫或攻擊 但是我今天使用這整包狗的類別的時候呢 它也會包含我繼承來生物體的名字跟生物長度 我就不需要重複定義名字跟生命長度 這些基礎的屬性了 而這樣子繼承有什麼好處呢 我們可以把這個生物體拿去延伸成其他類別 比如說我今天要新增一個龍的類別 那龍也一樣屬於生物 有自己的名字跟生命長度等等的 但是龍會飛 龍會飛翔也會生蛋 然後有自己的火焰強度跟和善程度 所以我們就基於相同的 生物體的這個高層級的類別 延伸成兩個比較低層級的狗跟龍 分別有自己的屬性了 ---- ![](https://i.imgur.com/d5nBu6T.png) NOTE:你可能會問我要怎麼 用在程式跟遊戲裡面呢 你可以注意到 我今天在玩遊戲的時候啊 每一個遊戲角色它都會有共用的生命值 跟位置這兩個東西 那這些角色有生命值跟位置之後呢 我們去繼承這個角色 延伸成三種類型 NPC 玩家跟敵人 那 NPC 可能有一些自動控制的方法阿 玩家有一些物品欄位 然後敵人有掉寶機率 所以這也就是繼承了角色延伸成敵人 那敵人也可以使用角色裡面所有屬性跟方法 接下來你也可以做第二層的繼承 你可以把一般的魔物 跟魔王這兩種敵人拆出來 那一般的魔物我們會指定它的出現區域 然後魔王的話會有一些特殊技能 或特殊的屬性設定 所以這樣子就可以幫助我們很有效的去管理 我們裡面的物件模型 而避免說我每個物件裡面有重複的部分 比如說我每個都重複寫了生命值跟位置 而導致我的程式變的很肥大 ---- ![](https://i.imgur.com/vuS2ArT.png) NOTE:那在 js 裡面怎樣去繼承呢 這邊大家要先搞清楚 我們說的繼承其實都是類別跟類別之間的繼承 那這樣子的話呢 我們剛剛講的 new function 先定義完類別 然後再 new 出來產生一個實體 比如說這張圖的動作 我根據狗的類別 new 出來產生一個 poppy 這隻小狗 那今天我們講的繼承其實就是在上面 我們在定義這個類別的時候呢 我們再額外指定它的母類別是什麼 比如說我們讓狗繼承生物 那在 js 裡面 它的繼承方式就會是有點像連結 所以說我今天根據這個狗 有繼承生物產生出來的這個 poppy 這個實體的物件的話 假設我呼叫吃東西這個方法 那就會找找找找找 找到 狗裡面沒有吃東西啊 那如果說你有指定繼承的時候 它就再繼續往上找 找到上游的原型 那這個原型呢 裡面如果你有定義吃東西 它就會執行 這個好處就會是說 假設你想要 複寫掉這個方式的時候會很方便 比如說狗雖然也有吃東西 但它吃東西的方法可能比較不一樣 只有狗專屬的吃東西的方式 那我就會在定義狗的時候 再定義一個它的吃東西 當我們在執行一個 poppy.吃東西 的時候呢 我們是定義在 prototype 裡面 因為不是物件裡面 所以它會說物件裡面沒有 然後接下來找到 prototype 當它找到狗上面吃東西的時候 它就會執行狗上面的吃東西 然後不會繼續往上找 所以它就會以最近的這個 function 也就是繼承的層次 最下面的這個函數為主 ---- ![](https://i.imgur.com/i5aqTcX.png) NOTE:如果要搞懂繼承的話 我們就要知道 類別是產生一個新的物件 而 prototype 是獨立於類別跟物件之外的 當物件有寫共用屬性的時候 就會往上查找到 prototype 而我們剛剛講的原型鏈 也就是繼承的方式 我們必須要讓狗可以使用生物體 它的一些方法或屬性的時候呢 我們就讓 prototype 的上游再一個 prototype 所以它就會不斷的往上查找方法 屬性直到找到可以使用為止 所以大家應該了解繼承的這整個概念了 那我們要怎麼樣定義 也就是在程式碼中實作繼承呢 ---- ![](https://i.imgur.com/gvuIXTk.png) NOTE:總共就是三個步驟 第一個是建立的時候呼叫母類別 第二個子類別新增的時候繼承 prototype 第三個將建構函數指向自己 那這看起來有點難 ---- ![](https://i.imgur.com/rQVFG7u.png) NOTE:我們直接進到說明的部分 首先第一件事情是我在建立這個物件的時候 我也要呼叫母類別的建構函數 什麼意思啊 你可以看到 我們之前的那個建構函數 那個 function 我們定義 this.attr 這個物件上面的屬性等於什麼東西 所以當我們希望這個物件 同時有繼承來生物體的屬性的時候 其實最簡單的方式就是把這個物件 丟進生物體的初始化函數嘛 那丟進這個初始化函數 我們必須要用一個特殊的方式 我們比如說 我們這邊用 Parent 就是繼承的這個母類別 .call 那 call 的方式就是呼叫函數 呼叫什麼函數呢 我拿 this 也就是這個物件來呼叫 所以你可以想像我們今天有點像偷樑換柱一樣 我們把現在正在用的這個物件 當做 this 傳進去給生物體讓它初始化 所以它就不會重複的產生出來兩個新物件 而是我們會在 比如說你看這邊 我們先定義一個 Creature 也就是生物這個類別 然後它的一些初始化函數 那我們定義了一個 Dog 也就是繼承了生物的狗 然後我們在第一行我們就讓它 Creature.call this 跟一些引數 所以在這邊生物體就把它拿來初始化了 而初始化的是這個目標 是 this 那這個 this 就是指向你 狗產生出來的這個新物件 所以不管是狗這個物件 狗正在新增的這個物件 還是生物體正在新增的這個物件 我都指向同一個這個東西 所以我們最後產生出來的結果就會是 我用狗的方式 設定完了一遍這個物件 也用生物體的方式設定完了這個物件 那也要注意一下順序是說 我會希望先有生物體的屬性嘛 所以我先用 Creature.call 然後接下來這一個初始化執行完之後呢 我們再去設定狗相關的屬性 ---- ![](https://i.imgur.com/BfsP0P3.png) NOTE:那接下來我們來講第二個 為子類別新增繼承的 prototype 甚麼叫做為子類別新增繼承呢 假設我們希望狗 可以用生物體的移動或吃東西的方法 那最簡單的方式 你會問說 為什麼我不把生物體的 prototype 指定給狗就好了 因為物件會自動往上找嘛 它就會找到生物的 prototype 那其實我們會發現一個問題是 因為我們想要對狗也要做一些 單獨的 prototype 設定 狗也有一些共用的函數 是只有狗有的 所以假設我現在定義一個吠叫或攻擊 而我沒有一個中繼的 prototype 我直接連到生物的 prototype 的時候呢 因為我定義在 prototype 上 所以我就會動到生物的這個物件了 所以要怎麼做 我們必須要使用 Object.create Object.create 它是創造一個空的新的物件 但是將它的上游 連到你給它的 prototype 所以我們用比如說生物跟狗的案例呢 我們讓 Dog.prototype 這個狗的原型會等於 Object.create 一個空的物件 然後我們傳給它 Creature.prototype 所以這一個空物件上游連結了一個原型鏈 一個 prototype 是生物的 所以你可以想像中間被產生出了一個空的物件 但是有這個虛線存在 讓我們找不到方法的時候 可以回溯的上游去呼叫生物的方法 所以這樣產生出來之後 我們把它指定給 Dog.prototype ---- ![](https://i.imgur.com/mYTc06e.png) NOTE:那最後一件事情呢 就是把建構函數指向自己 我們想像我們在定義類別的時候 都會定義一個新的 function 嘛 那這個 function 假設我們今天用 Object.create 做了一個新的空物件的時候 它也會往上找阿 可是它找不到這個 constructor 也是我們定義的這個產生函數的時候呢 它就會繼續找 找到生物的 prototype 上面的產生函數 所以就變成了 它會用生物的那個產生函數 來初始化我的物件 而不是狗的這個 所以我們必須要讓 Child.prototype.constructor 比如說我們讓狗的 prototype 的建構函數 會等於狗的建構函數 這樣子呢 它才不會繼續往上游回溯的生物的建構函數 ---- ![](https://i.imgur.com/YFQ14uk.png) NOTE:那這樣大家聽下來應該已經整個茫掉了 最後講一下實際範例是人的繼承 我們想要先做一個人的物件 Person 那這個 Person 有一些基礎的像名字 年齡 打招呼 那我們希望延伸這個人類的物件呢 新增一個 Worker 工作者的物件 那這個工作者的物件有工作種類 薪水跟工作的自我介紹 它會說我的名字是什麼 我的工作是什麼 所以它會用到上游的這個名字 那也會用到自己的這個 工作種類 那這邊我們看一下 我們首先 var 一個 Worker 它會等於一個 function 先初始化整個產生函數 那在裡面我們做的第一件事情 因為我們要繼承人 所以我們讓 Person.call(this.name) 所以我們這邊呼叫人的建構函數 然後我們把什麼傳給它建構呢 我們把這個正在新增的物件傳給它 也就是 this 然後我們傳給它這個人啊 我們之前人不是用個名字來初始化嗎 所以我們就後面函數的參數一樣接給它 那接著呢 新增完人的屬性之後 我們去設定 this.job 就是這個 Worker 上面的一些屬性 那新增完這個之後 第二件事情是 prototype 所以我們讓 Worker.prototype 這個工人的原型 會等於 Object.create 我們創造一個新的物件 連結到上游的 Person.prototype 所以這現在是空的 但是有那個連結存在 然後最後我們讓 Worker.prototype,constructor 也就是這個工作者的建構子 就是那個產生的函數呢 會等於自己的產生函數 所以我們讓它不會再上游回溯到人的產生函數 那做完這個長長的繼承之後 我們就可以來做 Worker.prototype.tellJob 我們在 prototype 上面定義我們最一開始提到的共用函數 所以我們 tellJob 等於 I am 然後 this.job 然後我們也可以在 比如說在後面接一個 my name is "this.name" 那它也一樣可以吃到這個繼承來的 Person 的名字 那最後就跟我們之前一樣 我們產生一個新的 Worker var Henry 會等於 new Worker 然後給它名字跟它的工作 它就會自動初始化那個 Person 的類別 然後繼承過來 那最後我們就可以讓 Henry.sayHello 或是 Henry.tellJob 不管是人上面的打招呼 或是 Worker 上面的自我介紹都可以呼叫 --- ## [物件導向入門之實作篇](https://yuakatl.github.io/JS-OOP-practice/) NOTE:https://codepen.io/yu_sheng/pen/rNazzxx?editors=0010 ---- ![](https://i.imgur.com/b64dMPF.png) NOTE:在前面 我們講了如何使用物件導向 來把屬性跟方法綁在一起 以及繼承其他類別的東西 產生一個更進階的類別 那在這裡 我們會用物件導向的思維 來寫一個球球對打的小遊戲 那這個小遊戲裡面 兩個板子會互相對打 這個大家應該蠻熟悉的 那在這個遊戲裡面的話呢 我會有幾個主要的物件 ---- ![](https://i.imgur.com/orwmhKy.png) NOTE:包括板子跟球 那這兩個板子跟球 你可以想像 它都是在遊戲的畫面裡面 所以它會有自己的位置 元素跟大小 所以我們會有一個遊戲物件當成母類別 分別有位置 元素跟大小 以及一些方法 像更新 css 把位置跟大小更新到實際的網頁上面 以及會有一個函數叫做 Collide 去偵測有沒有碰撞 我們可以傳給它另一個物件 然後讓它跟我們說 這兩個物件是不是有重疊 那接下來我們會延伸繼承這個遊戲物件 變成球跟板子兩個類別 在球的部份我們會加上一個速度的屬性 然後我們會有一個 Update 更新 它除了執行母類別的更新 css 之外呢 也會把速度加到位置上面 所以你可以想像球就會連續的移動 那以及球會有一個初始化的函數 裡面會讓它有亂數的初始速度 以及把它放到畫面中央 那接下來在板子的部份呢 我們沒有加上屬性 不過我們的 Update 更新 我們會檢查它是不是有超出邊界 那如果有超出邊界 就限制它的移動 所以它不會超出遊戲的範圍 然後最後它也會呼叫母類別的更新 css 把資料反應到現在的網頁上面 ---- ![](https://i.imgur.com/QmsYier.png) NOTE:那接下來是遊戲本體的部分 在遊戲本體我們也會建立一個物件 一個類別來記錄它 包括計時器 去呼叫每一個當下的那一個遊戲更新 以及 Control 我現在鍵盤按的狀態 然後分數 那以及一些方法 像是開始 結束遊戲 然後初始化我的鍵盤控制 以及遊戲本體更新的那個迴圈 那我們會藉由遊戲這個物件呢 來控制板子跟球的行為 ---- ![](https://i.imgur.com/5QOD0nh.png) NOTE:我們來看一下實際上的程式碼大概會長這樣子 定義一個遊戲物件 然後我們藉由這個縮放的功能 我們可以很清楚的看到 遊戲的物件上面有哪些方法可以呼叫 以及我在初始化遊戲物件的時候呢 要傳進去什麼樣的參數 ---- ![](https://i.imgur.com/Esnnz2J.png) NOTE:那這邊也是球 我們去繼承了遊戲的物件 那我們在球上面加上了 Update 跟初始化這兩個額外的方法 ---- ![](https://i.imgur.com/kNEqoyg.png) NOTE:那以及 Board 我們也是傳進去繼承遊戲物件之後呢 我們再加上一個 Update 去檢查有沒有超過邊界跟更新 ---- ![](https://i.imgur.com/CaJj3wP.png) NOTE:然後最後我們會有個總體的遊戲 那這個遊戲呢 我會有它的計時器 以及剛剛所講的 開始 結束遊戲等等的控制 所以你可以注意到用物件導向的方式來寫程式 我們可以讓程式碼非常好管理 而且一目了然 ---- ![](https://i.imgur.com/t7EiwPh.png) NOTE:那我們先講一下尺寸資訊的部分 我們整個遊戲場地是 500500 然後裡面的東西用絕對定位 針對這整個方框做定位 所以我們上面這個板子距離上面 30 下面這個板子的上距是 455 然後每個板子是 10015 的尺寸 然後最後球是 1515 那了解尺寸資訊之後呢 我們就可以開始進到物件導向的實作

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