詳解面向對象、構造函數、原型與原型鏈

詳解面向對象、構造函數、原型與原型鏈

?

為了幫助大家能夠更加直觀的學習和了解面向對象,我會用盡量簡單易懂的描述來展示面向對象的相關知識。并且也準備了一些實用的例子幫助大家更加快速的掌握面向對象的真諦。

  • jQuery的面向對象實現
  • 封裝拖拽
  • 簡易版運動框架封裝

這可能會花一點時間,但是卻值得期待。所以如果有興趣的朋友可以來簡書和公眾號關注我。

而這篇文章主要來聊一聊關于面向對象的一些重要的基本功。

一、對象的定義

在ECMAScript-262中,對象被定義為“無序屬性的集合,其屬性可以包含基本值,對象或者函數”

也就是說,在JavaScript中,對象無非就是由一些列無序的key-value對組成。其中value可以是基本值,對象或者函數。

創建對象

我們可以通過new的方式創建一個對象。

也可以通過對象字面量的形式創建一個簡單的對象。

當我們想要給我們創建的簡單對象添加方法時,可以這樣表示。

訪問對象的屬性和方法

假如我們有一個簡單的對象如下:

當我們想要訪問他的name屬性時,可以用如下兩種方式訪問。

如果我們想要訪問的屬性名是一個變量時,常常會使用第二種方式。例如我們要同時訪問person的name與age,可以這樣寫:

?

這種方式一定要重視,記住它以后在我們處理復雜數據的時候會有很大的幫助。

二、工廠模式

使用上面的方式創建對象很簡單,但是在很多時候并不能滿足我們的需求。就以person對象為例。假如我們在實際開發中,不僅僅需要一個名字叫做TOM的person對象,同時還需要另外一個名為Jake的person對象,雖然他們有很多相似之處,但是我們不得不重復寫兩次。

很顯然這并不是合理的方式,當相似對象太多時,大家都會崩潰掉。

我們可以使用工廠模式的方式解決這個問題。顧名思義,工廠模式就是我們提供一個模子,然后通過這個模子復制出我們需要的對象。我們需要多少個,就復制多少個。

相信上面的代碼并不難理解,也不用把工廠模式看得太過高大上。很顯然,工廠模式幫助我們解決了重復代碼上的麻煩,讓我們可以寫很少的代碼,就能夠創建很多個person對象。但是這里還有兩個麻煩,需要我們注意。

第一個麻煩就是這樣處理,我們沒有辦法識別對象實例的類型。使用instanceof可以識別對象的類型,如下例子:

因此在工廠模式的基礎上,我們需要使用構造函數的方式來解決這個麻煩。

三、構造函數

在JavaScript中,new關鍵字可以讓一個函數變得與眾不同。通過下面的例子,我們來一探new關鍵字的神奇之處。

為了能夠直觀的感受他們不同,建議大家動手實踐觀察一下。很顯然,使用new之后,函數內部發生了一些變化,讓this指向改變。那么new關鍵字到底做了什么事情呢。嗯,其實我之前在文章里用文字大概表達了一下new到底干了什么,但是一些同學好奇心很足,總期望用代碼實現一下,我就大概以我的理解來表達一下吧。

?

JavaScript內部再通過其他的一些特殊處理,將var p1 = New(Person, 'tom', 20);?等效于var p1 = new Person('tom', 20);。就是我們認識的new關鍵字了。具體怎么處理的,我也不知道,別刨根問底了,一直回答下去太難 – -!

老實講,你可能很難在其他地方看到有如此明確的告訴你new關鍵字到底對構造函數干了什么的文章了。理解了這段代碼,你對JavaScript的理解又比別人深刻了一分,所以,一本正經厚顏無恥求個贊可好?

當然,很多朋友由于對于前面幾篇文章的知識理解不夠到位,會對new的實現表示非常困惑。但是老實講,如果你讀了我的前面幾篇文章,一定會對這里new的實現有似曾相識的感覺。而且我這里已經盡力做了詳細的注解,剩下的只能靠你自己了。

但是只要你花點時間,理解了他的原理,那么困擾了無數人的構造函數中this到底指向誰就變得非常簡單了。

所以,為了能夠判斷實例與對象的關系,我們就使用構造函數來搞定。

