JS(五)原型與原型鏈

在這篇文章里我們將要了解以下幾個(gè)方面:

  • 關(guān)于內(nèi)存的那點(diǎn)事兒
  • 關(guān)于垃圾回收那點(diǎn)事兒
  • 包裝對(duì)象
  • 什么是原型
  • 什么是原型鏈
  • __ proto __和prototype

一嫩痰、關(guān)于內(nèi)存那點(diǎn)事兒

我們經(jīng)常會(huì)想么抗,當(dāng)我們聲明并賦值一個(gè)變量時(shí)干奢,存儲(chǔ)的狀態(tài)是如何的,我們?cè)傩姓{(diào)用時(shí)又是一套什么操作呢鹅颊?

我們來假設(shè)一下纵竖,如果計(jì)算機(jī)內(nèi)存有2G,開機(jī)后在旱,操作系統(tǒng)分配到了512M摇零,瀏覽器分配到了1G,假設(shè)除去各種頁面html等等內(nèi)存分配外桶蝎,JS分配到了100M驻仅,那這100M如何分配?

是這樣的登渣,它劃分了兩個(gè)區(qū)噪服,我們簡(jiǎn)單稱其為代碼區(qū)和數(shù)據(jù)區(qū),代碼區(qū)來存儲(chǔ)代碼胜茧,數(shù)據(jù)區(qū)來存儲(chǔ)賦值的數(shù)據(jù)粘优。

比如下列代碼:

var a = 1
// 代碼區(qū) 存儲(chǔ)a
// 數(shù)據(jù)區(qū) 存儲(chǔ)1

代碼區(qū)和數(shù)據(jù)區(qū)又存在著引用關(guān)系,比如我們只要訪問a呻顽,就可以讀取到1.

在JS中雹顺,每一個(gè)數(shù)據(jù)都需要一個(gè)內(nèi)存空間。內(nèi)存空間又被分為兩種廊遍,棧內(nèi)存(stack)與堆內(nèi)存(heap)嬉愧。

數(shù)據(jù)區(qū)內(nèi)也分了兩個(gè)區(qū),棧(stack)內(nèi)存和堆(heap)內(nèi)存喉前,如下圖所示:

image

我們接下來就看一下數(shù)據(jù)區(qū)没酣。

繼續(xù)重溫7種數(shù)據(jù)類型:
Number,String卵迂,Boolean裕便,null,undefined:簡(jiǎn)單類型數(shù)據(jù)存在stack內(nèi)存中
Object:復(fù)雜類型數(shù)據(jù)存儲(chǔ)在heap內(nèi)存中

JS里所有的數(shù)字都是以64位浮點(diǎn)數(shù)儲(chǔ)存的见咒,16位存儲(chǔ)一個(gè)字符偿衰,所以在棧內(nèi)存內(nèi)都是64位01

我們來看看代碼:

//代碼
var a = 1
var b = 2
var c = true
// 代碼區(qū)              //stack
a                      0000…1…000000(64位浮點(diǎn)數(shù))
b                      0000…10…000000(64位浮點(diǎn)數(shù))
c                      100000000000……(64位浮點(diǎn)數(shù))

假設(shè)b=a,那就把a(bǔ)存的東西復(fù)制然后覆蓋到b儲(chǔ)存的地方。

再復(fù)雜一點(diǎn)改览,我們存儲(chǔ)復(fù)雜類型呢哎垦?也就是heap內(nèi)存里是怎樣呢?

// 代碼
var o = {
    name:'vava'
    age:18
}
o.gender = female
var o2 = {
    name:'yaya'
}
// 代碼區(qū)              //stack                  //heap
   o                   ADDR 88                 88:name:'vava'  
                                                  age:18
                                                  gender:'female'
   o2                  ADDR 26                 26:name:'yaya'

就像上述代碼恃疯,當(dāng)需要存儲(chǔ)字符的時(shí)候漏设,一行64位浮點(diǎn)數(shù),只能存儲(chǔ)4個(gè)字符今妄,非常浪費(fèi)且再添加屬性的時(shí)候就需要整體移動(dòng)下位的代碼郑口,很麻煩鸳碧,所以我們就在棧內(nèi)存里存儲(chǔ)一個(gè)地址,地址隨意犬性,但是這個(gè)地址指向heap內(nèi)存里相應(yīng)地址的位置瞻离,而我們需要儲(chǔ)存的內(nèi)容就寫在這里。

如果上述代碼內(nèi)乒裆,令o2=o, 那么同理套利,復(fù)制o的存儲(chǔ)內(nèi)容覆蓋到o2,也就是,o2的地址26被88覆蓋鹤耍,o2指向88肉迫,將引用88的內(nèi)容。

上述代碼即下圖:


image

ps:沒有什么是畫個(gè)圖解決不了的 ~ 遇到內(nèi)存空間問題稿黄,放心大膽的畫圖看看吧~

二喊衫、關(guān)于垃圾回收(GC)的那點(diǎn)事兒

1、Garbage Collection

我們已經(jīng)知道程序的運(yùn)行需要內(nèi)存杆怕。但是如果我們不再用到的內(nèi)存呢族购?如果不釋放,內(nèi)存占用越來越高陵珍,輕則影響系統(tǒng)性能寝杖,重則導(dǎo)致進(jìn)程崩潰。所以我們頁面用完就要釋放內(nèi)存互纯,讓其重新分配瑟幕。

如果一個(gè)對(duì)象沒有被引用,它就是垃圾伟姐,就會(huì)被回收。

// 代碼
var a = {name:'vava'}
var b = {name:'haha'}
// 代碼區(qū)                    // stack                  //Heap
    a                        ADDR 67                   67:vava
    b                        ADDR 17                   17:haha
// 當(dāng)a = b 
//代碼區(qū)                     // stack                  //Heap
    a                        ADDR 17                   (67:vava 被回收)
    b                        ADDR 17                   17:haha

可以看到亿卤,一開始a指向地址67愤兵,b指向地址17,當(dāng)a=b排吴,b的地址覆蓋過來秆乳,a和b指向地址17,地址67不再被引用,所以被回收钻哩。

2屹堰、內(nèi)存泄漏:不再用到的內(nèi)存,沒有及時(shí)釋放街氢,就叫做內(nèi)存泄漏(memory leak)

即由于瀏覽器的一些bug扯键,使得該被標(biāo)記為垃圾回收的東西沒有被當(dāng)做垃圾回收,內(nèi)存始終被占用著珊肃,沒有被釋放荣刑。

比如我們有一個(gè)document.body.onclik事件占用著內(nèi)存馅笙,沒有被清除,我們可以如下操作:

window.onunload = function(){
       document.body.onclik = null
}

注意厉亏,還有其他事件的話董习,其他事件都要=null。

3爱只、深拷貝V.S淺拷貝

var a = 1
var b = a
    b = 2
    a
    // 1

像這樣皿淋,b改變不會(huì)影響a,這就是深拷貝

對(duì)于所有的基本類型恬试,賦值都是深拷貝窝趣,所以我們來研究對(duì)象。

image
// 代碼區(qū)         // stack              //heap
    o             ADDR 9              9:name:'yaya'
    b             ADDR 9              // 也指向地址9

如上述忘渔,b.name = 'maya' 高帖,改變name的值,是在地址9中完成畦粮,這時(shí)o和b都指向地址9散址,所以,引用a,得到的也是改變后的宣赔。

像這樣预麸,b的改變會(huì)導(dǎo)致a的改變,就是淺拷貝儒将。

三吏祸、包裝對(duì)象

我們都知道對(duì)象是一種復(fù)合值:它是屬性或已命名值得集合。當(dāng)屬性值為一個(gè)函數(shù)時(shí)稱之為方法钩蚊。那么我們來看一下下圖:

image

我們可以看到字符串也同樣具有屬性和方法贡翘,但字符串既不是對(duì)象,怎么會(huì)有屬性呢砰逻?

其實(shí)只要我們引用上述字符串的屬性鸣驱,JavaScript就會(huì)將字符串值通過調(diào)用new String(s)的方式轉(zhuǎn)換成對(duì)象,之歌對(duì)象繼承了字符串的方法蝠咆,處理屬性的引用踊东。屬性引用結(jié)束,這個(gè)新創(chuàng)建的對(duì)象就被銷毀刚操。

我們可以這樣來想闸翅,我們既想用簡(jiǎn)單類型,又想獲得對(duì)象的方法菊霜,然后Branden Eich就想了個(gè)辦法坚冀,我們可以建立臨時(shí)對(duì)象,獲取屬性后返回給調(diào)用鉴逞,然后銷毀就可以遗菠。

var n = 1
n.toString()
// '1'
// 代碼區(qū)             // stack               // heap
   n                     1
   temp                  ADDR 77             77: 1
                                                 toSting()
                                                 valueOf()

temp就是臨時(shí)對(duì)象联喘,當(dāng)調(diào)用toString返回后,temp馬上被銷毀了辙纬。

其他的數(shù)據(jù)類型也是一樣豁遭,

image

調(diào)取屬性的背后都是這樣一套操作。

四贺拣、什么是原型

從上一節(jié)包裝對(duì)象我們可以看到蓖谢,利用new創(chuàng)建并初始化一個(gè)新對(duì)象,運(yùn)算符new后面跟著一個(gè)函數(shù)調(diào)用譬涡,叫做構(gòu)造函數(shù)闪幽。

var s = new String('hello')

如上述代碼,s就是被創(chuàng)建的實(shí)例對(duì)象涡匀,new運(yùn)算符后跟著的String(注意這里開頭必須大寫來和string區(qū)分)就是構(gòu)造函數(shù)盯腌。

我們可以看到從實(shí)例對(duì)象中調(diào)用的屬性,但是他們都具有的屬性比如toString以及valueOf等陨瘩,如果每個(gè)實(shí)例對(duì)象都在heap存儲(chǔ)處生成這些都有的屬性豈不是很費(fèi)內(nèi)存腕够?所以我們可以共有屬性歸攏起來,然后通過 __ proto__ 來指向共有屬性。

var o = new Object({name:'vava'})
//代碼區(qū)             // stack             //heap
   o                    ADDR 89           89: name:vava             object:toString()
                                              __proto__:object              valueOf()
                                                                            ……

所以在公有屬性調(diào)用時(shí)就是上述操作

image
image

通過在console.log打印出來,我們可以看到一個(gè)哈希锤窑,__ proto__指向了object去調(diào)用屬性。

object公有屬性是所有對(duì)象的公有屬性大诸,但是還有一些比如是只有number數(shù)據(jù)類型共同具有的,或者只有String數(shù)據(jù)類型共同具有的呢贯卦?我們來看一下:

image
image
image

明白了么资柔?也是一樣的操作去調(diào)用,只不過實(shí)例對(duì)象通過__ proto__先調(diào)用number公有屬性.如果沒有要調(diào)用的屬性撵割,就繼續(xù)通過__ proto__調(diào)用object公有屬性贿堰。

看到這你可能想問了,說了這么多跟原型有什么關(guān)系呢睁枕?當(dāng)然有了官边,公有屬性就是原型沸手,每一個(gè)對(duì)象都在原型繼承屬性外遇,所有的函數(shù)對(duì)象都具有原型對(duì)象,并且通過JavaScript代碼Object.prototype來獲得對(duì)屬性的引用契吉。

image

看上圖就可以了解到跳仿,構(gòu)造函數(shù)通過new運(yùn)算符創(chuàng)建了實(shí)例對(duì)象,實(shí)例對(duì)象內(nèi)通過__ proto__指向了原型對(duì)象即Object.prototype.

image

當(dāng)你聲明了一個(gè)對(duì)象捐晶,JS引擎除了在棧里搞了一個(gè)哈希菲语,還干了一件事妄辩,那就是把__ proto__指向了公有屬性,即原型山上。

五眼耀、什么是原型鏈

原型鏈:每一個(gè)對(duì)象都有自己的原型對(duì)象,原型對(duì)象本身也是對(duì)象佩憾,原型對(duì)象也有自己的原型對(duì)象哮伟,這樣就形成了一個(gè)鏈?zhǔn)浇Y(jié)構(gòu),叫做原型鏈妄帘。

