摘要:而作為構造函數,需要有個屬性用來作為以該構造函數創造的實例的繼承。
歡迎來我的博客閱讀:「JavaScript 原型中的哲學思想」
記得當年初試前端的時候,學習JavaScript過程中,原型問題一直讓我疑惑許久,那時候捧著那本著名的紅皮書,看到有關原型的講解時,總是心存疑慮。
當在JavaScript世界中走過不少旅程之后,再次萌發起研究這部分知識的欲望,翻閱了不少書籍和資料,才搞懂__proto__和prototype的概念。
故以作此筆記,日后忘了可以回來看看。如果你看的過程中覺得理解有些困難,把例子在代碼中跑一跑,親手試一試也許能解決不少疑惑。
一切皆為對象殊不知,JavaScript的世界中的對象,追根溯源來自于一個 null
「一切皆為對象」,這句著實是一手好營銷,易記,易上口,印象深刻。
萬物初生時,一個null對象,憑空而生,接著Object、Function學著null的模樣塑造了自己,并且它們彼此之間喜結連理,提供了prototype和constructor,一個給子孫提供了基因,一個則制造萬千子子孫孫。
在JavaScript中,null也是作為一個對象存在,基于它繼承的子子孫孫,當屬對象。乍一看,null像是上帝,而Object和Function猶如JavaScript世界中的亞當與夏娃。
原型指針 __proto__在JavaScript中,每個對象都擁有一個原型對象,而指向該原型對象的內部指針則是__proto__,通過它可以從中繼承原型對象的屬性,原型是JavaScript中的基因鏈接,有了這個,才能知道這個對象的祖祖輩輩。從對象中的__proto__可以訪問到他所繼承的原型對象。
var a = new Array(); a.__proto__ === Array.prototype // true
上面代碼中,創建了一個Array的實例a,該實例的原型指向了Array.prototype。
Array.prototype本身也是一個對象,也有繼承的原型:
a.__proto__.__proto__ === Object.prototype // true // 等同于 Array.prototype.__proto__ === Object.prototype
這就說了明了,Array本身也是繼承自Object的,那么Object的原型指向的是誰呢?
a.__proto__.__proto__.__proto__ === null // true // 等同于 Object.prototype.__proto__ === null
所以說,JavaScript中的對象,追根溯源都是來自一個null對象。佛曰:萬物皆空,善哉善哉。
除了使用.__proto__方式訪問對象的原型,還可以通過Object.getPrototypeOf方法來獲取對象的原型,以及通過Object.setPrototypeOf方法來重寫對象的原型。
值得注意的是,按照語言標準,__proto__屬性只有瀏覽器才需要部署,其他環境可以沒有這個屬性,而且前后的兩根下劃線,表示它本質是一個內部屬性,不應該對使用者暴露。因此,應該盡量少用這個屬性,而是用 Object.getPrototypeof和Object.setPrototypeOf,進行原型對象的讀寫操作。這里用__proto__屬性來描述對象中的原型,是因為這樣來得更加形象,且容易理解。
原型對象 prototype函數作為JavaScript中的一等公民,它既是函數又是對象,函數的原型指向的是Function.prototype
var Foo = function() {} Foo.__proto__ === Function.prototype // true
函數實例除了擁有__proto__屬性之外,還擁有prototype屬性。通過該函數構造的新的實例對象,其原型指針__proto__會指向該函數的prototype屬性。
var a = new Foo(); a.__proto__ === Foo.prototype; // true
而函數的prototype屬性,本身是一個由Object構造的實例對象。
Foo.prototype.__proto__ === Object.prototype; // true
prototype屬性很特殊,它還有一個隱式的constructor,指向了構造函數本身。
Foo.prototype.constructor === Foo; // true a.constructor === Foo; // true a.constructor === Foo.prototype.constructor; // true
PS: a.constructor屬性并不屬于a(a.hasOwnProperty("constructor") === false),而是讀取的a.__proto__.constructor,所以上圖用虛線表示a.constructor,方便理解。
原型鏈概念:
原型鏈作為實現繼承的主要方法,其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
每個構造函數都有一個原型對象(prototype),原型對象都包含一個指向構造函數的指針(constructor),而實例都包含一個指向原型對象的內部指針(__proto__)。
那么,假如我們讓原型對象等于另一個類型的實例,此時的原型對象將包含一個指向另一個原型的指針,相應地,另一個原型中也包含著一個指向另一個構造函數的指針。假如另一個原型又是另一個類型的實例,那么上述關系依然成立。如此層層遞進,就構造了實例與原型的鏈條,這就是原型鏈的基本概念。
意義:“原型鏈”的作用在于,當讀取對象的某個屬性時,JavaScript引擎先尋找對象本身的屬性,如果找不到,就到它的原型去找,如果還是找不到,就到原型的原型去找。以此類推,如果直到最頂層的Object.prototype還是找不到,則返回undefine。
親子鑒定在JavaScript中,也存在鑒定親子之間DNA關系的方法:
instanceof 運算符返回一個布爾值,表示一個對象是否由某個構造函數創建。
Object.isPrototypeOf() 只要某個對象處在原型鏈上,isProtypeOf都返回true
var Bar = function() {} var b = new Bar(); b instanceof Bar // true Bar.prototype.isPrototypeOf(b) // true Object.prototype.isPrototypeOf(Bar) // true
要注意,實例b的原型是Bar.prototype而不是Bar
一張歷史悠久的圖這是一張描述了Object、Function以及一個函數實例Foo他們之間原型之間聯系。如果理解了上面的概念,這張圖是不難讀懂。
從上圖中,能看到一個有趣的地方。
Function.prototype.__proto__ 指向了 Object.prototype,這說明Function.prototype 是一個 Object實例,那么應當是先有的Object再有Function。
但是Object.prototype.constructor.__proto__ 又指向了 Function.prototype。這樣看來,沒有Function,Object也不能創建實例。
這就產生了一種類「先有雞還是先有蛋」的經典問題,到底是先有的Object還是先有的Function呢?
這么哲學向的問題,留給你思考了。
我只是感慨:越往JavaScript的深處探索,越覺得這一門語言很哲學。
先有雞還是先有蛋?update on 2017/01/05
時隔半年,偶爾翻開這篇文章。
對于這個問題,又有了新的思考。
愿意跟能看到這里的你來分享一下。
我們可以先把 Object.prototype 和 Function.prototype 這兩個拎出來看,因為他們本身就是一個實例對象。
為方便理解,我們改一下名字,避免和 Object 和 Function 的強關聯,分別叫:Op 和 Fp
那么就有這樣的原型鏈存在了
我再描述一下上面的原型鏈,先有 null , 再有了 Op , 然后再有了 Fp ,然后以 Fp 為原型的兩個構造函數 (Object, Function) 出現了。
而作為構造函數,需要有個 prototype 屬性用來作為以該構造函數創造的實例的繼承。
所以Object.prototype = Op, Function.prototype = Fp。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79804.html
摘要:設計模式是以面向對象編程為基礎的,的面向對象編程和傳統的的面向對象編程有些差別,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續了解設計模式必須要先搞懂面向對象編程,否則只會讓你自己更痛苦。 JavaScript 中的構造函數 學習總結。知識只有分享才有存在的意義。 是時候替換你的 for 循環大法了~ 《小分享》JavaScript中數組的那些迭代方法~ ...
摘要:繼承的是超類型中構造函數中的屬性,如上繼承了屬性,但沒有繼承原型中的方法。上述造成的結果是子類型實例中有兩組超類型的構造函數中定義的屬性,一組在子類型的實例中,一組在子類型實例的原型中。 ECMAScript只支持實現繼承,主要依靠原型鏈來實現。與實現繼承對應的是接口繼承,由于script中函數沒有簽名,所以無法實現接口繼承。 一、原型鏈 基本思想:利用原型讓一個引用類型繼承另一個引用...
摘要:首先,需要來理清一些基礎的計算機編程概念編程哲學與設計模式計算機編程理念源自于對現實抽象的哲學思考,面向對象編程是其一種思維方式,與它并駕齊驅的是另外兩種思路過程式和函數式編程。 JavaScript 中的原型機制一直以來都被眾多開發者(包括本人)低估甚至忽視了,這是因為絕大多數人沒有想要深刻理解這個機制的內涵,以及越來越多的開發者缺乏計算機編程相關的基礎知識。對于這樣的開發者來說 J...
摘要:對象重新認識面向對象面向對象從設計模式上看,對象是計算機抽象現實世界的一種方式。除了字面式聲明方式之外,允許通過構造器創建對象。每個構造器實際上是一個函數對象該函數對象含有一個屬性用于實現基于原型的繼承和共享屬性。 title: JS對象(1)重新認識面向對象 date: 2016-10-05 tags: JavaScript 0x00 面向對象 從設計模式上看,對象是...
摘要:封裝手寫的方筆記使用檢測文件前端掘金副標題可以做什么以及使用中會遇到的坑。目的是幫助人們用純中文指南實現復選框中多選功能前端掘金作者緝熙簡介是推出的一個天挑戰。 深入理解 JavaScript Errors 和 Stack Traces - 前端 - 掘金譯者注:本文作者是著名 JavaScript BDD 測試框架 Chai.js 源碼貢獻者之一,Chai.js 中會遇到很多異常處理...
閱讀 2902·2021-11-25 09:43
閱讀 2320·2021-11-24 09:39
閱讀 2708·2021-09-23 11:51
閱讀 1400·2021-09-07 10:11
閱讀 1449·2019-08-27 10:52
閱讀 1929·2019-08-26 12:13
閱讀 3356·2019-08-26 11:57
閱讀 1393·2019-08-26 11:31