主頁 > 企業開發 > 好好理解一下JavaScript中的原型

好好理解一下JavaScript中的原型

2020-10-10 10:51:36 企業開發

目錄

Table of Contents generated with DocToc

  • 目錄
    • 一、參考書籍和資料
    • 二、原型,[[prototype]]和.prototype以及constructor
    • 三、原型鏈
      • for...in和in運算子
    • 四、屬性設定和屏蔽
    • 五、JavaScript只有物件
    • 六、建構式和new關鍵字
    • 七、模仿類
    • 八、對constructor的錯誤理解
    • 九、原型繼承
    • 十、類之間的關系
    • 十一、總結
    • 有問題就留言交流我很樂意

一、參考書籍和資料

翻看了幾本JS書籍,其中主要有以下幾本:《JavaScript高級程式設計第三版》、《你不知道的JavaScript卷一》、《JavaScript權威指南》以及查看了MDN檔案,文章主要說了JavaScript中原型的一些概念知識,花了一點時間去總結,如任何問題的話可以提出來一起交流解決,文章中的圖大多是從網路和書中截取下來,并非本人原創,

二、原型,[[prototype]]和.prototype以及constructor

結合書中的概念,原型是什么這個問題,可以這樣去解釋:原型就是一個參考(也就是指標),指向原型物件,這并不是廢話,很多人說原型,實際上沒意識到它只是一個參考,指向原型物件,原型在實體物件和建構式中有不同的名稱屬性,但總是指向原型物件,如圖所示:

[[prototype]]和.prototype以及constructor
- 其中的constructor是原型物件的屬性,參考的是物件關聯的函式,不可列舉,但這個屬性是可以修改的,因此不可靠, - 在實體物件中,原型就是物件的`[[prototype]]`內置屬性(雙方括號代表這是JavaScript引擎內部使用的屬性/方法,正常JS代碼無法訪問,但可以通過`__proto__`訪問到,后面會說到),在物件被創建時就包含了該屬性,指向它的建構式的原型物件, - 在函式中,原型就是函式的`.prototype`屬性,在函式被創建時就包含該屬性,指向建構式的原型物件 ,

三、原型鏈

要理解原型鏈,首先需要明白原型物件的作用就是讓所有實體物件共享它的屬性和方法,根據上圖,不難發現,person1和person2中的內部屬性[[prototype]]都指向Person原型物件,當進行物件屬性查找的時候,比如person1.name,首先會檢查物件本身是否有這個屬性,如果沒有就繼續去查找該物件[[prototype]]指向的原型物件中是否有該屬性,如果還是沒有就繼續去找這個原型物件的[[prototype]]指向的原型物件(注意,原型物件也是有他自己的[[prototype]]屬性的)!這個程序會持續找到匹配的屬性名或查找完整的原型鏈,不難理解了,原型鏈就是:每個實體物件( object )都有一個私有屬性(稱之為[[prototype]])指向它的建構式的原型物件(prototype ),該原型物件也有一個自己的原型物件( [[prototype]] ) ,層層向上直到一個物件的原型物件為Object.prototype(因為所有物件都是源于Object.prototype,其中包含許多通用的功能方法),顯然,如果找完這個原型鏈都找不到就會回傳undefined,這個程序可以用一張圖描述:

顯然,原型和原型鏈的作用就是:如果物件上沒有知道需要的屬性和方法參考,JS引擎就會繼續在[[prototype]]關聯的物件上進行查找,這也是原型和原型鏈存在的意義,

for...in和in運算子

兩個跟原型鏈有關的操作

  • for...in遍歷物件時,任何可以通過原型鏈訪問到的(并且是enumerable為true)屬性都會被列舉,
  • in運算子用于檢測驗性在物件中是否存在,同樣是會查找整條原型鏈,
function Person(name){
  this.name = name;
}
Person.prototype.sayName = function() {
  return this.name;
}
let myObject = new Person('練習生');
// 輸出兩個屬性:name和sayName,其中sayName是原型物件中的屬性
for(let key in myObject) {
  console.log(key);
}
// 輸出true,表示不可列舉的constructor存在于myObject中,
// 事實上constructor是在Person.prototype物件中
console.log("constructor" in myObject);