關于構造函數,如果你暫時不能夠理解new的具體實現,就先記住下面這幾個結論吧。

  • 與普通函數相比,構造函數并沒有任何特別的地方,首字母大寫只是我們約定的小規定,用于區分普通函數;
  • new關鍵字讓構造函數具有了與普通函數不同的許多特點,而new的過程中,執行了如下過程:
    1. 聲明一個中間對象;
    2. 將該中間對象的原型指向構造函數的原型;
    3. 將構造函數的this,指向該中間對象;
    4. 返回該中間對象,即返回實例對象。

四、原型

雖然構造函數解決了判斷實例類型的問題,但是,說到底,還是一個對象的復制過程。跟工廠模式頗有相似之處。也就是說,當我們聲明了100個person對象,那么就有100個getName方法被重新生成。

這里的每一個getName方法實現的功能其實是一模一樣的,但是由于分別屬于不同的實例,就不得不一直不停的為getName分配空間。這就是工廠模式存在的第二個麻煩。

顯然這是不合理的。我們期望的是,既然都是實現同一個功能,那么能不能就讓每一個實例對象都訪問同一個方法?

當然能,這就是原型對象要幫我們解決的問題了。

我們創建的每一個函數,都可以有一個prototype屬性,該屬性指向一個對象。這個對象,就是我們這里說的原型。

當我們在創建對象時,可以根據自己的需求,選擇性的將一些屬性和方法通過prototype屬性,掛載在原型對象上。而每一個new出來的實例,都有一個__proto__屬性,該屬性指向構造函數的原型對象,通過這個屬性,讓實例對象也能夠訪問原型對象上的方法。因此,當所有的實例都能夠通過__proto__訪問到原型對象時,原型對象的方法與屬性就變成了共有方法與屬性。

我們通過一個簡單的例子與圖示,來了解構造函數,實例與原型三者之間的關系。

由于每個函數都可以是構造函數,每個對象都可以是原型對象,因此如果在理解原型之初就想的太多太復雜的話,反而會阻礙你的理解,這里我們要學會先簡化它們。就單純的剖析這三者的關系。

?

?

?

圖示

通過圖示我們可以看出,構造函數的prototype與所有實例對象的__proto__都指向原型對象。而原型對象的constructor指向構造函數。

除此之外,還可以從圖中看出,實例對象實際上對前面我們所說的中間對象的復制,而中間對象中的屬性與方法都在構造函數中添加。于是根據構造函數與原型的特性,我們就可以將在構造函數中,通過this聲明的屬性與方法稱為私有變量與方法,它們被當前被某一個實例對象所獨有。而通過原型聲明的屬性與方法,我們可以稱之為共有屬性與方法,它們可以被所有的實例對象訪問。

當我們訪問實例對象中的屬性或者方法時,會優先訪問實例對象自身的屬性和方法。

在這個例子中,我們同時在原型與構造函數中都聲明了一個getName函數,運行代碼的結果表示原型中的訪問并沒有被訪問。

我們還可以通過in來判斷,一個對象是否擁有某一個屬性/方法,無論是該屬性/方法存在與實例對象還是原型對象。

in的這種特性最常用的場景之一,就是判斷當前頁面是否在移動端打開。

更簡單的原型寫法

根據前面例子的寫法,如果我們要在原型上添加更多的方法,可以這樣寫:

除此之外,我還可以使用更為簡單的寫法。

這種字面量的寫法看上去簡單很多,但是有一個需要特別注意的地方。Person.prototype = {}實際上是重新創建了一個{}對象并賦值給Person.prototype,這里的{}并不是最初的那個原型對象。因此它里面并不包含constructor屬性。為了保證正確性,我們必須在新創建的{}對象中顯示的設置constructor的指向。即上面的constructor: Person

四、原型鏈

原型對象其實也是普通的對象。幾乎所有的對象都可能是原型對象,也可能是實例對象,而且還可以同時是原型對象與實例對象。這樣的一個對象,正是構成原型鏈的一個節點。因此理解了原型,那么原型鏈并不是一個多么復雜的概念。

我們知道所有的函數都有一個叫做toString的方法。那么這個方法到底是在哪里的呢?

先隨意聲明一個函數:

那么我們可以用如下的圖來表示這個函數的原型鏈。

?

原型鏈

其中foo是Function對象的實例。而Function的原型對象同時又是Object的實例。這樣就構成了一條原型鏈。原型鏈的訪問,其實跟作用域鏈有很大的相似之處,他們都是一次單向的查找過程。因此實例對象能夠通過原型鏈,訪問到處于原型鏈上對象的所有屬性與方法。這也是foo最終能夠訪問到處于Object原型對象上的toString方法的原因。

基于原型鏈的特性,我們可以很輕松的實現繼承

