diff options
author | MDN <actions@users.noreply.github.com> | 2022-01-18 00:57:02 +0000 |
---|---|---|
committer | MDN <actions@users.noreply.github.com> | 2022-01-18 00:57:02 +0000 |
commit | 149b599368b4e27cf7d05f270a43519f599372fd (patch) | |
tree | 7e31e2b1434a345cad94b028cdf5d294fa21257b /files/zh-tw/learn/javascript/objects/classes_in_javascript | |
parent | a9248e91ff3d691330714a995987a33cb67b9fa8 (diff) | |
download | translated-content-149b599368b4e27cf7d05f270a43519f599372fd.tar.gz translated-content-149b599368b4e27cf7d05f270a43519f599372fd.tar.bz2 translated-content-149b599368b4e27cf7d05f270a43519f599372fd.zip |
[CRON] sync translated content
Diffstat (limited to 'files/zh-tw/learn/javascript/objects/classes_in_javascript')
-rw-r--r-- | files/zh-tw/learn/javascript/objects/classes_in_javascript/index.html | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/files/zh-tw/learn/javascript/objects/classes_in_javascript/index.html b/files/zh-tw/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..fdbd246ba2 --- /dev/null +++ b/files/zh-tw/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,211 @@ +--- +title: JavaScript 中的「繼承」 +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}</div> + +<p class="summary">在解釋過大部分的 OOJS 細節之後,本文將說明該如何建立「子」物件類別 (建構子),並從其「母」類別繼承功能。此外,也將建議開發者應於何時、於何處使用 OOJS。</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">必備條件:</th> + <td>基本的電腦素養、已了解 HTML 與 CSS 基本概念、熟悉 JavaScript 基礎 (可參閱〈<a href="/en-US/docs/Learn/JavaScript/First_steps">First steps</a>〉與〈<a href="/en-US/docs/Learn/JavaScript/Building_blocks">Building blocks</a>〉) 與 OOJS 的基礎 (可參閱〈<a href="/en-US/docs/Learn/JavaScript/Object-oriented/Introduction">Introduction to objects</a>〉)。</td> + </tr> + <tr> + <th scope="row">主旨:</th> + <td>了解應如何建構 JavaScript 中的繼承。</td> + </tr> + </tbody> +</table> + +<h2 id="原型繼承">原型繼承</h2> + +<p>目前為止,我們看過幾個繼承的實作範例:原型鍊的運作方式,以及繼承的成員如何形成原型鍊。但這些大部分都與內建的瀏覽器函式有關。那我們又該如何在 JavaScript 建立物件且由其他物件繼承而來呢?</p> + +<p>如稍早的系列文章中所述,某些人認為 JavaScript 並非真正的物件導向 (OO) 語言。在「典型 OO」中,你必須定義特定的類別物件,才能定義哪些類別所要繼承的類別 (可參閱〈<a href="http://www.tutorialspoint.com/cplusplus/cpp_inheritance.htm">C++ inheritance</a>〉了解簡易範例)。JavaScript 則使用不同的系統 —「繼承」的物件並不會一併複製功能過來,而是透過原型鍊連接其所繼承的功能,亦即所謂的原型繼承 (<strong>Prototypal inheritance</strong>)。</p> + +<p>就透過範例了解此一概念吧。</p> + +<h2 id="入門">入門</h2> + +<p>首先將 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-start.html">oojs-class-inheritance-start.html</a> 檔案 (亦可參考<a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-start.html">實際執行</a>) 複製到本端磁碟。可在裡面找到本課程一直沿用的 <code>Person()</code> 建構子範例,但這裡有些許不同,也就是該建構子只定義了屬性:</p> + +<pre class="brush: js">function Person(first, last, age, gender, interests) { + this.name = { + first, + last + }; + this.age = age; + this.gender = gender; + this.interests = interests; +};</pre> + +<p>這些函式均已定義於建構子的原型之上,例如:</p> + +<pre class="brush: js">Person.prototype.greeting = function() { + alert('Hi! I\'m ' + this.name.first + '.'); +};</pre> + +<p>假設要建立一個像前面 OO 定義中說過的 <code>Teacher</code> 類別,且除了繼承 <code>Person</code> 的所有成員,還要包含:</p> + +<ol> + <li>新的 <code>subject</code> 屬性,可包含老師所傳授的科目。</li> + <li>更新過的 <code>greeting()</code> 函式,要比標準的 <code>greeting()</code> 函式更正式一點,更適合老師在校對學生使用。</li> +</ol> + +<h2 id="定義_Teacher()_建構子函式">定義 Teacher() 建構子函式</h2> + +<p>首先必須建立 <code>Teacher()</code> 建構子,將下列程式碼加到現有程式碼之下:</p> + +<pre class="brush: js">function Teacher(first, last, age, gender, interests, subject) { + Person.call(this, first, last, age, gender, interests); + + this.subject = subject; +}</pre> + +<p>這看起來和 Person 建構子有許多地方類似,但比較奇怪的就是之前沒看過的 <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call">call()</a></code> 函式。此函式基本上可讓你呼叫在其他地方定義的函數,而非目前內文 (context) 中定義的函式。當執行函式時,第一個參數用來指定你要使用 <code>this</code> 值,其他參數則指定應該傳送到該函式的參數。</p> + +<div class="note"> +<p><strong>注意:</strong>我們此範例中建立新的物件實例時,會指定所要繼承的屬性。但必須注意的是,即使實例不需將屬性指定為參數,你還是必須在建構子中將屬性指定為參數 (在建立物件時,你可能獲得設定為隨意值的屬性)。</p> +</div> + +<p>所以這裡可在 <code>Teacher()</code> 建構子函式之內有效執行 <code>Person() </code>建構子函式 (如上述),藉以在 <code>Teacher()</code> 之內定義相同的屬性,但使用的是傳送到 <code>Teacher()</code> 的屬性值,而非 <code>Person()</code> 的屬性值 (我們將 <code>this</code> 值設為簡單的「<code>this」並</code>傳送到 <code>call()</code>,代表這個 <code>this</code> 是 <code>Teacher()</code> 的函式)。</p> + +<p>建構子的最後一行則定義了新的 <code>subject</code> 屬性,代表只有老師具備,一般人不會有。</p> + +<p>我們也可以單純這樣做:</p> + +<pre class="brush: js">function Teacher(first, last, age, gender, interests, subject) { + this.name = { + first, + last + }; + this.age = age; + this.gender = gender; + this.interests = interests; + this.subject = subject; +}</pre> + +<p>但這樣只是重新定義了新的屬性,而不是繼承自 <code>Person() 而來,所以無法達到我們預設的目標,也需要更多程式碼才能達成。</code></p> + +<h2 id="設定_Teacher()_的原型與建構子參考">設定 Teacher() 的原型與建構子參考</h2> + +<p>到目前為止發現一個問題:我們定義了新的建構子,但預設只有 1 個空白的 <code>prototype</code> 屬性。接著要讓 <code>Teacher()</code> 繼承 <code>Person()</code> 原型中所定義的函式,該怎麼做呢?</p> + +<ol> + <li>繼續在現有程式碼下方加入: + <pre class="brush: js">Teacher.prototype = Object.create(Person.prototype);</pre> + 這裡再次用好朋友 <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create">create()</a></code> 解救。我們透過 <code>create()</code> 並搭配等同於 <code>Person.prototype</code> 的原型,建立新的 <code>prototype</code> 屬性值 (它本身就是物件,包含屬性與函式) ,並將之設定為 <code>Teacher.prototype</code> <code>的值。也就是說</code> <code>Teacher.prototype</code> 現在會繼承 <code>Person.prototype</code> 上的所有可用函式。</li> + <li>此外,基於我們的繼承方式,<code>Teacher()</code> <code>prototype</code> 的建構子屬性目前設定為 <code>Person()。可參閱</code> <a href="https://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor">Stack Overflow post</a> 進一步了解原因。可先儲存自己的程式碼、在瀏覽器中載入頁面,再將下列程式碼輸入至 JavaScript 主控台以驗證: + <pre class="brush: js">Teacher.prototype.constructor</pre> + </li> + <li>這樣可能會產生問題,所以要設定正確。你可回到自己的原始碼並在最下方加入下列程式碼: + <pre class="brush: js">Teacher.prototype.constructor = Teacher;</pre> + </li> + <li>如果儲存並重新整理之後,只要輸入 <code>Teacher.prototype.constructor</code> 應該就會回傳 <code>Teacher()</code>。</li> +</ol> + +<h2 id="給_Teacher()_新的_greeting()_函式">給 Teacher() 新的 greeting() 函式</h2> + +<p>接著必須在 <code>Teacher()</code> 建構子上定義新的 <code>greeting()</code> 函式。</p> + +<p>最簡單的方法就是在 <code>Teacher()</code> 的原型上定義此函式。將下列加入程式碼最底部:</p> + +<pre class="brush: js">Teacher.prototype.greeting = function() { + var prefix; + + if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') { + prefix = 'Mr.'; + } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') { + prefix = 'Mrs.'; + } else { + prefix = 'Mx.'; + } + + alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.'); +};</pre> + +<p>這樣就會顯示老師的問候語,也會針對老師的性別使用合適的稱呼,可用於條件陳述式。</p> + +<h2 id="簡易範例">簡易範例</h2> + +<p>現在你已經輸入所有程式碼了,可以試著用<code>Teacher()</code> 建立物件實例。將下列 (或是你想用的類似程式碼) 加入現有程式碼的底部:</p> + +<pre class="brush: js">var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');</pre> + +<p>儲存並重新整理之後,試著存取新 <code>teacher1</code> 物件的屬性語函式,例如:</p> + +<pre class="brush: js">teacher1.name.first; +teacher1.interests[0]; +teacher1.bio(); +teacher1.subject; +teacher1.greeting();</pre> + +<p>這樣應該運作無虞。前 3 行的存取成員即繼承自一般 <code>Person()</code> 建構子 (類別)。最後 2 行的存取成員只能用於特定的 <code>Teacher()</code> 建構子 (類別) 之上。</p> + +<div class="note"> +<p><strong>注意:</strong>如果你無法進行到現有進度,可比較自己與<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-finished.html">完整版本</a> (亦可看<a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-finished.html">實際執行</a>的情形) 的程式碼。</p> +</div> + +<p>這裡所提的技巧,當然不是在 JavaScript 建立繼承類別的唯一方法,但足以堪用。並可讓你了解應如何於 JavaScript 實作繼承。</p> + +<p>你可能也想看看某些新的 {{glossary("ECMAScript")}} 功能,可更簡潔的在 JavaScript 中繼承 (參閱 <a href="/en-US/docs/Web/JavaScript/Reference/Classes">Classes</a>)。但由於這些功能尚未廣泛支援其他瀏覽器,這裡先略過不提。本系列文章中提到的其他程式碼,均可回溯支援到 IE9 或更早版本。當然還是有辦法支援更舊的版本。</p> + +<p>一般方法就是使用 JavaScript 函式庫,且最常見的就是簡單集結可用的功能,更快、更輕鬆的完成繼承。例如 <a href="http://coffeescript.org/#classes">CoffeeScript</a> 即提供了 <code>class</code><code>、extends</code> 等等。</p> + +<h2 id="進階習題">進階習題</h2> + +<p>在<a href="/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS#Object-oriented_programming_from_10000_meters">〈OOP 理論〉段落</a>中,我們也納入了 <code>Student</code> 類別並繼承了 <code>Person</code> 的所有功能,此外也提供不同的 <code>greeting()</code> 函式,且較 <code>Teacher</code> 的問候語沒那麼正式。在看了該段落中的學生問候語之後,可試著實作自己的 <code>Student()</code> 建構子,並繼承 <code>Person()</code>, 的所有功能,再實作不同的 <code>greeting()</code> 函式。</p> + +<div class="note"> +<p><strong>注意:</strong>如果你無法進行到現有進度,可參考<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-student.html">完成版本</a> (亦可看<a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-student.html">實際執行</a>的情形)。</p> +</div> + +<h2 id="物件成員摘要">物件成員摘要</h2> + +<p>簡單來說,你基本上需要考量 3 種類型的屬性\函式:</p> + +<ol> + <li>已於建構子函式中定義,會交給物件實體的屬性\函式。這應該很容易處理。在你自己的程式碼中,就是透過 <code>this.x = x</code> 類別行並在建構子中定義的成員;在瀏覽器程式碼中,就是僅限物件實體可用的成員 (一般是透過 <code>new</code> 關鍵字並呼叫建構子所建立,例如 <code>var myInstance = new myConstructor()</code>)。</li> + <li>直接在建構子上定義,並僅能用於該建構子的屬性\函式。這類屬性\函式往往只能用於內建瀏覽器物件之上,並直接「鍊接」至建構子 (而非實例) 以利識別,例如 <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys">Object.keys()</a></code>。</li> + <li>定義於建構子原型上的屬性\函式,交由所有的實例繼承,亦繼承了物件類別。這類屬性\函式包含建構子原型屬性之上定義的所有成員,例如 <code>myConstructor.prototype.x()</code>。</li> +</ol> + +<p>如果你不確定到底屬於上述的哪一個,也別擔心。現在你還在學習階段,往後定會熟能生巧。</p> + +<h2 id="在_JavaScript_中使用繼承的時機?">在 JavaScript 中使用繼承的時機?</h2> + +<p>看到這裡,你心裡應該覺得很複雜。沒錯。「原型」與「繼承」可說是 JavaScript 最複雜的概念之二,但許多 JavaScript 的強大功能與彈性,均來自於其物件結構與繼承,也值得你了解運作方式。</p> + +<p>不論是使用 WebAPI 的多樣功能,或是你在字串、陣列等所呼叫的函式\屬性 (定義於內建瀏覽器物件之上),你都可以不斷繼承下去。</p> + +<p>在自己的程式碼裡,特別是剛接觸或小型專案時,你用繼承的頻率可能沒那麼高。若沒真正需要,只是「為使用而使用」繼承,老實說只是浪費時間。但隨著程式碼規模越來越大,你就會找到使用的時間。如果你發現自己開始建立類似功能的多個物件時,就可先建立通用的物件類型,來概括所有共用的功能,並在特定物件類型中繼承這些功能,既方便又有效率。</p> + +<div class="note"> +<p><strong>注意:</strong>基於 JavaScript 運作的方式 (如原型鍊等),物件之間的功能共享一般稱為<strong>「</strong><strong>委託 (Delegation)」</strong>,即特定物件將功能委託至通用物件類型。「委託」其實比繼承更精確一點。因為「所繼承的功能」並不會複製到「進行繼承的物件」之上,卻是保留在通用物件之中。</p> +</div> + +<p>當使用繼承時,建議你不要設定太多層的繼承關係,並時時留意你所定義的函式與屬性。在開始寫程式碼時,你可能會暫時修改內建瀏覽器物件的原型,除非你真的需要,否則儘量別這麼做。太多繼承可能連你自己都搞混,而且一旦需要除錯就會痛苦萬分。</p> + +<p>最後,物件可說是另一種形式的程式碼再利用,如同函式或迴圈一般,且有其專屬的角色與優點。如果你正建立一堆相關變數與函式,並要全部一起追蹤、封裝,就可以透過物件。你也能以物件方式傳送整個資料集合。而且上述兩種情況均不需使用建構子或繼承就能夠達成。如果你只需要某一物件的單一實作,那麼單純使用物件就好,完全不需要繼承。</p> + +<h2 id="摘要">摘要</h2> + +<p>本文為大家溫習了 OOJS 核心理論和語法。現在你應該了解 JavaScript 物件與 OOP 的基本概念、原型及原型繼承、建立類別 (建構子) 與物件實例的方法、為類別新增功能、建立從其他類別繼承而來的子類別。</p> + +<p>下篇文章就要來看看該如何搭配 JavaScript Object Notation (JSON),使用 JavaScript 物件的常見資料交換格式。</p> + +<h2 id="另可參閱">另可參閱</h2> + +<ul> + <li><a href="http://www.objectplayground.com/">ObjectPlayground.com</a> — 互動式學習網站,讓你了解物件。</li> + <li><a href="https://www.amazon.com/gp/product/193398869X/">Secrets of the JavaScript Ninja</a> 的第六章 — 進階 JavaScript 概念與技術的好書。第六章講述了原型與繼承的概念。此書有紙本與線上版。</li> + <li><a href="https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes">You Don't Know JS: this & Object Prototypes</a> — Kyle Simpson 絕佳 JavaScript 手冊系列的一部分。第五章特別深入講述了原型。我們透過本文為初學者提供簡易概念,但 Kyle 則說明得更深入、更精確。</li> +</ul> + +<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}</p> |