今天再學習ts的枚舉類型的時候,對ts解釋成js後的代碼有疑問。帶着疑問,一步步追根溯源,最終有了這篇文章。segmentfault
這是一段簡單的ts代碼解析成js代碼的例子。數組
Direction[Direction["Up"] = 1] = "Up";
Direction[Direction["Down"] = 2] = "Down";
Direction[Direction["Left"] = 3] = "Left";
Direction[Direction["Right"] = 4] = "Right";
複製代碼
這幾句代碼,引發了個人注意。 由於,這四句代碼中,有8個賦值操做。學習
賦值操做1:Direction["Up"] = 1
測試
賦值操做2:Direction[1] = "Up"
ui
賦值操做3:Direction["Down"] = 2
spa
賦值操做4:Direction[2] = "Down"
3d
賦值操做5:Direction["Left"] = 3
code
賦值操做6:Direction[3] = "Left"
cdn
賦值操做7:Direction["Right"] = 4
對象
賦值操做8:Direction[4] = "Right"
爲何會有Direction[1] = "Up"
這類賦值呢?
經查閱資料發現,原來每一個賦值語句都有返回值的(叫返回值可能不太準確,先這麼叫着吧...😄)
具體是這樣的:
能夠看到,聲明變量的時候,返回值是undefined
, aaa=2
的時候,返回值是2
.
因此賦值的時候,是有返回值。而且這個返回值是賦值號=
右邊的值。以下圖:
因此,上面的ts解析出來的js代碼就能夠讀懂了。
Direction[Direction["Up"] = 1] = "Up"
// 至關於
Direction["Up"] = 1
Direction[1] = "Up"
複製代碼
有了上面的認識後,引伸出了一個新的問題。 連續賦值的時候,第二個賦值的值來源是什麼? 例如:
b = a = 10
複製代碼
之前的認知(多是學過C和C++的緣由,留下了印象。),賦值語句是從右往左執行的。 上面的語句是默認是 10賦值給a,a賦值給b。很簡單,咱們知道a
的值是10
,b
的值也是10
。 可是,由於a = 10
有返回值(10)
,因此b
的值是從a
來的仍是從這個(10)
來的呢?
咱們來看一個例子:
var a = { name: 'HoTao' }
a.myName = a = { name: '你好' }
console.log(a) // { name: '你好' }
console.log(a.myName) // undefined
複製代碼
按咱們正常的理解,連續賦值語句,從右往左賦值,那麼應該是 a = { name: '你好' }
, a.myName = a。因此,輸出的結果應該都是{ name: '你好' }
,可是,事與願違,並非這個結果。
這是怎麼回事啊?
因而作了如下測試:
前置知識:JS中,對象和數組屬於引用類型,在將它們賦值給別人時,傳的是內存地址
var a = { name: 'HoTao' }
var b = a
b.name = '你好'
console.log(a.name) // 你好
console.log(b.name) // 你好
複製代碼
上述代碼解析:
{ name: 'HoTao' }
的內存地址(a1
)a1
)b.name = '你好'
這句代碼,修改了內存地址a1
所指向的對象的name
的值。因此a和b同時受到了影響。再看一個例子:
var a = { name: 'HoTao' }
var b = a
b = { name: '你好' }
console.log(a.name) // HoTao
console.log(b.name) // 你好
複製代碼
當執行b= { name: '你好' }
給b賦了一個新的內存地址(a2
),因此,變量a和變量b已經指向不一樣的地址,他們兩個如今毫無瓜葛了。
咱們再反過來看一下連續賦值的問題:
var a = { name: 'HoTao' }
var b = a
a.myName = a = { name: '你好' }
console.log(a) // { name: '你好'}
console.log(a.myName) // undefined
console.log(b) // { name: 'Hotao', myName: { name: '你好' } }
console.log(b.myName) // { name: '你好' }
複製代碼
代碼解析:
{ name: 'HoTao' }
的內存地址(叫a1
)a = { name: '你好' }
。這個時候變量a指向的內存地址變成了值爲{ name: '你好' }
的內存地址(叫a2
),而且這句賦值語句有返回值,返回值爲 { name: '你好' }
a.myName = { name: '你好' }
,這個時候a.myName
中的a仍是指向a1
。(由於js是解釋執行的語言,在解釋器對代碼進行解釋的階段,就已經肯定了a.myName
的指向)a.myName = { name: '你好' }
以前,就已經對a.myName
再內存地址a1所指向的對象中建立了一個屬性名爲myName
的屬性,默認值就是咱們常見的undefined
。因此,執行a.myName = { name: '你好' }
的結果,就是往內存地址爲a1
所指向的對象中的myName
屬性賦值。a.myName
爲undefined
,是由於此時變量a
指向的地址是(a2)
,a2
中沒有myName
這個屬性b.myName
爲{ name: '你好' }
,是由於b指向的內存地址是(a1),而,a1存在myName
這個屬性,而且還成功賦值了。因此,正常輸出a.myName = a = { name: '你好' }
時,因爲連續賦值語句是從右自左,先執行a = { name: '你好' }
,執行後 a 在內存中的地址已經改變了a.myName = { name: '你好' }
時,因爲解析時已經肯定了 a.myName
所指向的地址爲變量a原來的內存地址(a1
) ,因此 a.myName = { name: '你好' }
是給變量a
原來的內存地址(a1
)指向的變量賦了值。console.log(a.myName)
,因爲 a 如今是指向新地址(a2
),而咱們只給變量a的舊地址(a1
)的 a.myName
賦了值,新地址a2
中沒有a.myName
這個屬性。