比如下面這個(gè)原型鏈

實(shí)例對(duì)象s---(通過__ proto__)-->String.prototype---(通過__ proto__)---->Object.prototype----(通過__ proto__)----->null

對(duì)這個(gè)實(shí)例化對(duì)象而言楞黄,訪問對(duì)象的屬性,是首先在對(duì)象本身去找抡驼,如果沒有鬼廓,就會(huì)去他的原型對(duì)象中找,一直找到原型鏈的終點(diǎn)null致盟;根據(jù)定義碎税,null沒有原型,并作為這個(gè)原型鏈中的最后一個(gè)環(huán)節(jié)勾邦。

六蚣录、__ proto__和prototype

var 對(duì)象 = new 函數(shù)() , 故而

對(duì)象.__ proto__ === 函數(shù).prototype

__ proto __是實(shí)例對(duì)象的公有屬性引用眷篇,prototype是函數(shù)對(duì)象公有屬性的引用萎河。

1、函數(shù).prototype.__ proto __ ===Object.prototype

image

這里我們循著原型鏈就可以理解蕉饼,當(dāng)函數(shù)是Object虐杯,它的原型對(duì)象自然指向了原型鏈的終點(diǎn)null。

2昧港、函數(shù).__ proto __ === Function.prototype

image

Function是Object的構(gòu)造函數(shù)擎椰,所以函數(shù)Object或者函數(shù)Number等等,他們的__ proto __ 都指向了Function.prototype

