【JS 筆記】從這裡入門,你會更了解 Javascript 原型鏈

Chengcheng Hsu
7 min readSep 10, 2021

--

Photo by Hush Naidoo Jade Photography on Unsplash

寫這篇的發想是初學到原型鏈時被一堆文章和專有名詞轟炸,一開始就 new 給你看,看到最後也只是掌握一個模糊的概念,另外 Alpha camp 的這篇文章讓我從不同的角度去加深這塊的理解,裡面有圖文並茂的解釋,很推薦可以先看看,那這篇主要是以那篇文章的觀念加上以初學 JS 的角度來做切入,最後實做一個基本的建構式函式來映證前面的名詞,總之正面迎擊自己覺得棘手的主題是學習最快的方式,藉此也能釐清觀念。如果以下理解有誤或是你想進一步討論的歡迎指教。

這篇你會知道什麼是:

1. 建構式函式 (constructor function)
2. 原型 (prototype)
3. 原型鏈 (prototype chain)
4. 原型繼承 (prototypal inheritance)

這裡先不討論:

1. ES6 的 class
2. 基於類別(Class)和基於原型(Prototype) 物件導向程式語言的差別
3. new 這個關鍵字到底做了什麼事

1. 先從你懂的 JS 開始:

在初學 JS 的時候會有很多預設的方法讓你可以對字串、數字、陣列和字串做操作,讓他們協助你達到你想要做的事情,像是以下是使用 toUpperCase() 就能很方便把字串變大寫的方法,所以今天如果你想了解原型鏈時可以先從知道 toUpperCase()預設的函式它打從哪來的開始。

let test = 'example'console.log(test.toUpperCase())
//EXAMPLE

我們可以把它拿到 Devtool console 來執行看看,當你宣告完 test 後,當你打 test. 時會跑出很多方法,包含印出字串長度、索引字串位置等,當然也包含toUpperCase() 這個函式,這時候可以發現原來他們是來自同個地方。

那這個地方又是哪裡?以及這個 test 的變數又是怎麼取得這個函式的?

2. 從你懂的 JS 延伸至那些名詞

這時候我們可以從上面的例子來了解一些名詞了

什麼是建構子函式、函式建構式(function constructor)?

從上面的例子我們可以說 test 的建構子函式是 String ,怎麼知道的?

可以在 Devtool console 印出變數名稱.constructor.name 就可以知道這個變數的函式建構式是什麼,當你宣告 number 的變數,建構式就是 number,宣告 array ,建構式就是 array,所以今天如果你自己做一個函式建構式並命名 Dog,這時候宣告一個變數去承接,那這個變數的函式建構式就會是 Dog

所以變數 test 就是透過建構式函式 String() 產生的實例 (instance)

函式建構式它其實就已經內建在 JS ,像是 String、number 等等,讓你可以使用它裡面的屬性與方法,當然你也可以自己建構以及自己定義那些會被重複利用的屬性與方法。

什麼是原型 (prototype)?

一樣可以在 Devtool console 印出Object.getProtoytypeOf(變數名稱) 就可以來檢視原型,可以看到 test 這個變數的原型是建構子函式 String 和裡面所定義的屬性與方法。

所以原型是建構子函式 + 屬於此函式的屬性與方法

什麼是原型鏈 (prototype chain)?原型繼承(prototypal inheritance)?

再來是必須了解到原型不是只有一層,所以我們可以利用 Object.getProtoytypeOf(變數名稱) 定義三個變數並印出來查看他們的原型,可以看到 test 的「原型」有其屬性與方法, test 的「原型的原型」也有其屬性與方法,test 的「原型的原型的原型」就是 null 了,它們的關係是層層相依的。

也可以利用變數名稱.__proto__ 來查看原型,結果是一樣的

當你使用 test.toUpperCase() 時他會透過隱藏的 .__proto__ 來幫你往上一層去找,以下可以發現有無加 .__proto__印出來的結果會是一樣的

今天我們用某個函式時它會先找 test 有沒有這個函式,然後一層一層往上找,如果有找到就會依照函式去執行,沒找到則會回傳錯誤,toUpperCase() 則在 String.prototype 找到並回傳結果。

以階層來看的話會是這樣:

所以原型鏈就是變數、物件這些原型有層層相依的概念

原型繼承則是依循原型鏈讓你可以使用到上層的屬性與方法

3. 實作一個基本的原型來那些名詞

可以從程式碼上至下來看註解,也將saySomething()tellSomething() 函式設定在不同的原型階層來做測試。

在執行印出時,順序會是會先去找 d 這個變數有沒有saySomething()tellSomething() 函式,沒有找到則透過隱藏的 d.__proto__來找到 Dog 這層的原型,發現有 saySomething()所以執行並回傳結果,沒找到 tellSomething() 函式則會再往上找,發現設定在 Oject 這層的原型,所以執行並回傳結果。

另外可以發現印出 d.__proto__d.__proto__.__proto__ 分別擁有這兩個函式,所以 d.__proto__ 就是 Dog 的原型,d.__proto__.__proto__ 就是 Object 的原型

console.log(d.__proto__ === Dog.prototype)// Trueconsole.log(d.__proto__.__proto__ === Object.prototype)// True

總結:

對於此 JS 原型鏈還有衍伸很多觀念可以了解,像是你會發現不論是字串、數值、布林值、陣列,甚至是函式,當我們使用 Object.getPrototypeOf() 去往上找它們的原型時,你會發現都有 Oject 這個原型,而且也可以去定義新的函式或是使用其預設的方法。此篇僅著重於從初學者對於 JS 從原來會使用一些預設的方法來去延伸了解這些名詞,讓那些和我一樣讀很多文章但只有模糊架構的人有不一樣的角度做切入並了解,再去看參考文章那些神人的文章會有更深的體會。

參考文章:

原型繼承與原型鏈

JavaScript 的內建函數建構子

該來理解 JavaScript 的原型鍊了

前端三十|15. [JS] 什麼是原型鏈?

JavaScript 的內建函數建構子

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Chengcheng Hsu
Chengcheng Hsu

Written by Chengcheng Hsu

Movie, Travel and Story | Software developer | contact me: ychsu.wk@gmail.com

No responses yet

Write a response