JS 里的数据类型转换

1,310 阅读6分钟

之前的博客介绍了JS中的各种数据类型,那么在js当中,不同的数据类型怎么相互转换呢,以下是常见的不同数据类型转换的用法。

任意类型转字符串

  • x.toString()
(1).toString()  // '1'
true.toString()  // 'true'
null.toString()  // 报错
undefined.toString()  // 报错
({}).toString()  // "[object Object]"

toString 方法适用于 number 类型和 boolean 类型,但是对于 nullundefined,使用这种方法会报错,对于 objecttoSring 方法结果不正确,结果永远是 "[object Object]"

console.log 其实就是用的这个原理,理论上 console.log 只能接受字符串。

console.log 打印出来的其实是有引号的但是浏览器没打,console.log(1) 打印出来的相当于 console.log((1).toString)

所以 console.log 如果发现不是字符串,会自动调用 toString 这个API 转化为字符串,objectkey 也是会这样。

  • 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 内存进行完全的拷贝。