從上圖中有清晰的對(duì)比创肥,實(shí)例對(duì)象o的__ proto __ 就指向了構(gòu)造實(shí)例對(duì)象函數(shù)的原型對(duì)象达舒,即Object.prototype.而函數(shù)Object的__ proto __ 就指向了它的構(gòu)造函數(shù)Function.prototype.

所以接下來兩個(gè)推論我們也很容易理解了:

3、Function.__ proto __ === Function.prototype

4叹侄、Function.prototype.__ proto __ === Object.prototype

(這里Function.prototype是對(duì)象巩搏,原型對(duì)象)

tips:
1、每一個(gè)構(gòu)造函數(shù)都擁有一個(gè)prototype屬性趾代,這個(gè)屬性指向一個(gè)對(duì)象贯底,也就是原型對(duì)象。當(dāng)使用這個(gè)構(gòu)造函數(shù)創(chuàng)建實(shí)例的時(shí)候撒强,prototype屬性指向的原型對(duì)象就成為實(shí)例的原型對(duì)象禽捆。
2笙什、原型對(duì)象默認(rèn)擁有一個(gè)constructor屬性,指向指向它的那個(gè)構(gòu)造函數(shù)(也就是說構(gòu)造函數(shù)和原型對(duì)象是互相指向的關(guān)系)胚想。
3琐凭、每個(gè)對(duì)象都擁有一個(gè)隱藏的屬性[[prototype]],指向它的原型對(duì)象浊服,這個(gè)屬性可以通過 Object.getPrototypeOf(obj)obj.__proto__ 來訪問淘正。
4、實(shí)際上臼闻,構(gòu)造函數(shù)的prototype屬性與它創(chuàng)建的實(shí)例對(duì)象的[[prototype]]屬性指向的是同一個(gè)對(duì)象鸿吆,即 對(duì)象.__proto__ === 函數(shù).prototype
5述呐、如上文所述惩淳,原型對(duì)象就是用來存放實(shí)例中共有的那部分屬性。
6乓搬、在JavaScript中思犁,所有的對(duì)象都是由它的原型對(duì)象繼承而來,反之进肯,所有的對(duì)象都可以作為原型對(duì)象存在激蹲。
7、訪問對(duì)象的屬性時(shí)江掩,JavaScript會(huì)首先在對(duì)象自身的屬性內(nèi)查找学辱,若沒有找到,則會(huì)跳轉(zhuǎn)到該對(duì)象的原型對(duì)象中查找环形。

