賦值就是把某一個值賦給變數。
我憑什麼要把值賦給變數?
變數相當於名字。
拿我舉例,如果我沒有名字,當別人叫我幫忙的時候,就只能說:
“那個個頭不高、顏值爆表、頭髮很硬、坐在角落的小哥哥,過來幫我一下唄!”
而有名字的情況是:
“小強快來!”
可見變數賦值的意義在於便於使喚。
基本型別的賦值
基本型別的賦值,好比在每個盒子裡放東西。
直接賦值
例如,
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}
}
複製程式碼
感謝大家指出文章中的諸多bug,