繼承表示一個物件取用另一個物件屬性或方法,只要了解其簡單觀念就好了,許多人在解釋這區塊時常會用各種火車、汽車等等例子來做舉例,但講師認為直接講清楚會比較簡顯易懂。
那古典繼承和原型繼承是什麼呢?古典繼承在 C#、Java 裡都有,而且非常熱門。
而古典繼承裡面有非常多方法可以用
原型繼承呢?東西就簡單許多了
古典和原型繼承各自都有他的好壞,所以並沒有一定,所以當有人在講繼承時,就是在講
一個物件取用另一個物件屬性或方法
JavaScript 用了原型繼承,所以代表有個叫作原型(prototype)的概念。
首先我們知道物件可以有屬性和方法,然後我們可以使用點運算子取得屬性或方法,JavaScript 中所有物件、函數,都有原型屬性,而這個屬性會參考到另一個屬性通常被稱為 proto。
假設今天我們有一個 obj 的物件,底下有一個叫 prop1,所以我們可以透過 obj.prop1 來取得。
JavaScript 中所有物件、函數,都有原型屬性。
而這個屬性會參考到另一個物件,稱之為 proto,而 proto 也可以有屬性,例如 prop2,所以當我們要取用 prop2時就可以這樣寫 obj.prop2。
而我們使用點運算子去取用 obj 中的 prop2 時,會找不到,所以他會往原型裡面找。
那原型物件也可以指向到另一個物件。
每個物件都可以有自己的原型,當這個 proto 裡面有一個 prop3,我們就可以用 obj.prop3 取得。
而這過程就像一個鏈子,所以又稱為原型鏈 (prototype chain),但是不要把他跟範圍鏈搞混了,雖然很相似,可是範圍鏈是尋找可以取用的變數,但原型鏈是尋找屬性及方法。
而一般來講這 proto 是隱藏起來的,所以我們才不用這樣撰寫 obj.proto.proto.prop3,只需要 obj.prop3 就好。
但是 JavaScript 中有一個很有趣的狀況,當若有第二個 obj 時,他可以指向同一個原型。
所以當我們呼叫 obj2.prop2,他一樣會回傳相同位子。
而以上這些就是原型及原型鏈的概念,只需要想簡單一點只是有一個特別的參考到我們的物件而已,那接下來我們直接來看點範例。
這邊我們有兩個物件,接下來我們要將 john 設定成原型,但以下範例千萬不要使用於現實中,這只是為了簡單理解觀念而已。
為什麼不是抓到 Default?因為原型鏈的原因導致,所以點運算子會在 john 裡面找到 firstname,所以就會停下來不會再找了,接下來在加一點物件上去。
現在我們知道物件原型,接著我們可以深入瞭解到一件事情 JavaScript 所有東西都是物件或是純值
數值、布林、字串、函數、陣列、物件他們都有原型,除了基本物件(base object)。
讓我們從範例來瞭解,接下將會利用這三個東西 物件、函數、陣列來講解為什麼所有東西都有原型。
首先讓我們試著在瀏覽器輸入以上範例,然後再輸入 a.proto
我們會得到一個基本物件,這在原型鏈上非常底層,而這基本物件有屬性與方法。
那函數呢?我們試著輸入 b.proto
這就是所有函數的原型,所有我們建立的函數都有這個原型,當然也有相關的屬性與方法(你會看到熟悉的 apply、call、bind)。
接下來是陣列 c.proto
這就是一個原型陣列,我們也來看看它是否也有屬性與方法(這裡你也會看到許多熟悉的字眼)。
所以由上面這三個範例我們可以知道一件事情。
JavaScript 所有物件、所有陣列及所有函數都有原型。
那這邊再講一個好玩的問題,原型的原型是什麼?
是原型,所以我們要記得,原型鏈最底下的東西就是原型物件。
接下來將要講解另一個建立物件的函數很有趣很有效的東西,許多資料庫都會使用到,而這稱為 extend。
而通常我們都是用一個叫 Reflection 的東西來做到 extend。
Reflection: 一個物件可以看到自己的東西,然後改變自己的屬性和方法。
讓我們試著從範例來學習。
這時候會看到一個很神奇的東西,getFullName(),這是因為 for in 會到外面取用屬性和方法的 不只在物件本身,還會到原型上找
那如果今天我們要
這樣 getFullName() 就沒有出現了,因為 john 的物件屬性並沒有 getFullName()。
而這個概念可以用來幫助我們做一些事情【補足原型繼承】,所以這邊試試看引入 underscore.js。
這時候我們可以看到 john 有 jane 的物件地址、jim 物件的 getFirstName 函數,還有 jane 物件的 getFormalFullName()。
那這是怎麼做到的?首先先開啟 underscore.js,開啟之前記得千萬不要害怕開源的專案,這是一個非常好學習教材,我們試著找 extend 看看。
首先我們可以看到他建立了屬性或方法,而這個叫 createAssigner 的函數我們試著找看看。
我們可以發現 createAssigner 是一個函數,其中他接受了 keys 還有 defaults,然後回傳函數本身,我們可以看到這是一個閉包。
首先它會先取得傳入參數的長度
再來是包裝物件,他將物件包裝起來(避免參考問題) (((我猜)))
接下來就是當物件長度若小於兩筆,或是物件為空,就返回物件
接下來他會用 for 將所有物件跑一遍,因為陣列第一筆是 john ,所以起始會直接跳過 0 從 1 開始
所以因為經過 underscore.js 的處理, jim 就可以使用 getFormFullName()了。
WierdJavascript