就到這里啦~ 歡迎糾錯(cuò) ~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末策泣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抬吟,更是在濱河造成了極大的恐慌萨咕,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件火本,死亡現(xiàn)場(chǎng)離奇詭異危队,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)钙畔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門茫陆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刃鳄,你說我怎么就攤上這事盅弛∏睿” “怎么了叔锐?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵挪鹏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我愉烙,道長(zhǎng)讨盒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任步责,我火速辦了婚禮返顺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔓肯。我一直安慰自己遂鹊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布蔗包。 她就那樣靜靜地躺著秉扑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪调限。 梳的紋絲不亂的頭發(fā)上舟陆,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音耻矮,去河邊找鬼秦躯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛裆装,可吹牛的內(nèi)容都是我干的踱承。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼哨免,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼勾扭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起铁瞒,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤妙色,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后慧耍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體身辨,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年芍碧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了煌珊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泌豆,死狀恐怖定庵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤蔬浙,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布猪落,位于F島的核電站,受9級(jí)特大地震影響畴博,放射性物質(zhì)發(fā)生泄漏笨忌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一俱病、第九天 我趴在偏房一處隱蔽的房頂上張望官疲。 院中可真熱鬧,春花似錦亮隙、人聲如沸途凫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颖榜。三九已至,卻和暖如春煤裙,著一層夾襖步出監(jiān)牢的瞬間掩完,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工硼砰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留且蓬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓题翰,卻偏偏與公主長(zhǎng)得像恶阴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子豹障,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容