object,你的腰间盘为啥这么突出?——数据类型转换中object的不同之处及原因探究

突出.jpg

在我的上一篇文章中JavaScript的家族谱(数据类型)已经介绍了关于object和普通类型在数据类型呈现方式上的不同,object是复杂类型,不同于其他基本类型的不可再分割,object内部可能有函数、数组和其他数据类型混合其中。而本文将从另外一个角度——储存方式来阐述object的特殊之处。(注意本文不谈symbol)

数据类型之间的相互转化

为什么要先谈这个,因为基本类型和复杂类型(object)的存储方式并不一样,在数据转化过程中这个事实最为明显。

其他基本类型转化为String(特别注意object)

方式一 :原始数据.toString()

var a = 1
a.toString()
"1" //数值转化为字符串

var a = true
a.toString()
"true" //布尔值转化为字符串

var a = null
a.toString()
VM426:1 Uncaught TypeError: Cannot read property 'toString' of null
    at :1:3 
//null不能直接转化为字符串,因为null没有toString这个属性

var a = undefined
undefined
a.toString()
VM446:1 Uncaught TypeError: Cannot read property 'toString' of undefined
    at :1:3
(anonymous) @ VM446:1
// 同理undefined也没有

var a = {}
undefined
typeof a
"object"
a.toString()
"[object Object]"
// 突出1:要么就老老实实转化,要么就像null一样说没有不就好了,给个奇怪的返回值

方式二:String(原始数据)

var a = 1
String(a)
"1"
//剩下的就省略了,结果和上面的相同

方式三:原始数据 + "
这种方式并不是调用属性,而是让原始数据匹配字符串,所以null和undefined也是可以转化的

a + ''
"1"

null+''
"null" 

undefined+''
"undefined"

var a = {key:1}
undefined
a+""
"[object Object]" //行,你NB

其他类型转化为布尔值

五个falsy值

这里说一下,所有数据类型转化成布尔值,其中只有5个会被转化为false:

  1. 0
  2. NaN
  3. ''(空字符串)
  4. null
  5. undefined

方法

方法一:Boolean(原始数据)

Boolean(a)
true
Boolean(0)
false
Boolean(NaN)
false
Boolean('')
false
Boolean(null)
false
Boolean(undefined)
false
Boolean({})
true // 突出2:object啥也没有也是true

方法二:!!原始数据(!是取反的意思)

!!1
true
!!null
false
// 其他的就不举例了

其他类型转化成数值(这个不突出,主要是有NaN背锅)

方法

方法一:Number(原始值)

var a = {key :'1'}
typeof a
"object"
Number(a)
NaN
Number('1')
1
Number('1sss')
NaN //这个方法只要字符串中有非数字的就返回NaN
Number(true)
1 //后面从储存的角度来解释
Number(false)
0  //后面从储存的角度来解释
Number(null)
0
Number(undefined)
NaN

方法二:parseInt(原始值,'进制数')--注意是十进制时可以不写,系统默认是存在的
这个方法和上面不同之处在于这个命令只取整,除了数值和能转化的字符串,其他的全是NaN。另外,他能提取字符串前面的数字,具体用法见parseInt

parseInt('1sss')
1 //

方法三:parseFloat('原始值'),具体用法见parseFloat

方法四(简单写法):原始数据 - 0
方法五(更简单写法):+ 原始数据

true -0
1
'a' -0
NaN
+ true
1
+'a' 
NaN

(symbol转化我们就不谈及,至于怎么转化成null或者undefined,请把你的需求写在评论区,让大家一起来深入探讨)

那么其他数据类型转化成对象怎么做?

答案是:obeject(原始值)

var a =1
typeof(a)
"number"
b = Object(a)
typeof b
"object" //给值一个对象包裹器

讨论(开始闲扯)

现在我们来回顾一下上面所有的转化过程,只从数据的展现形式什么也看不出来。这时候我们只能从数据的储存方式来看看。

简单的存储模型

JS有两个储存数据的区域(Stack和Heap),基本数据类型几乎全部存放在Stack里面,如此我们假设一个简单模型 stack里面保存数据都是用哈希表的方式保存(key:value),Heap保存数据的方式是堆(无序存放,只有地址)。--比实际情况简单便于理解

  1. 模型中基本类型数据的储存方式(注意所有的数据都是用二进制表示)
    布尔值(booleantrue保存的时候就是1false就是0
    字符串(String)把引号内部的内容用utf-16保存,单双引号就是单双引号,前后各一个表示开头结尾(真实情况并非如此);
    数值(number)用二进制表示;
    null:null用二进制表示;
    undefined:undefined用二进制表示;
    以上全部保存在Stack里面

  2. 模型中复杂类型数据(object)的储存方式(突出3:存的地方有两个)
    object保存在Stack里面的是object Object(二进制表示) +地址(二进制表示),Heap保存的是这个地址引用的数据,也就是object里面的内容。

  3. String等命令只在Stack里面寻找数据。

推演

  1. 基本类型之间转化在储存的角度上的事件(简单模型中)
    那么所有的基本类型的转化,只需要把他原始的数据修改或者全部删除然后写入,读取的时候把要读取的部分取出来就可以了。

  2. object在上文中的突出点的解读
    突出1:为什么字符串转化是"[object Object]"

var a = {}
undefined
typeof a
"object"
a.toString()
"[object Object]"

看看我们的对象在Stack里面是怎么保存的:object Object(二进制表示) +地址(二进制表示)。字符串没有解读地址而已,而每一个对象都是这个格式保存着。这个模型也就解释了突出2:空的对象转成布尔值也是true,因为里面写着object Object,不是5个Falsy值,返回true。至于地址有没有,对不起,布尔值无法解读。

  1. 综上看,实际上object和其他数据类型转化这涉及到了储存数据方式的变化

拓展--深拷贝和浅拷贝

深拷贝就是我变你也不变,浅拷贝就是我变你也变
用上面这个简单模型可以很好地解决深浅拷贝的问题
例如

var a = {name:'a'}
var b = a
b.name = 'b'
// a = ?

模型分析:

  1. Stack存储着哈希表a:object Object 地址1
  2. Heap里面存储着堆数据:地址1{name:'a'}
  3. b = a 使又增加一个哈希表b:object Object 地址1,这时b变量也是引用地址1
  4. b.name(这是object的命令)调用地址1里面的属性name,让name:'a'变成name:'b',此时Heap里的地址1变成地址1{name:'b'}
  5. 然后问a,a的stack并没有变化a:object Object 地址1,而引用的地址1数据是地址1{name:'b'},所以a = 'b'。这就是浅拷贝,你变我也变。变的原因在于地址里的内容变了。

再举一个深拷贝的例子

var a = 1
var b = a
b = 2
// a = ?

模型分析:

  1. Stack存储着哈希表a:1
  2. b = a使Stack中又多出一个哈希表b:1
  3. b = 2使Stack中b的value由1变成2,哈希表为b:2
  4. 问a是多少,哈希表a:1并没有变化,所以a=1。这就是深拷贝,你变我不变。

由这两个例子可以看出,只涉及Stack的数据变化并不会发生浅拷贝,所以基本类型的拷贝全是深拷贝

文末要谢谢方方老师精彩的知识讲解,这篇文章中大部分知识借鉴方方老师的知识,另外因为内存图是方方老师原创,而且不能加动态图,所以这里使用了简单模型代替,如有错误欢迎指正。

你可能感兴趣的:(object,你的腰间盘为啥这么突出?——数据类型转换中object的不同之处及原因探究)