物件原型- 學習該如何開發Web | MDN

文章推薦指數: 80 %
投票人數:10人

JavaScript 的物件即透過原型(Prototype) 機制相互繼承功能,且與典型的物件導向(OO) 程式語言相較,其運作方式有所差異。

我們將透過本文說明相異之 ... SkiptomaincontentSkiptosearchSkiptoselectlanguage學習該如何開發WebJavaScript—動態的客戶端指令JavaScript物件介紹物件原型ArticleActions正體中文(繁體)ThispagewastranslatedfromEnglishbythecommunity.LearnmoreandjointheMDNWebDocscommunity.「原型」架構的程式語言?了解原型物件原型屬性也定義所要繼承的成員再次溫習create()建構子的屬性修改原型摘要RelatedTopics 全新手請從這開始! Web入門 Web概述 安裝基本軟體 自己的網站會是什麼樣子? 處理檔案 HTML基礎概念 CSS基礎概念 JavaScript基礎概念 發佈自己的網站 Web運作的方式 HTML—架構Web HTML介紹 HTML概述 HTML入門 標題裡是什麼?HTML中的後設資料(Metadata) HTML文字基礎概念 建立超連結 進階文字格式 文件與網站架構 HTML除錯 親和度:設個字母 親和度:架構出具備內容的網頁 多媒體與嵌入 多媒體與嵌入的概述 HTML中的圖片 視訊與音訊內容 從物件到iframe—其他嵌入技巧 為Web新增向量圖 適應性圖片 親和度:Mozilla形象頁面 HTML表格 HTML表格概述 HTML表格基礎 HTMLtableadvancedfeaturesandaccessibility Assessment:Structuringplanetdata CSS—設計Web的風格 初探CSS 初探CSS(概述) CSS是什麼? CSS入門 HowCSSisstructured CSS怎麼運作 Assessment:Stylingabiographypage CSS組件 CSSbuildingblocksoverview Cascadeandinheritance CSSselectors Theboxmodel Backgroundsandborders Handlingdifferenttextdirections Overflowingcontent CSSvaluesandunits SizingitemsinCSS Images,media,andformelements Stylingtables DebuggingCSS OrganizingyourCSS Assessment:FundamentalCSScomprehension Assessment:Creatingfancyletterheadedpaper Assessment:Acool-lookingbox 樣式化文字 樣式化文字概述 基礎的文字與字型樣式化 樣式化列表 樣式化連結 Web字型 親合度:設定社區大學首頁的版面 CSS版面配置 CSS版面配置概述 IntroductiontoCSSlayout NormalFlow 彈性區塊 格線 浮動 定位 Multiple-columnlayout Responsivedesign Beginner'sguidetomediaqueries Legacylayoutmethods Supportingolderbrowsers Assessment:Fundamentallayoutcomprehension JavaScript—動態的用戶端指令 JavaScript第一步 JavaScript第一步概述 什麼是JavaScript? 初次接觸JavaScript 出了什麼問題?JavaScript疑難排解 儲存你所需的資訊—變數 JavaScript基礎概念—數字與運算子 處理文字—JavaScript中的字串 有用的字串函式 陣列 親合度:傻瓜故事產生器 JavaScript基礎要件 JavaScript基礎要件概述 於程式碼中決策—條件 程式碼迴圈 函式—可重複使用的程式碼區塊 建立自己的函式 函式回傳值 事件介紹 親合度:圖庫 JavaScript物件介紹 JavaScript物件概述 物件基礎概念 物件原型 Object-orientedprogrammingconcepts ClassesinJavaScript 使用JSON資料 物件建構實作 親合度:為彈跳球展示新增功能 非同步的JavaScript AsynchronousJavaScriptoverview IntroducingasynchronousJavaScript Howtousepromises Implementingapromise-basedAPI Introducingworkers Assessment:sequencinganimations 客戶端webAPIs 客戶端webAPIs IntroductiontowebAPIs 文件操作 Fetchingdatafromtheserver ThirdpartyAPIs Drawinggraphics VideoandaudioAPIs Client-sidestorage 網頁表單-與使用者資料合作 核心的表單學習途徑 網頁表單概述 Yourfirstform 如何建構網頁表單 Basicnativeformcontrols TheHTML5inputtypes Otherformcontrols Stylingwebforms Advancedformstyling UIpseudo-classes Client-sideformvalidation Sendingformdata 深入網頁表單 Howtobuildcustomformcontrols SendingformsthroughJavaScript CSSpropertycompatibilitytableforformcontrols 無障礙網頁—每個人都可以使用的網頁 無障礙網頁指南 無障礙網頁概述 何謂無障礙網頁? HTML:Agoodbasisforaccessibility CSSandJavaScriptaccessibilitybestpractices WAI-ARIA基礎 Accessiblemultimedia 行動裝置上的無障礙 無障礙網頁評估 Assessment:Accessibilitytroubleshooting 工具與測試 Client-sidewebdevelopmenttools Client-sidewebdevelopmenttoolsindex Client-sidetoolingoverview Commandlinecrashcourse Packagemanagementbasics Introducingacompletetoolchain Deployingourapp 介紹前端框架 前端框架簡介 Frameworkmainfeatures React GettingstartedwithReact BeginningourReacttodolist ComponentizingourReactapp Reactinteractivity:Eventsandstate Reactinteractivity:Editing,filtering,conditionalrendering AccessibilityinReact Reactresources Ember GettingstartedwithEmber Emberappstructureandcomponentization Emberinteractivity:Events,classesandstate EmberInteractivity:Footerfunctionality,conditionalrendering RoutinginEmber Emberresourcesandtroubleshooting Vue 開始學Vue CreatingourfirstVuecomponent RenderingalistofVuecomponents Addinganewtodoform:Vueevents,methods,andmodels StylingVuecomponentswithCSS UsingVuecomputedproperties Vueconditionalrendering:editingexistingtodos FocusmanagementwithVuerefs Vueresources Svelte Svelte入門 StartingourSvelteTodolistapp DynamicbehaviorinSvelte:workingwithvariablesandprops ComponentizingourSvelteapp AdvancedSvelte:Reactivity,lifecycle,accessibility WorkingwithSveltestores TypeScriptsupportinSvelte Deploymentandnextsteps Angular Angular新手入門 開始開發我們的Angular待辦事項應用程式 使用樣式點綴我們的Angular應用程式 建立一個item元件 篩選我們的待辦事項項目 建構Angular應用程式與更多資源 GitandGitHub GitandGitHub概述 HelloWorld GitHandbook ForkingProjects Aboutpullrequests MasteringIssues 跨瀏覽器測試 跨瀏覽器測試概述 跨瀏覽器測試介紹 測試執行策略 處理常見的HTML與CSS問題 處理常見的JavaScript問題 處理常見的親合度問題 建置功能偵測 自動化測試介紹 設定自己的自動化測試環境 伺服端網站程式設計 第一步 第一步概述 伺服端介紹 用戶端概述 伺服端網路框架 網站安全 Django網站框架(Python) Django網站框架(Python)概述 介紹 設定開發環境 線上教學:本地圖書館網站 線上教學2:建立網站骨架 線上教學3:使用模型 線上教學4:Django管理網站 線上教學5:建立我們的首頁 線上教學6:泛型清單與細節檢視 線上教學7:會話(Sessions)框架 線上教學8:使用者授權與許可 線上教學9:搭配表單 線上教學10:測試Django的WebApp 線上教學11:佈署Django至產品 WebApp安全性 親合度:DIY迷你部落格 Express網站框架(node.js/JavaScript) Express網站框架(Node.js/JavaScript)概述 Express/Node介紹 設定Node(Express)的開發環境 Express教學1:本地圖書館網站 Express教學2:建立骨架網站 Express教學3:使用資料庫(Mongoose) Express教學4:路由與控制器 Express教程5:呈現圖書館的資料 Express教學6:使用表單 Express教學7:佈署到正式環境 更多資源 常見問題 HTML問題 CSS問題 JavaScriptquestions Web的運作方式 工具與設定 設計與親合度 「原型」架構的程式語言?了解原型物件原型屬性也定義所要繼承的成員再次溫習create()建構子的屬性修改原型摘要物件原型 前頁 Overview:Objects 次頁 JavaScript的物件即透過原型(Prototype)機制相互繼承功能,且與典型的物件導向(OO)程式語言相較,其運作方式有所差異。

