摘要:原型鏈和構(gòu)造函數(shù)是一種面向?qū)ο蟮恼Z(yǔ)言,并且可以進(jìn)行原型繼承。來(lái)了極大的支持了工程化,它的標(biāo)準(zhǔn)讓瀏覽器內(nèi)部實(shí)現(xiàn)類(lèi)和類(lèi)的繼承構(gòu)造函數(shù)構(gòu)造函數(shù)調(diào)用父類(lèi)構(gòu)造函數(shù)現(xiàn)在瀏覽器對(duì)其支持程度還不高。
原型鏈
原型鏈比作用域鏈要好理解的多。
JavaScript中的每個(gè)對(duì)象,都有一個(gè)內(nèi)置的_proto_屬性。這個(gè)屬性是編程不可見(jiàn)的(雖然ES6標(biāo)準(zhǔn)中開(kāi)放了這個(gè)屬性,然而瀏覽器對(duì)這個(gè)屬性的可見(jiàn)性的支持不同),它實(shí)際上是對(duì)另一個(gè)對(duì)象或者null的引用。
當(dāng)一個(gè)對(duì)象需要引用一個(gè)屬性時(shí),JavaScript引擎首先會(huì)從這個(gè)對(duì)象自身的屬性表中尋找這個(gè)屬性標(biāo)識(shí),如果找到則進(jìn)行相應(yīng)讀寫(xiě)操作,若沒(méi)有在自身的屬性表中找到,則在_proto_屬性引用的對(duì)象的屬性表中查找,如此往復(fù),直到找到這個(gè)屬性或者_proto_屬性指向null為止。
這個(gè)_proto_的引用鏈,被稱(chēng)作原型鏈。
注意,此處有一個(gè)性能優(yōu)化的問(wèn)題:往原型鏈越深處搜索,耗費(fèi)的時(shí)間越多。
原型鏈和構(gòu)造函數(shù)JavaScript是一種面向?qū)ο蟮恼Z(yǔ)言,并且可以進(jìn)行原型繼承。
JavaScript中的函數(shù)有一個(gè)屬性prototype,這個(gè)prototype屬性是一個(gè)對(duì)象,它的一個(gè)屬性constructor引用該函數(shù)自身。即:
func.prototype.constructor === func; // ==> true
這個(gè)屬性有什么用呢?我們知道一個(gè),一個(gè)函數(shù)使用new操作符調(diào)用時(shí),會(huì)作為構(gòu)造函數(shù)返回一個(gè)新的對(duì)象。這個(gè)對(duì)象的_proto_屬性引用其構(gòu)造函數(shù)的prototype屬性。
因此這個(gè)就不難理解了:
var obj = new Func(); obj.constructor == Func; // ==> true
還有這個(gè):
obj instanceof Func; // ==> true
也是通過(guò)查找原型鏈上的constructor屬性實(shí)現(xiàn)的。
構(gòu)造函數(shù)生成的不同實(shí)例的_proto_屬性是對(duì)同一個(gè)prototype對(duì)象的引用。所以修改prototype對(duì)象會(huì)影響所有的實(shí)例。
“子類(lèi)”繼承實(shí)現(xiàn)的幾種方式之所以子類(lèi)要加引號(hào),是因?yàn)檫@里說(shuō)“類(lèi)”的概念是不嚴(yán)謹(jǐn)?shù)摹avaScript是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,但是它跟Java等語(yǔ)言不同,在ES6標(biāo)準(zhǔn)出爐之前,它是沒(méi)有類(lèi)的定義的。
但是熟悉Java等語(yǔ)言的程序員,也希望使用JavaScript時(shí),跟使用Java相似,通過(guò)類(lèi)生成實(shí)例,通過(guò)子類(lèi)復(fù)用代碼。那么在ES6之前,怎么做到像如下代碼一樣使用類(lèi)似"類(lèi)"的方式呢?
var parent = new Parent("Sam"); var child = new Children("Samson"); parent.say(); // ==> "Hello, Sam!" child.say(); // ==> "Hello, Samson! hoo~~" child instanceof Parent; // ==> true
我們看到,這里我們把構(gòu)造函數(shù)當(dāng)做類(lèi)來(lái)用。
以下我們討論一下實(shí)現(xiàn)的幾種方式:
最簡(jiǎn)單的方式結(jié)合原型鏈的概念,我們很容易就能寫(xiě)出這樣的代碼:
function Parent(name){ this.name = name; } Parent.prototype.say = function(){ console.log("Hello, " + this.name + "!"); } function Children(name){ this.name = name; } Children.prototype = new Parent(); Children.prototype.say = function(){ console.log("Hello, " + this.name + "! hoo~~"); }
這個(gè)方式缺點(diǎn)很明顯:作為子類(lèi)的構(gòu)造函數(shù)需要依賴(lài)一個(gè)父類(lèi)的對(duì)象。這個(gè)對(duì)象中的屬性name根本毫無(wú)用處。
第一次改進(jìn)// ... Children.prototype = Parent.prototype; // ...
這樣就不會(huì)產(chǎn)生無(wú)用的父類(lèi)屬性了。
然而,這樣的話(huà)子類(lèi)和父類(lèi)的原型就引用了同一個(gè)對(duì)象,修改子類(lèi)的prototype也會(huì)影響父類(lèi)的原型。
這時(shí)候我們發(fā)現(xiàn):
parent.say(); // ==> "Hello,Sam! hoo~~"
這第一次改進(jìn)還不如不改。
第二次改進(jìn)——臨時(shí)構(gòu)造函數(shù)/Object.createfunction F(){ // empty } F.prototype = Parent.prototype; Children.prototype = new F(); // ... parent.say(); // ==> "Hello, Sam!" child.say(); // ==> "Hello, Samson! hoo~~"
這樣一來(lái),修改子類(lèi)的原型只是修改了F的一個(gè)實(shí)例的屬性,并沒(méi)有改變Parent.prototype,從而解決了上面的問(wèn)題。
在ES5的時(shí)代,我們還可以直接這樣:
Children.prototype = Object.create(Parent.prototype);
這里的思路是一樣的,都是讓子類(lèi)的prototype不直接引用父類(lèi)prototype。目前的現(xiàn)代瀏覽器幾乎已經(jīng)添加了對(duì)這個(gè)方法的支持。(但我們下面會(huì)仍以臨時(shí)構(gòu)造函數(shù)為基礎(chǔ))
但是細(xì)細(xì)思考,這個(gè)方案仍有需要優(yōu)化的地方。例如:如何讓父類(lèi)的構(gòu)造函數(shù)邏輯直接運(yùn)用到子類(lèi)中,而不是再重新寫(xiě)一遍一樣的?這個(gè)例子中只有一個(gè)name屬性的初始化,那么假設(shè)有很多屬性且邏輯一樣的話(huà),豈不是沒(méi)有做到代碼重用?
第三次改進(jìn)——構(gòu)造函數(shù)方法借用使用apply,實(shí)現(xiàn)“方法重用”的思想。
function Children(name){ Parent.apply(this, arguments); // do other initial things }“圣杯”模式
現(xiàn)在完整的代碼如下:
function Parent(name){ this.name = name; } Parent.prototype.say = function(){ console.log("Hello, " + this.name + "!"); } function Children(name){ Parent.apply(this, arguments); // do other initial things } function F(){ // empty } F.prototype = Parent.prototype; Child.prototype = new F(); Children.prototype.say = function(){ console.log("Hello, " + this.name + "! hoo~~"); }
這就是所謂“圣杯”模式,聽(tīng)著很高大上吧?
以上就是ES3的時(shí)代,我們用來(lái)實(shí)現(xiàn)原型繼承的一個(gè)近似最佳實(shí)踐。
“圣杯”模式的問(wèn)題“圣杯”模式依然存在一個(gè)問(wèn)題:雖然父類(lèi)和子類(lèi)實(shí)例的繼承的prototype對(duì)象不是同一個(gè)實(shí)例,但是這兩個(gè)prototype對(duì)象上面的屬性引用了同樣的對(duì)象。
假設(shè)我們有:
Parent.prototype.a = { x: 1}; // ...
那么即使是“圣杯”模式下,依然會(huì)有這樣的問(wèn)題:
parent.x // ==> 1 child.x // ==> 1 child.x = 2; parent.x // ==>2
問(wèn)題在于,JavaScript的拷貝不是 深拷貝(deepclone)
要解決這個(gè)問(wèn)題,我們可以利用屬性遞歸遍歷,自己實(shí)現(xiàn)一個(gè)深拷貝的方法。這個(gè)方法在這里我就不寫(xiě)了。
ES6來(lái)了ES6極大的支持了工程化,它的標(biāo)準(zhǔn)讓瀏覽器內(nèi)部實(shí)現(xiàn)類(lèi)和類(lèi)的繼承:
class Parent { constructor(name) { //構(gòu)造函數(shù) this.name = name; } say() { console.log("Hello, " + this.name + "!"); } } class Children extends Parent { constructor(name) { //構(gòu)造函數(shù) super(name); //調(diào)用父類(lèi)構(gòu)造函數(shù) // ... } say() { console.log("Hello, " + this.name + "! hoo~~"); } }
現(xiàn)在瀏覽器對(duì)其支持程度還不高。但是這種寫(xiě)法的確省力不少。讓我們對(duì)未來(lái)拭目以待。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90857.html
摘要:原文地址詳解的類(lèi)博主博客地址的個(gè)人博客從當(dāng)初的一個(gè)彈窗語(yǔ)言,一步步發(fā)展成為現(xiàn)在前后端通吃的龐然大物。那么,的類(lèi)又該怎么定義呢在面向?qū)ο缶幊讨?,?lèi)是對(duì)象的模板,定義了同一組對(duì)象又稱(chēng)實(shí)例共有的屬性和方法。這個(gè)等同于的屬性現(xiàn)已棄用。。 前言 生活有度,人生添壽。 原文地址:詳解javascript的類(lèi) 博主博客地址:Damonare的個(gè)人博客 ??Javascript從當(dāng)初的一個(gè)彈窗語(yǔ)言,一...
摘要:原文地址詳解的類(lèi)博主博客地址的個(gè)人博客從當(dāng)初的一個(gè)彈窗語(yǔ)言,一步步發(fā)展成為現(xiàn)在前后端通吃的龐然大物。那么,的類(lèi)又該怎么定義呢在面向?qū)ο缶幊讨?,?lèi)是對(duì)象的模板,定義了同一組對(duì)象又稱(chēng)實(shí)例共有的屬性和方法。這個(gè)等同于的屬性現(xiàn)已棄用。。 前言 生活有度,人生添壽。 原文地址:詳解javascript的類(lèi) 博主博客地址:Damonare的個(gè)人博客 ??Javascript從當(dāng)初的一個(gè)彈窗語(yǔ)言,一...
摘要:接下來(lái)我們來(lái)聊一下的原型鏈繼承和類(lèi)。組合繼承為了復(fù)用方法,我們使用組合繼承的方式,即利用構(gòu)造函數(shù)繼承屬性,利用原型鏈繼承方法,融合它們的優(yōu)點(diǎn),避免缺陷,成為中最常用的繼承。 JavaScript是一門(mén)面向?qū)ο蟮脑O(shè)計(jì)語(yǔ)言,在JS里除了null和undefined,其余一切皆為對(duì)象。其中Array/Function/Date/RegExp是Object對(duì)象的特殊實(shí)例實(shí)現(xiàn),Boolean/N...
摘要:原型鏈理解第一件事你不用管其他語(yǔ)言,一句話(huà),你只要記住里面的對(duì)象包含一個(gè)原型,原型是啥,就是另外一個(gè)對(duì)象。原型就相當(dāng)于你家的車(chē)棚子,而你的那個(gè)自行車(chē)就是對(duì)象。萬(wàn)事萬(wàn)物皆對(duì)象有啥用一句話(huà),擴(kuò)展原型方法,給大家一到面試題,數(shù)組去重自己體會(huì)下。 概述 通過(guò)上節(jié)課的學(xué)習(xí),大家已經(jīng)會(huì)用一種json的方式定義對(duì)象了,其實(shí)這個(gè)就是傳說(shuō)中的單體模式,當(dāng)然這個(gè)大家不用記,關(guān)于設(shè)計(jì)模式暫時(shí)不用了解。但是總...
閱讀 940·2021-10-27 14:14
閱讀 1757·2021-10-11 10:59
閱讀 1329·2019-08-30 13:13
閱讀 3167·2019-08-29 15:17
閱讀 2764·2019-08-29 13:48
閱讀 505·2019-08-26 13:36
閱讀 2094·2019-08-26 13:25
閱讀 867·2019-08-26 12:24