徹底搞懂JavaScript原型和原型鏈

来源:https://www.cnblogs.com/easy1996/p/18208431
-Advertisement-
Play Games

FreeTube —— 一款開源桌面 YouTube 播放器,基於 Electron 實現,同時支 Windows(10 及更高版本)、Mac(macOS 10.15 及更高版本)和 Linux。 ...


基於原型編程

在面向對象的編程語言中,類和對象的關係是鑄模和鑄件的關係,對象總是從類創建而來,比如Java中,必須先創建類再基於類實例化對象。
而在基於原型編程的思想中,類並不是必須的,對象都是通過克隆另外一個對象而來,這個被克隆的對象就是原型對象。
基於原型編程的語言通常遵循下麵的規則:

  • 所有的數據都是對象
  • 要得到一個對象,不是通過實例化類,而是找到一個對象作為原型並克隆它
  • 無法自己直接創建一個對象,對象都是克隆產生,必須有一個根對象,然後克隆它才可以創建對象
  • 對象會記住它的原型
  • 如果對象無法響應某個請求,它會把這個請求委托給自己的原型

函數和對象

JavaScript是基於原型的語言,但JavaScript並非嚴格遵循原型編程的第一條規則,其中基本類型undefined、number、string、boolean就不是對象,其模仿Java,分為了基本類型和對象類型,當然和Java一樣,基本類型可以使用包裝的形式變為對象。
這裡先探討一個問題,有助於後面理解原型和原型鏈,那就是“所有的數據都是對象,函數也是對象“,雖然函數是對象,但是函數擁有很多普通對象不具備的特性:

  1. 可執行性:函數可以被調用執行
  2. 閉包:函數可以創建閉包
  3. 構造函數:函數可以作為構造函數,去創建對象實例,理論上,除了 Object.create 創建的對象,都是通過構造函數創建的對象。
  4. 原型對象屬性:除了箭頭函數,每個函數都有一個 prototype 屬性,它是一個對象,用於存儲通過該構造函數創建的所有實例的共用屬性和方法。

JavaScript原型

從上面可以得知,函數都有 prototype 屬性,稱之為原型,也稱為原型對象,原型對象裡面可以存放屬性和方法,共用給實例對象使用,用於實現繼承效果。
這裡的函數指的是構造函數,即可以通過new創建對象的函數,在JavaScript中普通函數都可以是構造函數,除了箭頭函數。

箭頭函數沒有 prototype 屬性是為了保持它們的設計簡潔性,避免與原型鏈相關的複雜性和潛在問題。箭頭函數同時不能使用new實例化對象。如果你需要定義一個構造器函數,應該使用普通函數而不是箭頭函數。

舉例說明:

const arr = new Array(1, 2, 3)
arr.reverse()
arr.sort()

在上面的代碼中 Array 就是一個構造函數,reverse和sort都是 Array.prototype 原型對象上的函數。
這裡有個問題,為何要將這些方法放在 prototype 原型屬性上,而不是放在構造函數內部呢?
我們來看下麵的代碼:

function Person() {
  this.getName = function () {
    console.log('person')
  }
}
Person.prototype.getProtoName = function () {
  console.log('proto person')
}
const person = new Person()
const person1 = new Person()

console.log(person.getName === person1.getName) // false
console.log(person.getProtoName === person1.getProtoName) // true

很明顯,雖然在實例化對象上都可以調用這些方法,但兩者有本質的不同,將方法放在 prototype 原型對象上,之後實例化的對象使用的都是同一個方法,類似於Java的類方法,而構造函數內的方法,則會重新創建,也就是實例對象方法。

JavaScript原型鏈

瞭解完原型之後,再來看原型鏈就非常簡單了。

  1. 對象可以調用其構造函數的 prototype 原型對象的屬性和方法
  2. 原型對象也是對象,同樣也可以調用其構造函數的prototype原型對象的屬性和方法
  3. 一層一層的形成一條鏈路就是原型鏈

如圖所示:

解讀:

  1. 在瀏覽器中,通常使用對象的 __proto__ 即可找到對象的原型對象,每個對象都有 __proto__ 屬性(還是要去除Object.create創建的),用於指向它的原型對象。
  2. 前面基於原型編程有一個規則是,必須有一個根對象,JavaScript中根對象是:Object.prototype,其是一個空對象。

原型鏈經典圖片

最後結合上面的內容,來看看下麵的經典原型鏈圖:

從上圖可以看出左邊是實例對象,中間是構造函數,右邊是原型對象,圖中還包含了一些其它內容:
函數是一個函數,同時也是一個對象:
構造函數作為一個函數時,擁有原型對象屬性 prototype
同時函數也是一個對象,函數都是由構造函數 Function 構造出來的,包括 Function 函數本身,可以看上圖中 function Foo()function Object()function Function()__proto__ 都是指向 Funtion.prototype,這是函數比較特殊的一點。
這個地方有點繞,再理一下,函數的 prototype 屬性是這個函數自己的屬性,而函數的 __proto__ 指向的是其構造函數的原型對象,可以理解為父級的屬性,兩者是不同的。

總結

  1. JavaScript是基於原型編程,創建對象是通過克隆對象的形式,不是通過類創建。
  2. 函數都擁有 prototype 原型屬性,實例化對象的 __proto__ 屬性指向這個原型屬性,對象可以直接調用原型對象的方法和屬性,不用寫 __proto__ 再調用,兩者效果一致。
  3. 對象的 __proto__ 指向構造函數的 prototype,構造函數的 prototype 同樣是對象,其 __proto__ 指向上一層原型對象,直到 Object.prototype,形成原型鏈。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 應用上下文(Context)是應用程式的全局信息的介面。它是一個抽象類,提供了訪問應用程式環境的方法和資源的方法。應用上下文可以用於獲取應用程式的資源、啟動Activity、發送廣播等。每個應用程式都有一個應用上下文對象,它在整個應用程式的生命周期內都是唯一的。通過應用上下文,我們可以獲得 ...
  • 前言 組件容器是一種用於管理和組織組件的工具或環境。它可以提供一些基本的功能,如組件的註冊、創建、銷毀和查找。組件容器通常會維護一個組件的依賴關係,並負責將這些依賴註入到組件中。它還可以提供一些其他的功能,如生命周期管理、事件通知、配置管理等。通過使用組件容器,開發者可以更方便地管理和使用組件, ...
  • ADB Remote ATV Android TV 的遙控器,基於 ADB Shell 命令 ADB Remote ATV 是一個 Android TV 的遙控器,基於 ADB Shell 命令,泛用性更高。 下麵的 shell 命令,是軟體的基本原理,通過 shell 命令可模擬物理遙控器的基本按 ...
  • 版本控制在軟體開發中至關重要,而 Git 是廣泛使用的代碼管理工具。有時,我們可能需要在多個平臺 (如 GitHub、GitLab 和 Gitee) 上同步同一 Git 倉庫,以便備份、協作等。 本文將帶你玩轉此操作,其中關鍵是“配置 SSH” 和“遠程倉庫”。首先,我們來講述 SSH 的配置。 配 ...
  • 一、卡片數據交互 HarmonyOS卡片數據交互是指在基於鴻蒙操作系統的設備上,卡片界面之間進行數據的傳輸和交互。 HarmonyOS的卡片是一種輕量級的應用界面,可以在設備的屏幕上顯示信息和提供操作功能。卡片可以包含各種類型的內容,如文本、圖片、按鈕、輸入框等,並可以根據用戶的操作進行相應的響 ...
  • 用戶在頁面訪問時發送數據到後臺,頁面關閉時也發送數據到後臺。 第一次進入頁面時觸發頁面訪問 刷新當前頁面時觸發頁面訪問 新 tab 進入頁面時觸發頁面訪問 當前頁面點擊 nav 進入其他模塊時,觸發頁面關閉&頁面訪問 關閉頁面時觸發頁面關閉 ...
  • 本章內容: 行分隔符(U + 2028)和段分隔符(U + 2029)符號現在允許在字元串文字中,與 JSON 匹配 更加友好的 JSON.stringify 新增了 Array 的flat()方法和flatMap()方法 新增了 String 的trimStart()方法和trimEnd()方法 ...
  • 一、區別 前面兩節我們有提到Loader與Plugin對應的概念,先來回顧下 loader 是文件載入器,能夠載入資源文件,並對這些文件進行一些處理,諸如編譯、壓縮等,最終一起打包到指定的文件中 plugin 賦予了 webpack 各種靈活的功能,例如打包優化、資源管理、環境變數註入等,目的是解決 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...