五、繼承

我們常常結合構造函數與原型來創建一個對象。因為構造函數與原型的不同特性,分別解決了我們不同的困擾。因此當我們想要實現繼承時,就必須得根據構造函數與原型的不同而采取不同的策略。

我們聲明一個Person對象,該對象將作為父級,而子級cPerson將要繼承Person的所有屬性與方法。

首先我們來看構造函數的繼承。在上面我們已經理解了構造函數的本質,它其實是在new內部實現的一個復制過程。而我們在繼承時想要的,就是想父級構造函數中的操作在子級的構造函數中重現一遍即可。我們可以通過call方法來達到目的。

而原型的繼承,則只需要將子級的原型對象設置為父級的一個實例,加入到原型鏈中即可。

?

?

原型鏈

當然關于繼承還有更好的方式,這里就不做深入介紹了,以后有機會再詳細解讀吧。

六、總結

關于面向對象的基礎知識大概就是這些了。我從最簡單的創建一個對象開始,解釋了為什么我們需要構造函數與原型,理解了這其中的細節,有助于我們在實際開發中靈活的組織自己的對象。因為我們并不是所有的場景都會使用構造函數或者原型來創建對象,也許我們需要的對象并不會聲明多個實例,或者不用區分對象的類型,那么我們就可以選擇更簡單的方式。

我們還需要關注構造函數與原型的各自特性,有助于我們在創建對象時準確的判斷我們的屬性與方法到底是放在構造函數中還是放在原型中。如果沒有理解清楚,這會給我們在實際開發中造成非常大的困擾。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279014.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279014.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279014.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何將Wii遙控器用作陀螺儀鼠標

If you have a spare Nintendo Wii remote with the Motion Plus add-on, you can use it to control your Windows PC from across the room. Here’s how to get it working in a couple of easy steps. 如果您有帶Motion Plus附加組件的備用Nintendo Wii遙控器,則…

68.iOS設備尺寸及型號代碼(iPhoneXR/XS)

所有設備型號官網地址: https://www.theiphonewiki.com/wiki/Models iPhone: 機型像素比例像素密度屏幕尺寸機型代碼發布日期iPhone 2g4803203:2163ppi3.5iPhone1,12008.01iPhone 3g4803203:2163ppi3.5iPhone1,22008.06iPhone 3gs4803203:2163ppi3.5iPhone2,12009.0…

php 自帶 web server 如何重寫 rewrite .htaccess

為什么80%的碼農都做不了架構師&#xff1f;>>> <?php // filename: route.php if (preg_match(/\.(?:png|jpg|jpeg|gif|css|js)$/, $_SERVER["REQUEST_URI"])) {return false; } else {include __DIR__ . /index.php; } 更好的寫法&#xff1a; &l…

oracle11g導入錯誤,oracle 11g導入到10g引起的錯誤

環境介紹老環境新環境操作系統&#xff1a;redhat5.8 64位redhat6.4 64位數據庫版本&#xff1a;oracle 10.2.0.4 64位oracle 11.2.0.4 64位背景&#xff1a;之前有一套老的數據庫rac是基于oracle10g搭建&#xff0c;跑了幾年了。現在前端應用程序準備升級&#xff0c;考慮到前…

sci-hub谷歌插件_Google Home Hub具有隱藏屏幕設置菜單

sci-hub谷歌插件You can adjust the brightness or set an alarm on your Google Home Hub with a voice command. But if you’re trying to be quiet or there’s a lot of background noise, you can also do these things using a hidden Screen Settings menu. 您可以使用…

二叉樹的前序、中序、后序遍歷與創建

#include <iostream> #include <string> #include <stack> using namespace std; struct node; typedef node *pNode; struct node { char data; pNode left, right; }; string line; string::iterator it; // 前序擴展序列建立二叉樹 void plan…

flex-2

1、 2、 justify&#xff1a;整理版面 3、 4、歸納 justify-content&#xff1a;flex-start&#xff08;默認&#xff09;、center、flex-end 下面還會提到剩下的兩種項目在主軸上對齊方式&#xff1a; space-between:兩端對齊&#xff08;項目間距離相等&#xff09; space-ar…

TextInput