我們將透過本文說明相異之處、解釋原型鍊(Prototypechain)運作的方式,並了解原型屬性是如何將函式新增至現有的建構子(Constructor)之中。

必備條件: 基本的電腦素養、已初步了解HTML與CSS、熟悉JavaScript(參閱〈Firststeps〉與〈Buildingblocks〉以及OOJS基礎概念(參閱〈Introductiontoobjects〉。

主旨: 了解JavaScript的物件原型、原型鍊的運作方式、應如何將新的函式加入原型屬性之中。

「原型」架構的程式語言?常有人說JavaScript是原型架構的程式語言—各個物件均具備1組原型物件作為範本物件,用以繼承函式與屬性。

物件的原型物件可能也具備原型物件,並繼承了其上的函式與屬性。

這就是我們所謂的「原型鍊(Prototypechain)」,同時正好說明為何A物件的屬性與函式是透過B物件的屬性與函式所定義。

精確點說,這些屬性與函式都是透過物件的建構子函式所定義,並非物件實例本身。

傳統的OOP都是先定義了類別,接著在建立物件實例之後,在類型上定義的所有屬性與函式均複製到此實例。

但JavaScript不會複製這些屬性與函式,卻是在物件實例與其建構子之間設定連結(原型鍊中的連結),只要順著原型鍊就能在建構子之中找到屬性與函式。

先看個範例會比較清楚點。

了解原型物件先回到我們寫過的Person()建構子範例。

在你的瀏覽器裡載入範例。

如果你還沒看完前篇文章並製作出此範例,可先使用oojs-class-further-exercises.html這個範例(可看到原始碼)。

我們在此範例中定義了建構子函式: functionPerson(first,last,age,gender,interests){ //propertyandmethoddefinitions }; 接著建立如下的物件實例: varperson1=newPerson('Bob','Smith',32,'male',['music','skiing']); 如果你在自己的JavaScript主控台中鍵入「person1.」,應該會看到瀏覽器根據此物件可用的成員名稱開始自動補完: 在此列表中,可以看到person1原型物件上所定義的成員,也就是Person()建構子—name、age、gender、interests、bio、greeting。

你也會看到其他如watch、valueOf等,同樣也是定義在Person()建構子原型物件之上的成員,如此構成Object。

下圖顯示原型鍊的運作方式。

所以當你在person1上呼叫了「實際上是定義於 Object上的函式」,會發生什麼事呢?舉例來說: person1.valueOf() 此函式僅回傳所呼叫的物件數值。

此範例所將發生的是: 瀏覽器先檢查person1物件上是否有可用的valueOf()函式。

其實沒有,所以瀏覽器接著檢查person1物件的原型物件(Person)上是否有可用的valueOf()函式。

同樣沒有,所以瀏覽器再檢查Person()建構子的原型物件(Object)上是否有可用的valueOf()函式。

這次有,所以就會呼叫。

注意:再次重申,在原型鍊中的函式與屬性並不是從任一物件複製到另一個物件,而是如上述的,沿著該原型鍊向上存取而得。

注意:直接存取物件的原型物件,並沒有一定的方式。

原型鍊中,項目之間的「連結」均定義於內部屬性之內,即JavaScript規格中的[[prototype]](可參閱ECMAScript)。

新版瀏覽器均具備所謂的「__proto__(兩邊都是2個底線)」屬性,其中就包含了物件的原型物件。

舉例來說,你可嘗試「person1.__proto__」與「person1.__proto__.__proto__」看看程式碼中的鍊會是什麼樣子! 原型屬性也定義所要繼承的成員所以該在哪裡定義所要繼承的屬性與函式呢?若看一下Object參考頁面,你就會看到左邊列出許多屬性與函式,遠超過上方擷圖所列person1物件所繼承的成員數量。

有些繼承了,有些則無?為什麼呢? 原因在於,繼承的成員就是在prototype屬性(你也能稱之為子命名空間subnamespace)中定義的成員,也就是以「Object.prototype.」開頭的成員;並非只以「Object.」開頭的成員。

prototype屬性值就是1個物件,基本上儲存了許多我們想「讓原型鍊上的物件一路繼承下去」的屬性與函式。

所以如Object.prototype.watch()、Object.prototype.valueOf()等等,均可用於繼承自Object()的任何物件類型,包含以建構子建立的新物件實例。

Object.is()、Object.keys(),及其他未於prototype內定義的成員,也就不會繼承至1).物件實例或2).從Object()繼承而來的物件類型。

這些函式\屬性都只能用於Object()建構子本身。

注意:這看起來很奇怪:你怎麼能在建構子上定義函式(Method),而且這建構子本身也是函式(Function)?其實「Function」也屬於一個物件類型,可參閱Function()建構子參考以進一步了解。

你可自行檢查現有的原型屬性。

回到我們之前的範例,試著於JavaScript主控台中輸入: Person.prototype 輸出結果很平淡,畢竟我們並未在自定的建構子原型上定義任何東西。

依預設值,建構子的prototype都是從空白開始。

現在可嘗試下列: Object.prototype 這樣就會看到Object的prototype屬性中所定義的許多函式,而繼承自Object的物件也能找到這些函式。

只要試著尋找如String、Date、Number、Array等全域物件的原型上定義的函式與屬性,就會看到JavaScript中的其他原型鍊繼承範例。

這些物件都在其原型上定義了多個成員,因此可作為你建立字串時的範例: varmyString='Thisismystring.'; myString上立刻就有多個有用的函式,如split()、indexOf()、replace()等。

重要:prototype這個屬性,是JavaScript中最讓人混淆的名稱之一。

你可能會認為this屬性即指目前物件(currentobject)的原型物件(prototypeobject),但它其實不是原型(應該是可透過__proto__存取的內部物件(internalobject)才對,記得上面說過的嗎?)。

prototype是一個物件(object),內含了你定義所應該繼承的成員。

再次溫習create()我們先前講過用Object.create()函式建立新物件實例的方法。

舉例來說,你可先在前面的JavaScript主控台範例中試著輸入: varperson2=Object.create(person1); create()實際上是透過特定的原型物件,來建立新的物件。

我們在這裡將person1作為原型物件,建立了person2。

你可於主控台輸入下列以測試之: person2.__proto__ 如此將回傳person1物件。

建構子的屬性每個物件實例都具備1個建構子屬性,指向「用以建立實例」的原始建構子函式。

舉例來說,若在主控台中輸入下列指令: person1.constructor person2.constructor 應該兩者都會回傳Person()建構子,因為此建構子包含這些實例的原始定義。

偷吃步的方法,是將圓括弧加到constructor屬性(須包含任何必要參數)末端,以從該建構子建立其他物件實例。

畢竟建構子也是函式(Function),所以可透過圓括弧將之觸發。

你只要納入new這個關鍵字,即可將此函式作為建構子。

在主控台中輸入: varperson3=newperson1.constructor('Karen','Stephenson',26,'female',['playingdrums','mountainclimbing']); 現在可試著存取新物件的功能,例如: person3.name.first person3.age person3.bio() 這樣運作得還不差。

你不需常常用這方法,但當你要建立新的實例,又因為某些原因找不到原始建構子的參照,這就特別有用了。

此外,constructor屬性還有其他用處。

舉例來說,如果你有個物件實例,並要回傳建構子(本身就是實例)的名稱,就透過: instanceName.constructor.name 也可嘗試: person1.constructor.name修改原型先看看建構子的prototype屬性的修改範例: 回到oojs-class-further-exercises.html範例,先在本機儲存1份原始碼的副本。

在現成的JavaScript中加入下列程式碼,即是將新函式新增到建構子的prototype屬性: Person.prototype.farewell=function(){ alert(this.name.first+'hasleftthebuilding.Byefornow!'); } 儲存程式碼並在瀏覽器中載入頁面,再輸入下列程式碼: person1.farewell(); 這時應該會看到警示訊息且內含了建構子所定義的人名。

這樣很有用,但如果能動態更新整個繼承鍊,且從建構子分割出來的所有物件實例都能使用此新的函式,就會更有用! 花個1分鐘想想,我們的程式碼中定義了建構子,然後根據建構子建立實例物件,接著將新函式添增到建構子的原型: functionPerson(first,last,age,gender,interests){ //propertyandmethoddefinitions }; varperson1=newPerson('Tammi','Smith',32,'neutral',['music','skiing','kickboxing']); Person.prototype.farewell=function(){ alert(this.name.first+'hasleftthebuilding.Byefornow!'); } 但是farewell()函式仍可用於person1物件實例,其可用的功能已自動更新過。

如此證明了我們之前對原型鍊的說明,也代表瀏覽器會沿著鍊往上找「尚未於物件實例上定義的函式」,而非「複製到實例中的函式」。

如此可建構強大且靈活的系統。

注意:如果你在讓此範例運作時感覺有點困難,可參閱oojs-class-prototype.html範例(也可看即時運作的情形)。

你很少會看到在prototype屬性上定義的屬性,因為照此範例定義的屬性彈性較低,舉例來說,你可新增如下的屬性: Person.prototype.fullName='BobSmith'; 但因為幾乎不會有人取這名字,所以就沒什麼彈性。

最好可以在name.first與name.last之外建立fullName: Person.prototype.fullName=this.name.first+''+this.name.last; 但因為這樣會參照全域範圍,而非函式範圍,所以也不適用。

若呼叫此屬性,則將回傳undefinedundefined。

這種模式適合我們先前於原型中定義的函式,因為該函式就是在功能範圍之內,且可成功轉移至物件實例的的範圍。

因此你可能會在原型中定義常數屬性(也就是永遠不需更改的屬性),但一般來說會比較適合在建構子中定義屬性。

事實上,許多物件定義較常見的模式,就是在建構子中定義屬性,而在原型中定義函式。

這樣一來,建構子只有屬性定義;函式則切分到不同的區塊,讓整個程式碼較清楚易讀。

舉例來說: //Constructorwithpropertydefinitions functionTest(a,b,c,d){ //propertydefinitions }; //Firstmethoddefinition Test.prototype.x=function(){...} //Secondmethoddefinition Test.prototype.y=function(){...} //etc. 你可在PiotrZalewa的「schoolplanapp」範例中看到實際運作的範例。

摘要本文說明了JavaScript物件原型,包含原型物件鍊是如何讓物件能互相繼承其特性、原型屬性的本質、原型屬性又是如何能將函式新增至建構子,以及其他相關概念。

接著我們將讓你在自己的任2個自訂物件之間,實作功能的繼承。

前頁 Overview:Objects 次頁 Foundaproblemwiththispage?EditonGitHubSourceonGitHubReportaproblemwiththiscontentonGitHubWanttofixtheproblemyourself?SeeourContributionguide.Lastmodified:2022年6月23日,byMDNcontributors



請為這篇文章評分?