今天再学习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
这个属性。