了解JavaScript中的物件原型和原型鏈
本篇文章帶大家介紹一下JavaScript中的物件原型和原型鏈。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
物件原型
相信大家都這樣用過 map
:
let arr = [0, 1, 2] let doubleArr = arr.map(c => c * 2) console.log(doubleArr) // 0, 2, 4
不知道你有沒有想過, arr
本身並沒有設定map
屬性,那為什麼可以用map
這個函數呢?
把它印出來看看:
console.log(arr) // 0: 0 // 1: 1 // 2: 2 // length: 3 // __proto__: Array(0)
出現了一個名為__proto__
的對象,如果再將其展開,就會看到所有Array 物件可以使用的函數;當然我們也可以在其中找到map
函數,而這也正是例子中所呼叫的arr.map
這個函數:
console.log(arr.map === arr.__proto__.map) // true
這裡出現的__proto__
對象,也就是所謂的原型對象(Prototype) 。
不同於Java、C# 等基於類別(Class) 的物件導向語言,透過定義類別、建立實例、指定繼承等方式來傳遞屬性及方法;Javascript 則是個基於原型(Prototype)的對語言 ,透過預先建立的原型對象,當新物件建立時指定物件的原型要參考哪個原型物件。
而當我們呼叫物件的屬性或方法時,若物件本身沒有這個屬性或方法,JavaScript 會自動尋找它原型中的方法,這也就是為什麼可以直接呼叫arr.map
而不會出錯的原因。
原型鏈
你可能已經發現在前面的例子中,__proto__
物件裡仍然有__proto__
屬性:
console.log(arr.__proto__) // Array 的 Prototype console.log(arr.__proto__.__proto__) // Object 的 Prototype console.log(arr.__proto__.__proto__.__proto__) // null
在上述機制中,每當物件建立時都會綁定原型,既然物件都有原型,物件原型本身也是對象,自然也不例外;由這個例子中我們可以看出:
-
arr
是陣列實例,原型是Array -
#arr.__proto__
是陣列的原型,原型是Object -
#arr.__proto__.__proto__
是物件的原型,原型是null
-
arr.__proto__.__proto__.__proto__
是null,沒有任何屬性
由於每個物件都有原型,這樣就形成了一個關聯一個、層層相互依賴的從屬關係,我們把它們叫做原型鏈(Prototype Chain) ;透過這個機制,讓物件得以使用原型中的屬性和方法,並憑藉原型鏈一層一層的依序繼承,讓物件能擁有原型鏈上所有原型的功能,這就是JavaScript 物件背後的運作機制。
補充:在 JavaScript 中,幾乎每個原型鏈的末端都會是 Object,並且最後指向到 null
。
使用原型
說了這麼多,該來點程式碼了,接下來就來練習一下原型的建立、設定及修改吧。
先來建立一個新的物件建構子:
function Person(name) { this.name = name }Person.prototype.hello = function () { console.log(`Hello ${this.name}.`) }let gary = new Person('Gary') gary.hello() // Hello Gary.Object.getPrototypeOf(gary) // {hello: ƒ, constructor: ƒ}
上面的範例建立了一個簡單的物件建構子Person()
,並在建構子中設定對象屬性。在物件的方法中,由於方法不需要讓每個物件獨自擁有一份,以避免造成冗餘的記憶體消耗,應該要像前面Array.prototype.map
的範例那樣把物件的方法設定給原型物件(Person.prototype
),讓這個建構函式建立出來的物件都可以共用這些方法。最後建立一個新的 Person
對象,並透過 getPrototypeOf(obj)
取得新產生對象的原型。
Q:為什麼不直接用__proto__
取得原型物件?
A:因為雖然__proto__
被幾乎所有的瀏覽器支持,但它是非標準屬性;透過getPrototypeOf
取得物件的原型是正確的方法。提醒:
Person.prototype
不是Person
的原型,而是建構子執行後所建立的新物件的原型;千萬不要把建構函數的prototype
屬性與物件的原型搞混!
原型繼承
接著再建立新的物件原型,並繼承Person
:
function Engineer(name, skill) { Person.call(this, name) this.skill = skill } Engineer.prototype = Object.create(Person.prototype) Engineer.prototype.constructor = Engineerlet alice = new Engineer('Alice', 'JavaScript') alice.hello() // Hello Alice. console.log(alice.skill) // JavaScriptObject.getPrototypeOf(alice) // Person {constructor: ƒ}
這裡建立了新的物件Engineer
的原型,並透過Engineer.prototype
的指定,讓它的原型繼承自Person.prototype
,最後重新設定Engineer.prototype.constructor
,讓建構子重新指回自己;這樣就完成了最基本的原型繼承。
Q:为什么需要重新设定constructor
?
A:这边功过Object.create
复制了Person.prototype
的全部属性,连同constructor
属性都会被覆盖掉,如果constructor
属性错误,在做instanceof
判断时会产生错误的结果;因此这边设定继承时需要再次将constructor
重新指定回构造函数本身。
修改原型
原型的引用、继承是直接参照到原型对象上,并非是在每个对象都复制一份原型;因此可以利用这个特性,在原型上增加自定义的属性和方法,让所有该类型的对象都能得到新方法;许多针对旧版浏览器的 Polyfill 就是这样实现的。
例如我们在写 Vue 项目的时候,可能都有做过类似的操作,把共用性高的属性方法放到 Vue.prototype
中:
Object.defineProperty(Vue.prototype, '$date', { value: dateTimeFormat }) // 之后就可以这样用 vm.$date(dateObj)
这样的确很方便,但也要提醒开大家,当我们在做原型修改的时候要特别小心。接着刚才的例子,如果尝试对 Person
原型中的方法做个修改:
Person.prototype.hello = function () { console.log(`Bye ${this.name}.`) }gary.hello() // Bye Gary. alice.hello() // Bye Alice.
如结果所示,当对象原型做修改时,所有原型链上有这个原型的对象,通通都会受到影响,不管对象是在修改前还是修改后创建的。
建议大家除非是 Polyfill,否则应该要极力避免对原生对象的原型进行修改,防止造成可能的意外结果。
ES6 的 Class
看完前面这一大段,是不是觉得心很累?别担心,从 ES6 开始添加了 Class
语法糖,使开发者体验提升了很多。下面把前面的例子用 Class
重构一下:
class Person { constructor (name){ this.name = name } // 方法会自动放到 Person.prototype hello() { console.log(`Hello ${this.name}.`) } }class Engineer extends Person { constructor (name, skill){ super(name) // 调用 Person 的构造函数 this.skill = skill } }let alice = new Engineer('Alice', 'JavaScript') alice.hello() // Hello Alice.Object.getPrototypeOf(alice) // Person {constructor: ƒ}
很方便,同样的功能,代码的可读性却提高了不少,繁琐的设定也都能交给语法自动帮你完成。不过方便的语法背后,底层仍然是对象原型及原型链。
总结
以上是 JavaScript 中关于对象原型的说明,希望能帮你理解对象原型,在这个什么都是对象的语言中,充分理解并掌握对象原型,是成为专业码农必须要突破的关卡之一。
更多编程相关知识,请访问:编程课程!!
以上是了解JavaScript中的物件原型和原型鏈的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數
