JavaScript – 数据类型
前言
写着 TypeScript 教程,顺便也写点 JavaScript 的。
参考
JavaScript 数据类型
JavaScript 一共有 8 种类型:
-
string
-
number
-
boolan
-
undefined
-
null
-
object
-
symbol (es6)
-
bigint (es2020)
symbol
和 bigint
是后来(es6 和 es2020)加进去的。
除了 object
以外,其余 7 种被称为原始类型(primitive type),它们都属于值类型(value type)。
object
被称为非原始类型(non-primitive type),它是引用类型(reference type)。
typeof
typeof
可以 detect 出 value
的类型(虽然不是很精准)
console.log('string', typeof ''); // typeof 字符串返回的是 'string'(也就是 string 类型的意思)
console.log('number', typeof 0); // 整数或浮点数是 'number'
console.log('boolean', typeof true); // true,false 是 'boolean'
console.log('undefined', typeof undefined); // undefined 是 'undefined'
console.log('null', typeof null); // null 是 'object'
console.log('object', typeof {}); // 对象是 'object'
console.log('symbol', typeof Symbol()); // Symbol 创建的是 'symbol'
console.log('bigint', typeof (100n)); // 整数+n 是 'bigint'
效果
都挺直观的,只有一个比较奇葩 —— null
是 'object'
,这是个历史错误,by right 它应该要是 'null'
才对。
其它常见 value
的 typeof
console.log('array', typeof []); // array 是 'object'
console.log('function', typeof function() {}); // 函数是 'function'
console.log('arrow function', typeof (() => {})); // 箭头函数是 'function'
console.log('date', typeof new Date()); // Date 实例是 'object'
console.log('reg', typeof /\d/gi); // 正则表达式是 'object'
console.log('NaN', typeof NaN); // not a number 是 'number'
console.log('class', typeof class{}); // class 是 'function'
效果
typeof
函数和箭头函数返回的是 'function'
。
不过,'function'
其实不是一种类型哦,它算是 'object'
的一种。
另外,class
的本质是函数,所以 typeof class
返回 'function'
。
new RegExp
和 new Date
返回的是实例,实例就是对象,所以是 'object'
。
NaN
是 "not a number",虽然名字是 "不是一个 number",但其实它依然是 'number'
类型。
array 不是一种类型,它算是一种 'object'
。
总结
typeof
可以返回 value 的类型信息,但它不直接等于 JavaScript 的 8 种类型。
我们需要自己进一步将其归类,比如:
typeof null
返回 'object'
,但 null
其实不是 object
类型,它是 null
类型才对。
typeof class
、typeof function
返回的是 'function'
,但 JavaScript
没有 function
类型,它应该要被归纳为 object
类型。
其它判断方式
除了 typeof
, 还有一些常用的类型技巧:
判断 null
null === null
判断 NaN
Number.isNaN(NaN)
判断 Array
Array.isArray([])
判断 Date
或 Regex
const date = new Date();
date instanceof Date; // true
黑魔法 typeof
console.log('string', Object.prototype.toString.call('string').slice(8, -1).toLowerCase());
效果
null
不是 'object'
而是 'null'
。
Array
不是 'object'
而是 'array'
。
Date
不是 'object' 而是 'date'
。
Regex
不是 'object' 而是 'regexp'
。
结论:透过 Object.prototype.toString
来做 typeof
可以判断的更仔细。
类型转换
参考:阮一峰 – 数据类型转换
先提醒:JavaScript 的类型转换规则挺乱的,最好不要常用,如果非要用,那要记好规则。
所有类型都可以强转成 boolean
、string
、number
。
转 boolean
Boolean(value)
或者 !!value
,两种写法都可以把 value
强制转换成 boolean
。
它的规则是:除了以下 5 个 value
会转成 false
,其它 value
都会转成 true
。
-
undefined
-
null
-
+0 或 -0
-
NaN
-
''(empty string)
像 emtpty object {}
、empty array []
这些都会转成 true
。
转 number
Number(value)
或者 +value
两种写法都可以把 value
强制转换成 number
。
-
'123'
→123
-
''
→0
-
true
→1
-
false
→0
-
'123abc'
→NaN
-
undefined
→NaN
-
null
→0
知识点:
-
boolean
true
→1
,false
→0
(这个还算好记) -
empty string
''
→0
(这个也还算记得住) -
numeric string
'123'
→123
(这个还算好记)numeric string + alphabetic string
注:有一点不要混淆,使用'123abc'
→NaN
(这个还算好记)Number.parseInt
或Number.parseFloat
的话,它会自动过滤掉 alphabetic string'abc'
的部分,所以不会返回NaN
,这一点和强转 number 是不同的。 -
undefined
→NaN
,而null
→0
(这个不好记,为什么它俩会不一致呢🤔?)
object 转 number
它比较复杂,有三个步骤:
valueOf
→ toString
→ Number(value)
先用 object.valueOf
获取 value
如果这个 value
是一个对象(注:array, function 也算对象哦),那就再用 object.toString
获取 value
。
如果 value
还是一个对象,那就报错。
只要 valueOf
或 toString
返回的 value
不是对象,那么就 Number(value)
。
console.log(+{ valueOf: () => '5', toString : () => '10' }); // 5
console.log(+{ valueOf: {}, toString: () => '10' }); // 10
转 string
String(value)
可以把 value
强制转换成 string
。
转换规则是:
-
123
→'123'
-
true
→'true'
-
undefined
→'undefned'
-
null
→'null'
都挺好记的。
object 转 string
toString
→ valueOf
→ String(value)
和 object to number 一样,只是顺序不同,它是先调用 toString
,如果返回的是对象,那就再调用 valueOf
。
只要 toString
或 valueOf
返回的 value
不是对象,那么就 String(value)
。
自动类型转换
if (value)
会把 value
强转成 boolean
!!value
会把 value
强转成 boolean
+value
会把 value
强转成 number
'string' + value
加号会把 value
强转成 string
。比如:'123' + 3
→ '1233'
value -*/ value
减乘除号会把 value
强转成 number
。比如:'123' - '3'
→ 120
value == value
双等号在对比时,会先自动转换类型(规则下面会详细讲)。
双等号 ==
双等号在对比时,会先自动转换类型,它的规则蛮多的:
-
null
和undefned
是相等的 -
null
和undefined
对比前不会被转换类型 -
NaN
和谁都不相等,包括NaN
,也就是说NaN == NaN
是false
,NaN != NaN
是true
。 -
string == string
、number == number
、boolean == boolean
、object == object
类型相同就不会自动转换。 -
类型不同的情况下,比如
string == number
、boolean == string
、object == number
,会先把它们转换成number
,像这样Number(string) == Number(boolean)
。注:不要问我为什么会转成
number
,我也不清楚,总是这就是它的规则。
我日常会用的类型转换
由于类型转换的规则很复杂,所以尽量还是少用为妙,以下是我常用的几个:
-
+(new Date())
把date
转成number
,等价于(new Date()).getTime()
,会得到 epoch time(从 1970 年 1 月 1 日到现在的 milliseconds)。 -
用
0
和1
来表示boolean
——0
是false
,1
是true
。 if (object | array)
判断有value
,如果是null
orundefined
会被转成false
。我只会用来判断
object | array
,string
或number
我不会这样写,因为规则不好记。-
'abc' + 123
把number
拼接到string
里。
就这么多,其它的规则不太好记,所以我会尽量避开它们的类型转换。
Auto-boxing 机制
7 个 primitive types 中,有 5 个是 object-like:
console.log(typeof ''); // 'string'
console.log(typeof 0); // 'number'
console.log(typeof true); // 'boolean'
console.log(typeof Symbol()); // 'symbol'
console.log(typeof 0n); // 'bigint'
除了 null
和 undefined
。
什么是 "object-like"?
console.log('hello world'.substring(0, 5)); // 'hello'
'hello world'
是 string
不是对象,为什么它可以调用 .substring
方法?
这方法哪来的?
new String('hello world').substring(0, 5);
其实这个方法来自 class String
。
当一个 string
(e.g. 'hello world'
).substring
时,JavaScript 引擎会进行 "临时装箱",把它变成 new String('hello world').substring
,这就叫 auto-boxing 机制。
number
→ Number
和 boolean
→ Boolean
也是如此。
symbol
和 bigint
则有一点特别。
Symbol
和 BigInt
不是 class
,它们不可以 new Symbol
、new BigInt
。
因此,当我们访问 Symbol().description
时,它并不是 auto-boxing 成 new Symbol().description
。
不过我们也不用分那么细,总之 JavaScript 引擎会特殊处理就是了(类似 auto-boxing 机制)。
结论:string
、number
、boolean
、symbol
、bigint
都不是对象,但是却可以访问属性和方法,这是因为 JavaScript 引擎做了特别处理,这个机制叫 auto-boxing。
什么时候会 auto-boxing?
除了 .property
和 .method
会 auto-boxing 以外,for...in
和 Object.getPrototypeOf
等也会
const number = 0;
console.log(Object.getPrototypeOf(number) === Number.prototype); // true
console.log(Object.keys('abc')); // ['0', '1', '2']
console.log(Object.getOwnPropertyNames('abc')); // ['0', '1', '2', 'length']
什么时候不会 auto-boxing?
console.log(typeof ''); // 'string'
console.log(typeof new String('')); // 'object'
console.log('' instanceof String); // false
console.log(new String('') instanceof String); // true
像 typeof
和 instanceof
就不会。
从这些例子中可以看到,并不是在每一种情况下都会 auto-boxing,因此我们在使用时一定要搞清楚状况。