摘要:下面輪到我們的主角原型繼承登場了,它從另一個角度解決了重用的問題。原型繼承的原理原型對象中的由兩部分組成,普通屬性的集合,和原型屬性。原型繼承的實現在上面的例子中,通過直接修改了屬性值,實現了原型繼承。使用原型繼承,同樣可以達到重用的目的。
繼承的本質:重用
在探討 JavaScript 的原型繼承之前,先不妨想想為什么要繼承?
考慮一個場景,如果我們有兩個對象,它們一部分屬性相同,另一部屬性不同。通常一個好的設計方案是將相同邏輯抽出來,實現重用。
以 xiaoMing liLei 兩位同學舉例。這兩位同學有自己的名字,并且會介紹自己。抽象為程序對象,可以做如下表示。
var xiaoMing = { name : "xiaoMing", hello : function(){ console.log( "Hello, my name is "+ this.name + "."); } } var liLei = { name : "liLei", hello : function(){ console.log( "Hello, my name is "+ this.name + "."); } }
使用過 java 的同學,可能第一眼就想到了用面向對象來解決這個問題。創造一個 Person 的類,然后實例化 xiaoMing 和 liLei 兩個對象。在 ES6 中也有類似于 java 中類的概念:class。
下面使用 ES6 的語法,用面向對象的思路來重構上面的代碼。
class Person { constructor(name){ this.name = name } hello(){ console.log(this.name); } } var xiaoMing = new Person("xiaoMing"); var liLei = new Person("liLei");
可以看到,使用類創建對象,達到了重用的目的。它基于的邏輯是,兩個或多個對象的結構功能類似,可以抽象出一個模板,依照模板復制出多個相似的對象。
使用類創建對象,就像自行車制造商一遍一遍地重用相同的藍圖來制造大量的自行車。
然解決重用問題的方案,當然不止一種。傳統面向對象的類,只是其中的一種方案。下面輪到我們的主角“原型繼承”登場了,它從另一個角度解決了重用的問題。
原型繼承的原理 原型對象JavaScript 中的 object 由兩部分組成,普通屬性的集合,和原型屬性。
var o = { a : "a", ... __proto__: prototypeObj }
普通屬性指的就是 a;原型屬性 指的是 __proto__。這本不屬于規范的一部分,后來 chrome 通過 __proto__ 將這個語言底層屬性給暴露出來了,慢慢的被大家所接受,也就添加到 ES6 規范中了。 o.__proto__ 的值 prototypeObj 也就是 原型對象 。原型對象其實也就是一個普通對象,之所以叫原型對象的原因,只是因為它是原型屬性所指的值。
原型對象所以特殊,是因為它擁有一個普通對象沒有的能力:將它的屬性共享給其他對象。
在 ES6 規范 中,對它是如下定義的:
object that provides shared properties for other objects屬性讀操作
回到最開始的例子,看看如何利用原型繼承實現重用的目的。
var prototypeObj = { hello: function(){ console.log( "Hello, my name is "+ this.name + "."); } // ... } var xiaoMing = { name : "xiaoMing", __proto__ : prototypeObj } var liLei = { name : "liLei", __proto__ : prototypeObj } xiaoMing.hello(); // Hello, my name is xiaoMing. liLei.hello(); // Hello, my name is liLei.
xiaoMing liLei 對象上,并沒直接擁有 hello 屬性(方法),但是卻能讀取該屬性(執行該方法),這是為什么?
想象一個場景,你在做數學作業,遇到一個很難的題目,你不會做。而你有一個好兄弟,數學很厲害,你去請教他,把這道題做出來了。
xiaoMing 對象上,沒有 hello 屬性,但是它有一個好兄弟,prototypeObj。屬性讀操作,在 xiaoMing 身上沒有找到 hello 屬性,就會去問它的兄弟 prototypeObj。所以 hello 方法會被執行。
原型鏈還是做數學題的例子。你的數學題目很難,你的兄弟也沒有答案,他推薦你去問另外一個同學。這樣直到有了答案或者再也沒有人可以問,你就不會再問下去。這樣就好像有一條無形鏈條把你和同學們牽在了一起。
在 JS 中,讀操作通過 __proto__ 會一層一層鏈下去的結構,就叫 原型鏈。
var deepPrototypeObj = { hello: function(){ console.log( "Hello, my name is "+ this.name + "."); } __proto__ : null } var prototypeObj = { __proto__ : deepPrototypeObj } var xiaoMing = { name : "xiaoMing", __proto__ : prototypeObj } var liLei = { name : "liLei", __proto__ : prototypeObj } xiaoMing.hello(); // Hello, my name is xiaoMing. liLei.hello(); // Hello, my name is liLei.原型繼承的實現
在上面的例子中,通過直接修改了 __proto__ 屬性值,實現了原型繼承。但是在實際生產中,
用這種方式來改變和繼承屬性是對性能影響非常嚴重的,所以并不推薦。
代替的方式是使用 Object.create() 方法。
調用 Object.create() 方法會創建一個新對象,同時指定該對象的原型對象為傳入的第一個參數。
我們將上面的例子改一下。
var prototypeObj = { hello: function(){ console.log( "Hello, my name is "+ this.name + "."); } // ... } var xiaoMing = Object.create(prototypeObj); var liLei = Object.create(prototypeObj); xiaoMing.name = "xiaoMing"; liLei.name = "liLei"; xiaoMing.hello(); // Hello, my name is xiaoMing. liLei.hello(); // Hello, my name is liLei.
You-Dont-Know-JS 的作者,對這種原型繼承的實現取了一個很好玩的名字 OLOO (objects-linked-to-other-objects) ,這種實現方式的優點是沒有使用任何類的概念,只有 object,所以它是很符合 javaScript 的特性的。
因為JS 中本無類,只有 object。
無奈的是,喜歡類的程序員是在太多,所以在 ES6 新增了 class 概念。下一篇會講 class 在 JS 中的實現原理
小結通過類來創建對象,使得開發者不必寫重復的代碼,以達到代碼重用的目的。它基于的邏輯是,兩個或多個對象的結構功能類似,可以抽象出一個模板,依照模板復制出多個相似的對象。就像自行車制造商一遍一遍地重用相同的藍圖來制造大量的自行車。
使用原型繼承,同樣可以達到重用的目的。它基于的邏輯是,兩個或多個對象的對象有一部分共用屬性,可以將共用的屬性抽象到另一個獨立公共對象上,通過特殊的原型屬性,將公共對象和普通對象鏈接起來,再利用屬性讀(寫)規則進行遍歷查找,實現屬性共享。
參考文章ES6 規范
You-Dont-Know-JS
MDN Object.create()
JavaScript difference between proto and prototype
proto VS. prototype in JavaScript
JavaScript. The core
Understanding "Prototypes" in JavaScript
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86779.html
摘要:如下所示在規范中,已經正式把屬性添加到規范中也可以通過設置和獲取對象的原型對象對象之間的關系可以用下圖來表示但規范主要介紹了如何利用構造函數去構建原型關系。 前言 在軟件工程中,代碼重用的模式極為重要,因為他們可以顯著地減少軟件開發的成本。在那些主流的基于類的語言(比如Java,C++)中都是通過繼承(extend)來實現代碼復用,同時類繼承引入了一套類型規范。而JavaScript是...
摘要:深入之繼承的多種方式和優缺點深入系列第十五篇,講解各種繼承方式和優缺點。對于解釋型語言例如來說,通過詞法分析語法分析語法樹,就可以開始解釋執行了。 JavaScript深入之繼承的多種方式和優缺點 JavaScript深入系列第十五篇,講解JavaScript各種繼承方式和優缺點。 寫在前面 本文講解JavaScript各種繼承方式和優缺點。 但是注意: 這篇文章更像是筆記,哎,再讓我...
摘要:深入理解原型與繼承看過不少書籍,不少文章,對于原型與繼承的說明基本上讓人不明覺厲,特別是對于習慣了面向對象編程的人來說更難理解,這里我就給大家說說我的理解。 深入理解:JavaScript原型與繼承 看過不少書籍,不少文章,對于原型與繼承的說明基本上讓人不明覺厲,特別是對于習慣了面向對象編程的人來說更難理解,這里我就給大家說說我的理解。 首先JavaScript是一門基于原型編程的語言...
摘要:原型鏈與繼承當談到繼承時,只有一種結構對象。如果對該圖不怎么理解,不要著急,繼續往下看基于原型鏈的繼承對象是動態的屬性包指其自己的屬性。當使用操作符來作用這個函數時,它就可以被稱為構造方法構造函數。 原型鏈與繼承 當談到繼承時,JavaScript 只有一種結構:對象。每個實例對象(object )都有一個私有屬性(稱之為proto)指向它的原型對象(prototype)。該原型對象也...
摘要:首先,需要來理清一些基礎的計算機編程概念編程哲學與設計模式計算機編程理念源自于對現實抽象的哲學思考,面向對象編程是其一種思維方式,與它并駕齊驅的是另外兩種思路過程式和函數式編程。 JavaScript 中的原型機制一直以來都被眾多開發者(包括本人)低估甚至忽視了,這是因為絕大多數人沒有想要深刻理解這個機制的內涵,以及越來越多的開發者缺乏計算機編程相關的基礎知識。對于這樣的開發者來說 J...
閱讀 2856·2021-10-14 09:42
閱讀 3174·2019-08-30 15:52
閱讀 3240·2019-08-30 14:02
閱讀 1102·2019-08-29 15:42
閱讀 529·2019-08-29 13:20
閱讀 1157·2019-08-29 12:24
閱讀 470·2019-08-26 10:20
閱讀 680·2019-08-23 18:31