Javascript细节总结2
1.JS 分为哪两大类型
i. 原始(Primitive)类型
在 JS 中,存在着 6 种原始值,分别是:
boolean
null
undefined
number
string
symbol
首先原始类型存储的都是值,是没有函数可以调用的,比如 undefined.toString()
此时你肯定会有疑问,这不对呀,明明 '1'.toString()
是可以使用的。其实在这种情况下,’1’ 已经不是原始类型了,而是被强制转换成了 String
类型也就是对象类型,所以可以调用 toString
函数。
除了会在必要的情况下强转类型以外,原始类型还有一些坑。
其中 JS 的 number
类型是浮点类型的,在使用中会遇到某些 Bug,比如 0.1 + 0.2 !== 0.3
。string
类型是不可变的,无论你在 string 类型上调用何种方法,都不会对值有改变。
另外对于 null
来说,很多人会认为他是个对象类型,其实这是错误的。虽然 typeof null
会输出 object
,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000
开头代表是对象,然而 null
表示为全零,所以将它错误的判断为 object
。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。
ii. 对象(Object)类型
在 JS 中,除了原始类型那么其他的都是对象类型了。对象类型和原始类型不同的是,原始类型存储的是值,对象类型存储的是地址。当你创建了一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址。
2.typeof vs instanceof
typeof
对于原始类型来说,除了 null
都可以显示正确的类型
1 | typeof 1 // 'number' |
typeof
对于对象来说,除了函数都会显示 object
,所以说 typeof
并不能准确判断变量到底是什么类型
1 | typeof [] // 'object' |
如果我们想判断一个对象的正确类型,这时候可以考虑使用 instanceof,因为内部机制是通过原型链来判断的
1 | const Person = function() {} |
对于原始类型来说,你想直接通过 instanceof
来判断类型是不行的,当然我们还是有办法让 instanceof
判断原始类型的
1 | class PrimitiveString { |
你可能不知道 Symbol.hasInstance
是什么东西,其实就是一个能让我们自定义 instanceof
行为的东西,以上代码等同于 typeof 'hello world' === 'string'
,所以结果自然是 true
了。这其实也侧面反映了一个问题, instanceof
也不是百分之百可信的。
3.类型转换
首先我们要知道,在 JS 中类型转换只有三种情况,分别是:
- 转换为布尔值
- 转换为数字
- 转换为字符串
i. 转Boolean
在条件判断时,除了 undefined
, null
, false
, NaN
, '
‘, 0
, -0
,其他所有值都转为 true
,包括所有对象。
ii. 对象转原始类型
对象在转换类型的时候,会调用内置的 [[ToPrimitive]]
函数,对于该函数来说,算法逻辑一般来说如下:
- 如果已经是原始类型了,那就不需要转换了
- 调用
x.valueOf()
,如果转换为基础类型,就返回转换的值 - 调用
x.toString()
,如果转换为基础类型,就返回转换的值 - 如果都没有返回原始类型,就会报错
当然你也可以重写 Symbol.toPrimitive
,该方法在转原始类型时调用优先级最高。
1 | let a = { |
iii. 四则运算符
加法运算符不同于其他几个运算符,它有以下几个特点:
运算中其中一方为字符串,那么就会把另一方也转换为字符串
如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
1
2
31 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"如果你对于答案有疑问的话,请看解析:
对于第一行代码来说,触发特点一,所以将数字
1
转换为字符串,得到结果'11'
对于第二行代码来说,触发特点二,所以将
true
转为数字1
对于第三行代码来说,触发特点二,所以将数组通过
toString
转为字符串'1,2,3'
,得到结果'41,2,3'
另外对于加法还需要注意这个表达式 'a' + + 'b'
1 | 'a' + + 'b' // -> "aNaN" |
因为 + 'b'
等于 NaN
,所以结果为 "aNaN"
,你可能也会在一些代码中看到过 + '1'
的形式来快速获取 number 类型。
那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字
1 | 4 * '3' // 12 |
iiii. 比较运算符
- 如果是对象,就通过
toPrimitive
转换对象 - 如果是字符串,就通过
unicode
字符索引来比较在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值。1
2
3
4
5
6
7
8
9let a = {
valueOf() {
return 0
},
toString() {
return '1'
}
}
a > -1 // true