本博客会讲解下面几个概念
数据类型转换
内存图
垃圾回收和内存泄漏
浅拷贝与深拷贝
数据类型转换
1. 任何类型转String(字符)
-
xxx.toString(x)
注意null
和undefined
类型是不能用此方法将值转为String
的,object
则转成的结果不能与我们预期相符
下面是代码
(1).toString() // "1"
true.toString() // "true"
null.toString() // Cannot read property 'toString' of null 无法读取null的'toString'属性
undefined.toString() // Cannot read property 'toString' of undefined 无法读取undefined的'toString'属性
{}.toString() // Unexpcted token 突如其来的标记
({}).toString() "[object Object]"
-
String(x)
这个方法则可以将null
和undefined
转为相应的字符串,object
还是有之前的问题
下面是代码
String(1) // "1"
String(true) // "true"
String(null) // "null"
String(undefined) // "undefined"
String({}) // "[object Object]"
-
x+''
利用任何数值与空字符串相加都会变成字符串,所以可以用下面这个方法
下面是代码
1+'' // "1"
true+'' // "true"
null+'' // "null"
undefined+'' // "undefined"
var o = {}
o+'' // "[object Object]"
举个栗子,如果用1+'1',没办法去加,由于加号只能加相同类型的东西,所以会把左边的先.toString
,再去加字符串"1"
1+'1' // "11"
2. 任何类型转number(数值)
-
Number(x)
下面是代码
Number('12345') // 12345
Number(null) // 0
Number(undefined) // NaN
Number(true)/Number(false) // 1/0
var a = {}
Number(a) // NaN
-
parseInt(x,10)
'10'是你要转换的进制,不写的话默认为十进制。
parseInt()
会从头开始,能判断多少就多少,不能判断的则跳过。MDN资料
下面是代码
parseInt('12345',10) // 12345
parseInt(null,10) // NaN
parseInt(undefined,10) // NaN
parseInt(true,10)/parseInt(false,10) // NaN
var a = {}
parseInt(a,10) // NaN
-
parseFloat(x)
parseFloat()
函数解析一个字符串参数并返回一个浮点数。MDN资料
下面是代码
parseFloat('12345') // 12345
parseFloat(null) // NaN
parseFloat(undefined) // NaN
parseFloat(true)/parseFloat(false) // NaN
var a = {}
parseFloat(a) // NaN
-
x-0
任何东西减0也会得到你想要的值
下面是代码
'111'-0 // 111
null-0 // 0
undefined-0 // NaN
true-0/false-0 // 1/0
var a ={}
a-0 // NaN
-
+x
在值前面放个'+'号,能取它原本的值,以数字的形式展示
下面是代码
+'111' // 111
+null // 0
+undefined // NaN
+true/+false // 1/0
var a ={}
+a // NaN
3. 任何类型转Boolean(布尔)
-
Boolean(x)
用Boolean()函数转换
-
!!x
在要转换的值前加两个感叹号就行
关于布尔,其他值转换成布尔时只有
5个特殊值
为false
,其他都是ture,这五个false
值为:0
,NaN
,''
(空字符串),null
,undefined
。
4.null和undefined
因为这两个类型都只有一个值,所以不需要转换
内存图
要知道对象是由基本类型组成的,那基本类型又是由什么组成的呢?
比如,我写了"var a = 1"这行代码,计算机到底做了什么,这就需要画内存图来了解。
1.假设你有一个8G的内存条
2.操作系统开机即占用来512MB内存
3.Chrome 打开即占用 1G 内存
4.Chrome 各每个网页分配一定数量的内存
5.这些内存要分给页面渲染器、网络模块、浏览器外壳和 JS 引擎(V8引擎)
6.JS 引擎将内存分为代码区和数据区
我们只研究数据区
7.数据区分为 Stack(栈内存) 和 Heap(堆内存)
8.简单类型的数据直接存在 Stack 里
9.复杂类型的数据是把 Heap 地址存在 Stack 里,而本体数据则存在Heap里
遇到问题就画图,不要分析
-
问题1:
var a = 1
var b = a
b = 2
请问 a 显示是几?
这时我们就画一张图
var a = 1
我们可以看到a先被赋值为1,然后把1存到Stack内存里
var b = a
接着b把a的Stack里的内存复制里一份放在自己里
b = 2
b的值变成了2,但是并不影响a的值
所以a还是2
-
问题2
var a = {name: 'a'}
var b = a
b = {name: 'b'}
请问现在 a.name 是多少?
我们再画一张图
var a = {name:'a'}
新建一个对象,把它赋值给变量a,a其实只是记录了该对象对应的heap地址,并没有存该对象本身(a引用了该对象)
var b = a
实际上a只是把该对象的地址复制了一份给b而已,并没有新建一个对象
b = {name:'b'}
b被新赋值了一个对象,所以内存地址和对象都是新的,和a的对象没关系了,所以a.name的值为'a'
-
问题3
var a = {name: 'a'}
var b = a
b.name = 'b'
请问现在 a.name 是多少?
画内存图~
var a = {name:'a'}
新建变量a储存对象{name:'a'}的地址
var b = a
b复制改变量地址
b.name = 'b'
b把该变量的的name值变成'b'
所以a.name的值就是'b'了
- 问题4
var a = {name: 'a'}
var b = a
b = null
请问现在的a是什么?
内存图内存图
var a = {name:'a'}
新建一个对象,把对象的内存地址给变量a
var b = a
把a的对象内存地址复制给b
b = null
这里要注意,null
不是对象,所以b并不会影响a对应的该对象,而是把自己的内存从对象内存地址变成null
对应的16位二进制码
上面就是内存图的画法和介绍,平时多画内存图就不会出错了
垃圾回收和内存泄漏
如果一个对象没有被引用,它就是垃圾,将被回收
这个可以用内存图来理解
-
内存回收1
var a = {name:xxx}
var b = {name:yyy}
b = null
当b指向的对象在b变成null后,就没有被引用来,所以这个对象就变成来垃圾,浏览器会在不确定什么时候把它的内存回收掉(视总占内存大小和cpu频率选择)
-
内存回收2
var fn = function(){}
document.body.onclick = fn
fn = null
如上面这个代码,当浏览器把当前标签页关闭时,document就不存在了,没有人引用这些对象了,所以这三个对象都会回收
-
内存泄漏
但是IE6的垃圾算法有问题,它无法将之前三个对象标记为垃圾,所以会一直留着
你只要不关掉整个浏览器,你的内存会会充满垃圾,无法重复利用
这就是内存泄漏
浅拷贝与深拷贝
深拷贝
var a = 1
var b = a
b = 2 //这个时候改变 b
a 完全不受 b 的影响
那么我们就说这是一个深复制(深拷贝)
对于简单类型的数据来说,赋值就是深拷贝。
对于复杂类型的数据(对象)来说,才要区分浅拷贝和深拷贝。
浅拷贝
var a = {name: 'frank'}
var b = a
b.name = 'b'
a.name === 'b' // true
因为我们对b操作后,a也变了
这就是就是浅拷贝
所谓深拷贝,就是对Heap内存进行完全的拷贝,修改该其值不影响另一个值
var a = {name: 'jiujizi'}
var b = deepClone(a) // deepClone 还不知道怎么实现
b.name = 'b'
a.name === 'a' // true