四、屬性設定和屏蔽

給物件設定屬性并不僅僅是添加一個屬性或修改已有屬性,這個程序應該是這樣的:

// myObject的宣告在第一個代碼塊

// 注意:sayName在Person.prototype中存在,將屏蔽原型鏈上的sayName方法
myObject.sayName = function() {
  return `my name is:${this.name}`;
}
// 注意:age在myObject的整個原型鏈都不存在,將在實體中新建age屬性
myObject.age = 23;

// 完成上述對myObject屬性的設定,再新建一個物件
let myObject_1 = new Person('James');

// 查找myObject的屬性和方法
myObject.age; //23
myObject.sayName(); // my name is: Bob

// 查找myObject_1的屬性和方法
myObject.age; // undefined
myObject.sayName(); // 'Cat'

直接設定實體屬性,都會屏蔽原型鏈上的所有同名屬性(前提是屬性的writable為 true,并且屬性沒有setter),并有以下兩種情況:

  • 當sayName屬性不直接存在物件中而存在于原型鏈上層時,將會在myObjet中直接添加sayName屬性,注意它只會阻止訪問原型鏈上層的sayName屬性,但不會修改按個屬性,
  • 當原型鏈上找不到age,則age直接添加到myObject中,

五、JavaScript只有物件

在面向物件語言中,類是可以被實體化多次,就像使用模具制作東西一樣,對于每一個實體都會重復這個程序,但在JavaScript中,沒有類,沒有復制機制,只能創建多個物件,通過它們的內置[[prototype]]關聯同一個原型物件,默認情況下,它們是關聯的,并非復制,因為是同一個原型物件所以它們之間也不會完全失去聯系,

比如說,new Person()生成一個物件,同時這個新物件的內置[[prototype]]關聯的是Person.prototype物件,這里得到了兩個物件,它們之間僅僅互相關聯,并沒有初始化類,如圖所示:

這種機制也就是所謂的原型繼承,這種Person()函式不算是類,它只是利用了函式的prototype屬性“模仿類”而已!所以說,JavaScript沒有類只有物件,

六、建構式和new關鍵字

文章第一個代碼塊很容易讓人認為Person是一個建構式,因為使用new呼叫并看到他構造了一個物件,但其實Person跟其他普通函式沒有什么不同,函式本身不是建構式,所有的一切只是在函式呼叫前加了new關鍵字!這樣就會把這個函式呼叫變成一個“建構式呼叫”,new會劫持所有普通函式并用構造物件的形式去呼叫它,下面這段代碼可以證明這點:

function BaseFunction() {
  console.log('Not a constructor!');
}
let myObject = new BaseFunction();
// Not a constructor.
typeof myObject; // object

BaseFunction是一個普通函式并非建構式,但通過new呼叫,卻會構造出一個物件,因此,建構式其實是所有帶new的函式呼叫,

七、模仿類

前面已經明確說過,JavaScript中只有物件,沒有真正的類,但JavaScript開發者通過下面兩種方法可以模擬類,如下代碼所示:

function Foo(name) {
  this.name = name;
}
Foo.prototype.myName = function() {
  return this.name;
}
let a = new Foo('a');
let b = new Foo('b');

a.myName(); // a
b.myName(); // b
  • this.name = name 給每一個new呼叫構造出來的物件都添加了.name屬性(this系結當前物件),這有點類似面向物件中“類實體封裝的資料值”,
  • Foo.prototype.myName = ...,給原型物件添加方法,那么通過該建構式呼叫創建的實體就能共享原型物件的方法和屬性,因此,a.myName和b.myName都可以正常作業,這有點類似面向物件中的什么?這點我還不知道,反正就是面向物件設計模式的一種,有知道的可以留言告訴我,

八、對constructor的錯誤理解

接上面的代碼所示,如果繼續運行a.constructor === Foo,回傳的是true,因此有這種錯誤觀點:物件由Foo構造,現在是時候把這個錯誤觀點改過來了,constructor是存在于Foo.prototype中,a物件只是[[prototype]]委托找到constructor!這和構造毫無關系,下面代碼可以證明這一點:

function Foo(){}
//將Foo的原型物件指向一個空物件
Foo.prototype = {};
let a = new Foo();
a.constructor === Foo; //false
a.constructor === Object; // true

嗯哼?現在你還敢說constructor表示a由Foo構建嗎?按照這種錯誤觀點,a.constructor === Foo應該回傳true!其實constructor在只是創建函式時一個默認屬性,指向prototype屬性所在的函式,constructor屬性時可以被修改的,讓原型物件指向新的物件的時候,為了讓constructor指向之前的函式,可以手動使用defineProperty方法添加一個不可列舉constructor屬性,但真的很麻煩,總而言之不要太信任constructor屬性!

九、原型繼承


從這張圖,可看出三點

  • a1/a2到Foo.prototype,b1/b2到Bar.prototype的委托關聯
  • Bar.Prototype到Foo.prototype的委托關聯
  • 箭頭由下到上表明這是委托關聯而不是復制操作,否則如果是復制操作箭頭應該回事由上往下,
    下面這段代碼是典型的原型繼承風格
function Foo(name){
  this.name = name;
}
Foo.prototype.myName = function() {
  return this.name;
}
function Bar(name, label) {
  Foo.call(this, name);
  this.label = label;
}
// 將新的Bar原型物件和Foo的原型物件進行關聯
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.myLabel = function() {
  return this.label;
}
let a = new Bar("a", "obj a");
a.myName();
a.myLabel();
  • 上面代碼中,Bar.prototype = Object.create(Foo.prototype)表示創建新的Bar.prototype物件并關聯到Foo.Prototype中,注意,這其實是把舊的Bar.prototype物件拋棄掉,再參考新的已關聯到Foo.prototype的物件,
  • ES6新增Object.setPrototypeOf(obj1, obj2),表示直接將obj1的[[prototype]]關聯到為obj2,

以下兩行代碼都是錯誤的物件關聯做法:

Bar.prototype = Foo.prototype;

Bar.prototype = new Foo();
  • 第一行代碼只是讓Bar的原型物件直接參考Foo的原型物件,如果對Bar.prototype的屬性進行修改,則會影響到Foo.prototype本身,
  • 第二行代碼,在《JavaScript高級程式設計第三版》的示例代碼出現,一開始覺得沒問題,后來在《你不知道的JavaScript》中,它指出是錯誤的做法,原因是Foo函式如果會有一些副作用(比如給this添加資料就很不好),會影響到Bar()的實體,

十、類之間的關系

檢查一個實體和祖先通常稱為反射或內省,在JavaScript中通常用到

  • 使用a instanceof Foo運算子,instanceof表示的是:在物件a的原型鏈上是否有指向Foo.prototype的物件,注意,instanceof的左側是物件,右側是函式,
  • 使用a.isPrototypeOf(b),isPrototypeOf表示的是:在物件a的整條原型鏈上是否出現過b,
  • 使用Object.getPrototypeOf(a),可以直接得到一個物件a的原型鏈,

十一、總結

這里例舉幾點比較重要的概念:

  1. 進行物件屬性查找,首先會在當前物件查找,如果沒有就會繼續去查找內置[[prototype]]關聯的物件,這個原型鏈會一直到Object.prototype,如果還是找不到就回傳undefined,
  2. 建構式只是函式,沒有任何區別,使用new呼叫函式就是建構式呼叫,
  3. JavaScript沒有類,默認下不會復制,物件之間通過[[prototype]]進行關聯,物件關聯是原型中很重要的概念!

有問題就留言交流我很樂意

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/165956.html

標籤:JavaScript

上一篇:設計模式

下一篇:DOMContentLoaded vs jQuery.ready vs onload, How To Decide When Your Code Should Run

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more