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}
逐句翻譯吧:
var a = {n: 1}
取一個盒子名a,建一個房子,鑰匙放到盒子a里面;
房子里有個盒子n,放着1。
var b = a
取一個盒子名b,盒子b偷看一下盒子a,哇哦,一把鑰匙,
盒子b里面也有了這把鑰匙,也能去訪問這個房間了。
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}
再逐句翻譯:
var xiaoMing = {moneyBox: 1}
小明有一把房鑰匙,這個房子里有個錢櫃,里面放着1元錢。
var xiaoQiang = xiaoMing
小強偷偷復制了一把小明的房鑰匙,從此他也可以進出小明的房子。
xiaoMing.keyBox = xiaoMing = {moneyBox: 200}
小明在此房子里做了一個鑰匙櫃,這個鑰匙櫃能自動生成一把小明口袋里的鑰匙(xiaoMing.keyBox = xiaoMing
的作用,可能有點超現實),
但是小明想,我口袋里的鑰匙現在就是這個房子的鑰匙,放在我的鑰匙櫃里也沒什么意義,
不如這樣吧,我再買一套房子,把口袋里的鑰匙替換成新房子的鑰匙,那這個鑰匙櫃里不就存下新房子的鑰匙了嗎。
於是,小明果斷又買了一套房子,這個房子里也有個錢櫃,里面放200元錢。
小明正准備回舊房子呢,突然想起來,自己口袋里的鑰匙已經替換成新房子的鑰匙了,
現在他只能進新房子,而進不去舊房子了,郁悶...
再說小強,
小強當初復制的是小明舊房子的鑰匙,所以小強依然能來到這個舊房子,
進來后發現,多了一個鑰匙櫃,並且里面放着一把鑰匙,
沒錯,這就是小明新房子的鑰匙。
所以現在的局勢很明朗了:
小明只有新房子的鑰匙,只能進新房子(而且他應該覺得舊房子已經沒人能進去了)。
而小強有小明舊房子的鑰匙,
同時這個房間里還有小明的新房子的鑰匙,所以小強也能進小明的新房子。
用代碼表示,就相當於
xiaoMing = {moneyBox: 200} xiaoQiang = { moneyBox: 1, keyBox: {moneyBox: 200} }
感謝大家對此篇文章提出任何建議。