javascript基本類型和引用類型


javascript賦值

賦值就是把某一個值賦給變量。

我憑什么要把值賦給變量?

變量相當於名字。

拿我舉例,如果我沒有名字,當別人叫我幫忙的時候,就只能說:

“那個個頭不高、顏值爆表、頭發很硬、坐在角落的小哥哥,過來幫我一下唄!”

而有名字的情況是:

“小強快來!”

可見變量賦值的意義在於便於使喚。

基本類型的賦值

基本類型的賦值,好比在每個盒子里放東西。

直接賦值

例如,

var a = '手機'
console.log(a)  // '手機'

相當於,給一個盒子起名為a,並放進去一個字符串‘手機’。

將變量賦值給變量

例如,

var a = '蘋果'
var b = a
a = ''
console.log(a)  // '' 
console.log(b)  // '蘋果' 

var b = a時,相當於:

給一個盒子起名為b,並偷看一下盒子a里面放的什么,然后自己里面放同樣的東西。

a = ''時,相當於:

盒子a里面原來的東西不要了,改放一個''進去。

但是,這並不會影響盒子b中的值。

因為,在盒子a里面發生變化的時候,盒子b並不關心。

只有將變量a賦值給變量b,即b = a時,

b才會出現偷看行為,進而使自身的值和a的值一樣。

可見,賦值就是復制,特點是賦值過后互不影響

好比我把感冒復制給朋友,我吃完葯好了,並不能代表他也好了。

引用類型的賦值

引用類型的賦值,看上去是共享,實際還是復制。

棧和堆

首先,基本類型采用棧存儲,一個蘿卜一個坑。

前面也說到,基本類型的賦值,就像在每個盒子里放東西,比如,

我在a盒子里放個香蕉、

我在b盒子里放個手機、

我在c盒子里放個老鼠、

我在d盒子里放個房子...

問題出現了,

之前,我們在盒子里放一些很傻很天真的東西,基本沒問題,

但是,盒子里可以放房子嗎?

這時候,我們進一步認識一下我們的盒子(棧存儲)有什么特點?

  • 盒子按順序排列
  • 每個盒子只能存放一件物品
  • 每個盒子都不是很大

顯然,我們的盒子不足以裝下房子。

而且常識告訴你:

房子里倒是能很多的盒子,因此房子就是堆存儲。

堆存儲就像找了一片空地,然后在上面盡情放盒子(請不要想到《我的世界》)。

特點就是存的多、存的亂。

理解堆和棧的區別,也可以參照公交車,

每個座位,

有序、只能坐一人、大小有限...

好比棧;

其它地方,

能擠下一堆人,很亂...

好比堆。

對象采用的就是堆存儲。

什么是引用類型

我們知道,javascript引用類型包括對象。

先隨便來一個對象:

var a = {}  // 堆存儲,相當於建了個房子,名為a

再隨便來一個數字:

var b = 10  // 棧存儲,相當於選了個盒子,名為b

再隨便來一下:

b = a
console.log(b)  // {}

太隨便就容易出問題:

原本b是一個正經盒子,a是一個正經房子,

執行b = a之后,b變成了{}

根據上面說的基本類型的賦值,這是不是在說

盒子b雖然之前只是一個小盒子、

但是偷看了一眼房子a之后、

自己發奮圖強變成像房子a一樣大的盒子、

並復制了房子a里面的東西?

不是的。

盒子b並沒有為了復制房子a里面的東西而變大,

實際上,a其實從頭到尾都只是一個盒子、而不是房子,僅管我們看到是a被賦值了一個堆存儲的對象。

為什么呢?

因為引用類型只暴露一個地址給我們,

操作引用類型的數據時(比如賦值),我們也只是在操作這個地址、引用、指針...愛叫啥叫啥。

這就好比,你送女朋友戒指,可以直接放到她手里,這是基本類型;

你送女朋友法拉利,並非把法拉利直接放到她手里,而是把車鑰匙放到她手里,這就是引用類型;