TextInput /** TextInput 是一個允許用戶在應用中通過鍵盤輸入文本的基本組件* 本組件的屬性提供了多種特性的配置,如自動完成,自動大小寫,占位文字,鍵盤類型等* 常用:* placeholder 占位符* value 輸入框的值* password 是否密文輸入* editable 是否可編輯* retureKeyType …

如何使用oracle查詢,oracle 表查詢

Oracle 的 oracle 表查詢通過scott用戶下的表來演示如何使用select語句&#xff0c;接下來對emp、dept、salgrade表結構進行解說。emp 雇員表字段名稱 數據類型 是否為空 備注-------- ----------- -------- --------EMPNO NUMBER(4) 員工編…

火狐標簽在中間_在Firefox中保留未使用的標簽

火狐標簽在中間If you have a lot of content heavy webpages open in Firefox, it soon adds up on memory usage. The BarTab extension puts unused tabs on hold and keeps them unloaded until you are ready to access them. 如果您在Firefox中打開了大量內容繁重的網頁&…

[CQOI2012]模擬工廠 題解(搜索+貪心)

[CQOI2012]模擬工廠 題解(搜索貪心) 標簽&#xff1a;題解 閱讀體驗&#xff1a;https://zybuluo.com/Junlier/note/1327574 鏈接題目地址&#xff1a;洛谷P3161 BZOJ P2667 這個題練一練綜合思想還是不錯的。。。&#xff08;然而蒟蒻不會啊&#xff09; 做法 肯定是在能完成某…

上傳文件的input問題以及FormData特性

1.input中除了type"file"還要加上name"file"&#xff0c;否則$_FILES為空&#xff0c;input的name值就是為了區分每一個input的 2.var formdata new FormData($("#form")[0]); 或者var formdata new FormData(document.getElementById("f…

iphone手機備忘錄遷移_如何在iPhone和iPad上使用語音備忘錄

iphone手機備忘錄遷移Whether you’re recording a voice message as a reminder of that million dollar idea or catching a snippet of a new song you know you’ll forget, the iPhone and iPad’s Voice Memos app is the perfect tool. 無論您是錄制語音消息來提醒這一百…

php 執行文件tar打包,利用tar for windows對大量文件進行快速打包

近期將某些網站換服務器&#xff0c;由于網站數量巨大&#xff0c;加上附件和靜態頁&#xff0c;文件數量異常多&#xff0c;考慮先打包然后直接傳過去。起初嘗試用winrar打包&#xff0c;但是發現即使選擇”僅儲存”速度仍然慢到無法接受&#xff0c;后來想到了tar&#xff0c…

DDD~領域事件中使用分布式事務

對于一個聚合來說&#xff0c;它可能會被附加很多事件&#xff0c;這里我們叫它領域事務&#xff0c;因為一個聚會我們可以把它理解成一個領域&#xff0c;一個業務。對于領域事件不清楚的同學可以看看我的這篇文章《DDD~領域事件與事件總線》&#xff0c;里面有詳細的說明&…

如何在PowerPoint中制作打字機或命令行動畫

Adding quirky animations to your Microsoft PowerPoint presentation gives your slideshow a little extra life. Not only will adding a typewriter or command line animation entertain your audience, but it will also keep them focused on the text. 在您的Microsof…

oracle中spool卸數,數據卸載--spool的使用

&#xfeff;&#xfeff;引言在項目中&#xff0c;我們經常會遇到數據的卸載、裝載需求。卸載就是需要將數據從數據庫中導入到文本文件中的需求&#xff0c;這樣的方法有很多&#xff0c;比較常用的就是spool命令。裝載就是需要將數據從文本文件中導入到數據庫中。方法也有很多…

Objective-C中的@property

1.property是什么 Property是聲明屬性的語法&#xff0c;它可以快速方便的為實例變量創建存取器&#xff0c;并允許我們通過點語法使用存取器。 存取器&#xff08;accessor&#xff09;&#xff1a;指用于獲取和設置實例變量的方法。用于獲取實例變量值的存取器是getter&#…

Linux基礎命令---findfs

findfs 查找指定卷標或者UUID的文件系統對應的設備文件。findfs將搜索系統中的磁盤&#xff0c;尋找具有標簽匹配標簽或與UUID相等的文件系統。如果找到文件系統&#xff0c;文件系統的設備名稱將打印在stdout上。 此命令的適用范圍&#xff1a;RedHat、RHEL、Ubuntu、CentOS、…

canvas 平滑運動_什么是電視上的運動平滑?人們為什么討厭它?

canvas 平滑運動Willy Barton/Shutterstock.com威利巴頓/Shutterstock.comIf you’ve just bought a new TV, you might be wondering why everything you watch feels eerily sped up and smooth, like you’re watching a live broadcast all the time. You’re not imaginin…