【JavaScript】理解原型與原型鏈

来源:https://www.cnblogs.com/chenzilin/archive/2020/05/01/12814190.html
-Advertisement-
Play Games

前言 理解原型和原型鏈,有助於更好的理解JavaScript中的繼承機制。 最近比較有空,所以想寫一篇關於原型和原型鏈的文章,如寫得不好請見諒。 原型對象 無論什麼時候,只要創建了一個新函數,就會根據一組特定的規則為該函數創建一個 prototype 屬性,這個屬性指向函數的原型對象。在預設情況下, ...


前言

理解原型和原型鏈,有助於更好的理解JavaScript中的繼承機制。

最近比較有空,所以想寫一篇關於原型和原型鏈的文章,如寫得不好請見諒。

原型對象

無論什麼時候,只要創建了一個新函數,就會根據一組特定的規則為該函數創建一個prototype屬性,這個屬性指向函數的原型對象。在預設情況下,所有的原型對象都會自動獲得一個constructor屬性,這個屬性是一個指向prototype屬性所在函數的指針。

function Person(name) {
      this.name = name
    }

Person.prototype.age = 18
Person.prototype.sayName = function() {
  alert(this.name)
}

var person = new Person('張三')
person.sayName() // 張三

上述我們創建了一個Person構造函數,並創建了一個name實例和原型對象的age屬性和sayName方法,接下來new實例對象都擁有這些屬性方法

1

Person構造函數的prototype屬性指向原型對象,person實例的[[prototype]]指向原型對象(在chrome瀏覽器中是__proto__),我們可以通過isPrototypeOf()方法來確認對象之間是否存在原型關係。ES5新增了一個新方法,Object.PrototypeOf(),這個方法返回[[prototype]]的值

alert(Peron.prototype.isPrototypeOf(person))  // true