你送女朋友房子,並非把房子直接交給她(除非你們是蝸牛),而是把房鑰匙交給她,這也是引用類型。

從賦值上來進一步認識引用類型:

直接賦值

開始說過,

變量相當於名字。

另一個事實是,

我們只能給盒子起名字,不能給房子起名字,但是我們能拿到房子的鑰匙

這就是為什么說,a其實從頭到尾都只是一個盒子。

var b = 10相當於取一個盒子叫b,然后里面放啥啥啥,這沒有問題;

由於我們不能給房子起名字,

所以var a = {}肯定不是說:取了一個房子叫a,然后里面放啥啥啥。

其實,和var b = 10的解釋一模一樣,var a = {}也相當於

取了一個盒子叫a,然后里面放啥啥啥。

只不過是,b盒子里面放很傻很天真的東西,

a盒子里面放很傻很天真的鑰匙,這把鑰匙對應一個大房子。

引用類型的直接賦值就是把鑰匙放到對應盒子里。

為什么只給盒子起名字?

代碼中,會出現很頻繁的變量賦值行為,

為了保證運行速度,這些行為被優先安排在一批有序的盒子中,偷看、復制、再偷看...

可以說,我們大部分時間在玩盒子。

可想而知,如果換成玩房子的話,要費多大的力氣。

但是呢,房子在我們的程序中也有着不可或缺的作用,

這時候它就暴露出一個可以找到它的鑰匙,相當於它的聯系方式

然后放進相應的盒子里,並說:

當你需要我的時候,我會在你身邊。

正是因為這樣,我們既便於使喚房子,又便於操作房子里的東西。

將變量賦值給變量

var obj = {name: '小強'}
var obj2 = obj
console.log(obj2)   // {name: '小強'}

首先,var obj = {name: '小強'}是引用類型的直接賦值,

相當於找到一個盒子名obj,把{name: '小強'}這個房子的鑰匙放進盒子obj里面。

obj2 = obj可以說和基本類型的變量賦值給變量一樣,

盒子obj2偷看一眼盒子obj中放的東西,復制一下,自己里面放同樣的東西。

喜出望外的是,竟然是一把對應某個房間的鑰匙!

這時,obj2就和obj一樣,都能訪問這把鑰匙對應的房間了。

所以引用對象的賦值都是操作鑰匙

插播廣告: {name: '小強'} == {name: '小強'}嗎?

答案是否定的。

這相當於在問,這兩個房子的鑰匙相同嗎?

這兩個房子只是在裝修上極其相似,我們不能通過將自己的房子布置得和鄰居的房子一樣、就能得到鄰居家的房鑰匙,

程序也是如此。

引用類型賦值面試題

例一、

var a = {n: 1}
var b = a
a.x = a = {n: 2}
console.log(a.x)    // undefined
console.log(b.x)    // {n: 2}

逐句翻譯吧:

  1. var a = {n: 1}

取一個盒子名a,建一個房子,鑰匙放到盒子a里面;

房子里有個盒子n,放着1。

  1. var b = a

取一個盒子名b,盒子b偷看一下盒子a,哇哦,一把鑰匙,

盒子b里面也有了這把鑰匙,也能去訪問這個房間了。

  1. a.x = a = {n: 2}
  • 變量賦值是從右向左的
  • 對象用.賦值的時候,就是操作對象的某個屬性,如果沒有該屬性就添加一個

我們通過盒子a中的鑰匙,來到了這把鑰匙對應的房間,

然后,我們在這個房間取一個盒子名x,並企圖在里面放東西。

執行到a.x = a的時候,我們還以為:

是把盒子a里面的鑰匙,放進我們所處房間的盒子X里面嗎?

差點就是了,但是后面又有=賦值。

根據變量賦值從右向左,

我們暫時先不在這個房間里的盒子x放東西,而是優先執行a = {n: 2}

這條語句顯然是引用類型的直接賦值,

即建了一個是{n: 2}這種樣子的房子,然后把鑰匙放到盒子a里面。

