之前的博客介绍了JS中的各种数据类型,那么在js当中,不同的数据类型怎么相互转换呢,以下是常见的不同数据类型转换的用法。
任意类型转字符串
- x.toString()
(1).toString() // '1'
true.toString() // 'true'
null.toString() // 报错
undefined.toString() // 报错
({}).toString() // "[object Object]"
toString
方法适用于number
类型和boolean
类型,但是对于null
和undefined
,使用这种方法会报错,对于object
,toSring
方法结果不正确,结果永远是"[object Object]"
。
console.log
其实就是用的这个原理,理论上 console.log
只能接受字符串。
console.log
打印出来的其实是有引号的但是浏览器没打,console.log(1)
打印出来的相当于 console.log((1).toString)
所以 console.log
如果发现不是字符串,会自动调用 toString
这个API 转化为字符串,object
的 key
也是会这样。
- String(x)
String()
该方法适用于所有数据类型(除了对象,结果同toString())
String(1) // '1'
String(true) // 'true'
String(null) // 'null'
String(undefined) // 'undefined'
String({}) // "[object Object]"
- x + ''
使用+运算符加上空字符串,功能更强大,可以 +null 和 +undefined。
1 + '' // '1'
true + '' // 'true'
obj+""; // "[object Object]"
'' + null // 'null'
'' + undefined // 'undefined'
1 + '1' // '11'
+
如果发现左右任意一边有字符串,它就会尝试把另外一边也变成字符串,+
总是希望可以得到两个字符串。
1 + '1'
由于 +
只能加相同类型的东西,所以它会尝试去改变这个类型,优先会尝试转化为字符串,等价于 (1).toString + '1'
。
任意类型转布尔
- Boolean(x)
Boolean(123); // true
Boolean("abc"); // true
Boolean({}); // true
Boolean(''); // false
- !!x
用任何东西取反两次就会得到一个 Boolean
!!"abc" ; // true
!!'' ; // false
!!{} ; // true
- falsy 值
falsy 是在 Boolean
上下文中认定可转换为 false
的值;
0 、 NaN 、 null 、 undefined 、‘’(空字符串)、false
任意类型转数字
- Number()
使用 Number
函数,可以将任意类型的值转化成数值。如果无法转换为数字,那么 Number()
函数返回 NaN
。
Number(123) // 123
Number('123') // 123
Number(true) // 1
Number(null) // 0
Number('123.45#') // NaN
- parseInt()
parseInt
方法用于将字符串转为整数。
parseInt('123') // 123
如果字符串头部有空格,空格会被自动去除。
parseInt(' 81') // 81
如果 parseInt
的参数不是字符串,则会先转为字符串再转换。
parseInt(1.23) // 1
// 等同于
parseInt('1.23') // 1
字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分。
parseInt('8a') // 8
- parseFloat()
parseFloat
方法用于将一个字符串转为浮点数。
parseFloat('3.14') // 3.14
如果字符串符合科学计数法,则会进行相应的转换。
parseFloat('314e-2') // 3.14
如果字符串包含不能转为浮点数的字符,则不再进行往后转换,返回已经转好的部分。
parseFloat('3.14more non-digit characters') // 3.14
parseFloat
方法会自动过滤字符串前导的空格。
parseFloat('\t\v\r12.34\n ') // 12.34
如果参数不是字符串,或者字符串的第一个字符不能转化为浮点数,则返回NaN
。
parseFloat([]) // NaN
- x - 0
123 - 0
- +x(取正)
+ 123 // 123
+ '-1' // -1
JS数据在内存中的存储方式
什么是内存呢,举个例子:你买一个8G 的内存条,操作系统开机即占用 512MB,Chrome 打开即占用 1G 内存, Chrome 各每个网页分配一定数量的内存, 这些内存要分给页面渲染器、网络模块、浏览器外壳和 JS 引擎(V8引擎)。 JS 引擎将内存分为代码区和数据区, 我们只研究数据区。 数据区分为 Stack(栈内存)和 Heap(堆内存)。 简单类型的数据(如Number,string等)直接存在 Stack 里, 复杂类型的数据(object对象)是把 Heap 地址存在 Stack 里。
对象的存储方式是在stack内存存储一个地址,形成对对象的引用,地址指向heap内存的某个位置,这样才能达到可以随时为对象添加或删除内容的目的。所以有句话这么说,object是对象的引用。它在stack内存存的是地址,而不像其他数据类型直接把内容存在stack内存。
关于内存的面试题
- 题目一
var a = 1
var b = a
b = 2
请问 a 显示是几?
在内存图可以看出 a
还是等于 1
。
- 题目二
var a = {name: 'a'}
var b = a
b = {name: 'b'}
请问现在 a.name 是多少?
等于号制作一件事情,就是把右边的东西存到左边,由内存图可以看出 a.name
是 'a'
。
- 题目三
var a = {name: 'a'}
var b = a
b.name = 'b'
请问现在 a.name 是多少?
在内存图可以看出 a.name
是 'b'
- 题目四
var a = {name: 'a'}
var b = a
b = null
请问现在 a 是什么?
基本类型在栈内存中,对象是复杂类型在堆内存中,所以 a
是等于本身。
- 题目五
var a = {n:1};
var b = a;
a.x = a = {n:2};
alert(a.x);
alert(b.x);
浏览器看到 a.x = a = {n:2};
的时候会先确定 a
的值,a
是内存地址34,然后申明对象 {n:2}
的内存地址为54,右边的 a
就等于内存54了,而前面的 a
已经确定了是内存34的对象,不会重复改变 a
的值。浏览器是先看左边,再往右看的,只不过是看了右边之后,算完了再回到左边。
GC 垃圾回收
如果一个对象没有被引用,它就是垃圾,将会被回收。
var fn = function(){}
document.body.onclick = fn
fn = null
fn
是垃圾将被回收吗?
fn
还被 document
引用,所以不是垃圾不会被回收。
如果现在把页面关了,fn 是垃圾吗?
fn
是垃圾将被回收,因为页面关闭后document
就不存在了,后面的对象失去了引用。里面三个对象相连接,但是外面没有连接,这三个都将被回收。
但是 IE6 有个 BUG,会认为这三个不是垃圾,会把这三个内存一直留着。只要不关闭整个IE,只关掉一个 tab 是没有用的,这样垃圾会越来越多,内存不能重新被利用。IE6 无法在页面关闭的时候,正常的把那些 onclick
对应的函数给标记为垃圾。
// 解决方法,把所有的事件监听置为 null
window.onunload = function(){
document.bady.onclick = null
}
这就是所谓的内存泄漏 ,内存泄漏就是由于浏览器的一些bug,使得该被标记为垃圾的没有标记为垃圾,然后内存就会被永久的占用。
浅拷贝 VS 深拷贝
- 这是一个深拷贝的例子
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 内存进行完全的拷贝。