alert(Object.PrototypeOf(person) == Person.prototype) // true 
alert(Object.PrototypeOf(peroson).name // '張三'

當我們訪問一個實例對象的某個屬性時,如果在實例中找到具有給定名字的屬性,則返回屬性的值;如果沒有找到,則繼續搜索指針指向的原型對象,在原型對象中查找具有給定名字的屬性,如果在原型對象中找到具有給定名字的屬性,則返回該屬性的值

看下麵這段代碼

function Person() {
}

Person.prototype.name = 'zhangsan';
Person.prototype.age = 18;
Person.prototype.sayName = function() { alert(this.name) };

var person1 = new Person();
var person2 = new Person();

person1.name = 'lisi';
console.log(person1.name); // 'lisi'——來自實例
console.log(person2.name); // 'zhangsan'——來自原型
console.log(person1.hasOwnProperty('name')) // true

delete person1.name; // 'zhangsan'——來自原型
console.log(person1.name);

console.log(person1.hasOwnProperty('name')) // false
console.log(person2.hasOwnProperty('name')) // false

在這個例子中,person1的name被一個新值給屏蔽了。當為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性。即使將這個屬性設置為null,也只會在實例中設置這個屬性,而不會恢復其指向原型的連接。不過可以使用delete操作符刪除實例屬性,從而讓我們重新訪問原型中的屬性。

使用hasOwnProperty()方法,可以檢測屬性是否來自實例,只在給定屬性存在於對象實例中時,才會返回true

我們還可以通過定義函數封裝一個檢測該屬性到底存在於對象中,還是存在於原型中的方法

function hasProtoypeProperty(object, name) {
  return !object.hasProperty(name) && (name in object)
}

function Person() {
}

Person.prototype.name = 'zhangsan';
Person.prototype.age = 18;
Person.prototype.sayName = function() { alert(this.name) };

var person = new Person();
console.log(hasProtoypeProperty(person, 'name')); // true

person.name = 'lisi'
console.log(hasProtoypeProperty(person, 'name')); // false

很多同學可能註意到前面例子每添加一個屬性和方法就要敲一遍Person.prototype。為減少不必要的輸入,我們可以使用一個包含所有屬性和方法的對象字面量來重寫整個原型對象

function Person() {}

Person.prototype = {
  name: 'zhangsan',
  age: 19,
  sayName: function() {
    console.log(this.name)
  }
}

上述代碼中有一個問題,Person.prototype中的constructor屬性不再指向Person,此時我們需要手動將constructor屬性指向Person

function Person() {}

Person.prototype = {
  constructor: Person,
  name: 'zhangsan',
  age: 19,
  sayName: function() {
    console.log(this.name)
  }
}

var person = new Person();
for(var val in person) {
  console.log(val); // 'constructor','name','age','sayName'
}

以這種方式重設constructor屬性會導致它的[[Enumerable]]特性被設置為true,導致可以通過for-in遍歷出來,最好的辦法是通過ES5中的Object.defineProperty()方法設置constructor屬性

function Person() {}

Person.prototype = {
  name: 'zhangsan',
  age: 19,
  sayName: function() {
    console.log(this.name)
  }
}

Object.defineProperty(Person.prototype, 'constructor', {
  enumerable: false,
  value: Person
})

原型還有一個特點,是具有動態性,即我們對原型對象所做的修改都能夠立即從實例上反映出來,即使是先創建實例後修改原型對象也是如此

function Person() { }

var person1 = new Person();

Person.prototype = {
  name: 'zhangsan',
  age: 19,
  sayName: function () {
    console.log(this.name)
  }
}

Object.defineProperty(Person.prototype, 'constructor', {
  enumerable: false,
  value: Person
})

person1.sayName(); // Uncaught TypeError: person.sayName is not a function

var person2 = new Person();
person2.sayName(); // zhangsan

前面提過,調用構造函數時會為實例添加一個指向最初原型的[[prototype]]指針,上述代碼中,重寫原型對象切斷了現有原型與任何之前已經存在的對象實例之間的聯繫。實例中的指針指向原型,而不指向構造函數


原型鏈

在JavaScript中,原型鏈是實現繼承的主要的方法,理解原型鏈有助於我們理解JavaScript中的繼承機制。

原型鏈,其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法,我們都知道,每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針(constructor),實例對象都包含一個指向原型對象的內部指針(__proto__),讓原型對象等於另一個類型的實例,此時原型對象將包含一個指向另一個原型的指針,假如另一個原型又是另外一個類型的實例,如此層層遞進,就構成了實例與原型的鏈條,簡稱原型鏈。

在JavaScript中,每個實例對象都有一個__proto__屬性指向它的構造函數的原型對象prototype,原型對象也有一個自己的原型對象__proto__,層層向上直到一個對象的原型對象為null幾乎所有JavaScript中的對象都是位於原型鏈頂端的Object的實例。

function Father() {
  this.name = 'zhangsan';
}

Father.prototype.sayName = function() {
  console.log(this.name);
}

function Son() {
  this.age = 19
}

Son.prototype = new Father();

var xiaoming = new Son();
xiaoming.sayName(); // 'zhangsan'

上述代碼中,讓Son構造函數的原型指向Father構造函數的實例,此時就實現了JavaScript中最簡單的繼承原理。讓我們通過下圖來更好的理解原型鏈。

2

上圖很清晰的描繪了原型鏈中的繼承機制,對象最頂層Object的原型的原型是指向一個null。註意,在為原型添加方法的代碼一定要放在替換原型的語句之後,也不能通過對象字面量創建原型方法,因為這樣做就會重寫原型鏈。

function Father() {
  this.name = 'zhangsan';
}

Father.prototype.sayName = function() {
  console.log(this.name);
}

function Son() {
  this.age = 19
}

Son.prototype = new Father();
Son.prototype = {
  sayAge: function() {
    console.log(this.age)
  }
}

var xiaoming = new Son();
xiaoming.sayName(); // Uncaught TypeError: xiaoming.sayName is not a function

使用對象字面量的方式到導致FatherSon之間的關係被切斷,因此最後調用sayName()方法就會報錯。

總結

以上就是我對原型原型鏈的理解,原型鏈雖然強大,但它也存在一些問題,最主要的問題就是包含引用類型值的原型屬性會被所有實例共用,本文只是對原型和原型鏈進行分析,關於繼承問題不在本文的討論範圍之內,這是cc的第一篇文章,也算是小白文章,如寫得不好請指出,我會及時更正,謝謝大家~


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

-Advertisement-
Play Games
更多相關文章
  • 會話技術 1. 會話:一次會話中包含多次請求和響應 一次會話:瀏覽器第一次給伺服器發送請求,會話建立,直到有一方斷開為止 2. 功能:在一次會話的範圍內的多次請求間,共用數據 3. 方式: 1. 客戶端會話技術:Cookie 2. 伺服器端會話技術:Session Cookie 1. 概念:客戶端會 ...
  • 1. Image.open(fp, mode="r") 調用此方法需要引入頭文件:from PIL import Image。 參數說明: fp:圖片路徑,可為絕對路徑或相對路徑。 model:預設即可。 2. 例子 2.1 Code 首先給定圖片路徑,然後調用函數Image.open()即可。 1 ...
  • 一、準備環境 1. 雲主機和功能變數名稱 雲主機 推薦使用阿裡雲或者騰訊雲的ecs主機,如果有學生證的話都很便宜。 功能變數名稱 國內的話,在阿裡雲或者騰訊雲購買都可以,國內功能變數名稱都需要備案,備案按照文檔去做。 ssl證書 雲服務商一般都有免費的ssl證書申請,也可以使用let's encrypt的證書。 2. 公眾 ...
  • 1. cv2.imread(filename, flags=None) 需要引入頭文件: import cv2 參數說明: filename: 文件路徑,絕對路徑和相對路徑都可以。 2. 例子 2.1 Code 首先定義路徑,然後直接調用函數cv2.imread()即可。 1 import cv2 ...
  • 1. repeat_interleave(self: Tensor, repeats: _int, dim: Optional[_int]=None) 參數說明: self: 傳入的數據為tensor repeats: 複製的份數 dim: 要複製的維度,可設定為0/1/2..... 2. 例子 2 ...
  • SpringBoot(十二)過濾器詳解 往期精彩推薦 "SpringBoot系列(一)idea新建Springboot項目" "SpringBoot系列(二)入門知識" "springBoot系列(三)配置文件詳解" "SpringBoot系列(四)web靜態資源配置詳解" "SpringBoot系 ...
  • 1. MQ的優缺點 優點: 解耦:通過MQ解除上游系統和下游系統的調用耦合,下游系統只需要做消息的訂閱和取消訂閱,上游系統無需任何改動。(一生產,多消費的典型場景) 非同步:通過MQ將一些不需要同步獲取執行的結果,並且非常耗時的調用操作通過MQ非同步化。 削峰:通過MQ將一些高峰期的高併發流量積壓在MQ ...
  • maven settings.xml 每次重裝maven都需要配置settings.xml所以在這裡記錄一下 win linux 【微信】打賞二維碼: 【支付寶】打賞二維碼: ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...