在棧和堆里面我們提到過:

每個盒子只能存放一件物品。

因此,盒子a首先會拋掉之前的鑰匙,然后存下這把新的鑰匙。

剛才我們拿着盒子a之前的鑰匙,進到對應的房間,企圖在房間的盒子x里放東西;

然后,發現后面還有賦值行為,所以優先執行后面的賦值行為。

但是,當時我們只是暫停,而不是放棄。

換句話說,是不忘初心,有始有終。

當初我們進的哪個房子,想在哪個盒子放東西,

現在我們就回到哪個房子,然后給哪個盒子放東西,

a.x = a可以看出,我們在盒子x里放的是盒子a的鑰匙,

在這個例子中,盒子a中現在的鑰匙就是能打開{n: 2}這間房子的鑰匙。

雖然說,

變量賦值是從右向左的。

但是,代碼執行是從左向右的

無論后面發生了多大變化,a.x都是最先執行的,它的作用就是:

通過鑰匙來到一個房間,取盒子x,然后等着在里面放東西。

后面的代碼,只能影響這個盒子里放什么東西。

於是,時過境遷:

盒子a里,拋棄舊房子鑰匙,放進了一把新房子鑰匙,等價於

a = {n: 2}

盒子b里,還是舊房子的鑰匙。

同時,因為在盒子a換鑰匙之前,我們通過盒子a拿到舊鑰匙來到舊房子,

並將盒子a換鑰匙之后的新鑰匙,放進了舊房子的盒子x里面,那盒子b等價於

b = {
    n: 1,
    x: {n: 2}
}

也可以將這個例子稍加處理:

var xiaoMing = {moneyBox: 1}
var xiaoQiang = xiaoMing
xiaoMing.keyBox = xiaoMing = {moneyBox: 200}
console.log(xiaoMing.keyBox)    // undefined
console.log(xiaoQiang.keyBox)    // {moneyBox: 200}

再逐句翻譯:

  1. var xiaoMing = {moneyBox: 1}

小明有一把房鑰匙,這個房子里有個錢櫃,里面放着1元錢。

  1. var xiaoQiang = xiaoMing

小強偷偷復制了一把小明的房鑰匙,從此他也可以進出小明的房子。

  1. xiaoMing.keyBox = xiaoMing = {moneyBox: 200}

小明在此房子里做了一個鑰匙櫃,這個鑰匙櫃能自動生成一把小明口袋里的鑰匙(xiaoMing.keyBox = xiaoMing的作用,可能有點超現實),

但是小明想,我口袋里的鑰匙現在就是這個房子的鑰匙,放在我的鑰匙櫃里也沒什么意義,

不如這樣吧,我再買一套房子,把口袋里的鑰匙替換成新房子的鑰匙,那這個鑰匙櫃里不就存下新房子的鑰匙了嗎。

於是,小明果斷又買了一套房子,這個房子里也有個錢櫃,里面放200元錢。

小明正准備回舊房子呢,突然想起來,自己口袋里的鑰匙已經替換成新房子的鑰匙了

現在他只能進新房子,而進不去舊房子了,郁悶...

再說小強,

小強當初復制的是小明舊房子的鑰匙,所以小強依然能來到這個舊房子,

進來后發現,多了一個鑰匙櫃,並且里面放着一把鑰匙,

沒錯,這就是小明新房子的鑰匙。

所以現在的局勢很明朗了:

小明只有新房子的鑰匙,只能進新房子(而且他應該覺得舊房子已經沒人能進去了)。

而小強有小明舊房子的鑰匙,

同時這個房間里還有小明的新房子的鑰匙,所以小強也能進小明的新房子。

用代碼表示,就相當於

xiaoMing = {moneyBox: 200}
xiaoQiang = {
    moneyBox: 1,
    keyBox: {moneyBox: 200}
}

 

感謝大家對此篇文章提出任何建議。

點擊查看此文章的最新版本和全部內容


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM