JS数据类型那点事儿
# 前言
js是一门弱类型语言,也就是在创建变量时并不强制限制变量的类型,而是根据赋值决定最终类型。这也就会导致某个变量可能一秒前还是字符串,下一秒就变成number了。虽然在书写过程中灵活度变高了,但是一旦发生bug,排查起来也是非常的头疼。下面就一起来深入了解一下js的数据类型吧
# JS的数据类型有哪些?
首先JS的数据类型一共分为两大类:基本类型
和引用类型
# 基本类型
存储在内存栈中,值不可变。分为以下几种:
- string(字符串)
- boolean(布尔值)
- number(数字)
- symbol(符号)
- null(空值)
- undefined(未定义)
注意:
1.string
、number
、boolean
和 null
undefined
这五种类型统称为原始类型(Primitive),表示不能再细分下去的基本类型;
2.symbol
是ES6
中新增的数据类型,symbol
表示独一无二的值,通过 Symbol
函数调用生成,由于生成的 symbol
值为原始类型,所以 Symbol
函数不能使用 new
调用;
3.null
和 undefined
通常被认为是特殊值,这两种类型的值唯一,就是其本身。
# 引用类型
存储在内存堆中,也可称作对象类型。array和function也是对象的子类型。对象在逻辑上是属性的无序集合,是存放各种值的容器。对象值存储的是引用地址,所以和基本类型值不可变的特性不同,对象值是可变的。
# 如何判断类型?
JS为我们提供了以下几种方式可供判断数据类型
# typeOf
通过 typeof
操作符来判断一个值属于哪种基本类型。
typeof 'seymoe' // 'string'
typeof true // 'boolean'
typeof 10 // 'number'
typeof Symbol() // 'symbol'
typeof null // 'object' 无法判定是否为 null
typeof undefined // 'undefined'
typeof {} // 'object'
typeof [] // 'object'
typeof(() => {}) // 'function'
2
3
4
5
6
7
8
9
10
typeOf
可以有效的判断基本数据类型,但是对引用类型的判断稍有欠缺,除了function
之外其余的都会判定为Object
。需要注意,typeOf
判断null
类型也为Object
。
# instanceof
instanceof可以用来判断引用类型,他的原理是通过构造函数的prototype 是否出现在对象的原型链上来判断的。
[] instanceof Array // true
({}) instanceof Object // true
(()=>{}) instanceof Function // true
2
3
要注意,instanceof
也不是万能的,例如一个引用类型的子类型:array,function
,他们的原形对象Array,Function
的原型链最终指向的还是Object
,所以在判断他们的类型的时候他们也同时满足Object
类型
let arr = []
let obj = {}
arr instanceof Array // true
arr instanceof Object // true
obj instanceof Object // true
2
3
4
5
并且如果目标变量的原型链被修改过的话也会导致instanceof
判断错误
# Object.prototype.toString()
判断js数据类型的终极办法 Object.prototype.toString()
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call(() => {}) // '[object Function]'
Object.prototype.toString.call('seymoe') // '[object String]'
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(Symbol()) // '[object Symbol]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(Math) // '[object Math]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(new WeakSet()) // '[object WeakSet]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Object.prototype.toString.call(new WeakMap()) // '[object WeakMap]'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 该方法本质就是依托
Object.prototype.toString()
方法得到对象内部属性[[Class]]
- 传入原始类型却能够判定出结果是因为对值进行了包装
null
和undefined
能够输出结果是内部实现有做处理
# 类型强制转换
类型强制转换一般发生在不同类型的运算上,下面就一起来看看不同类型在不同情况下都会发生哪些强制类型转换吧
一般强制转换分为以下几种
- 转为
string
- 转为
number
- 转为
boolen
# 类型转换前置知识——ToPrimitive(引用类型转换为原始值)
在学习基本类型的强制转换之前我们先来学习一下引用类型的强制转换
ToPrimitive
对原始类型不发生转换处理,只针对引用类型(object)
的,其目的是将引用类型(object)
转换为非对象类型,也就是原始类型。
ToPrimitive
运算符接受一个值,和一个可选的期望类型作参数。ToPrimitive
运算符将值转换为非对象类型,如果对象有能力被转换为不止一种原语类型,可以使用可选的 期望类型 来暗示那个类型。
转换后的结果原始类型是由期望类型决定的,期望类型其实就是我们传递的type。直接看下面比较清楚。
ToPrimitive
方法大概长这么个样子具体如下。
/**
* @obj 需要转换的对象
* @type 期望转换为的原始数据类型,可选
*/
ToPrimitive(obj,type)
2
3
4
5
type不同值的说明
- type为
string
- 先调用
obj
的toString
方法,如果为原始值,则return,否则进行第2步 - 调用
obj
的valueOf
方法,如果为原始值,则return,否则进行第3步 - 抛出
TypeError
异常
- type为
number
- 先调用
obj
的valueOf
方法,如果为原始值,则return,否则进行第2步 - 调用
obj
的toString
方法,如果为原始值,则return,否则第3步 - 抛出
TypeError
异常
- type参数为
空
- 该对象为
Date
,则type
被设置为String
- 否则,
type
被设置为Number
Date数据类型特殊说明:
对于Date
数据类型,我们更多期望获得的是其转为时间后的字符串,而非毫秒值(时间戳),如果为number,则会取到对应的毫秒值,显然字符串使用更多。 其他类型对象按照取值的类型操作即可。
ToPrimitive
总结
ToPrimitive
转成何种原始类型,取决于type
,type
参数可选,若指定,则按照指定类型转换,若不指定,默认根据实用情况分两种情况,Date
为string
,其余对象为number
。那么什么时候会指定type
类型呢,那就要看下面两种转换方式了。
toString
Object.prototype.toString()
toString()
方法返回一个表示该对象的字符串。
每个对象都有一个 toString()
方法,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。
这里先记住,valueOf()
和 toString()
在特定的场合下会自行调用。
valueOf
Object.prototype.valueOf()
方法返回指定对象的原始值。
JavaScript 调用 valueOf()
方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是我们很少需要自己调用此函数,valueOf
方法一般都会被 JavaScript 自动调用。
不同内置对象的valueOf
实现:
- String => 返回字符串值
- Number => 返回数字值
- Date => 返回一个数字,即时间值,字符串中内容是依赖于具体实现的
- Boolean => 返回Boolean的this值
- Object => 返回this
var str = new String('123');
console.log(str.valueOf());//123
var num = new Number(123);
console.log(num.valueOf());//123
var date = new Date();
console.log(date.valueOf()); //1526990889729
var bool = new Boolean('123');
console.log(bool.valueOf());//true
var obj = new Object({valueOf:()=>{
return 1
}})
console.log(obj.valueOf());//1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 何时自动转为string?
字符串自动转换主要发生在加法运算,当相加的变量里有一个字符串时,另外一个就会发生强制转换,转为string。 基本类型与字符串相加示例
'2' + 1 // '21'
'2' + true // "2true"
'2' + false // "2false"
'2' + undefined // "2undefined"
'2' + null // "2null"
2
3
4
5
字符串和对象相加 这个时候对象需要转为字符串,遵循ToPrimitive规则,传入string后,会先调用对象的toString方法,返回的不是原始值的话再调用valueOf方法。
//toString的对象
var obj2 = {
toString:function(){
return 'a'
}
}
console.log('2'+obj2)
//输出结果2a,改写了obj2对象的toString,返回字符串a,为原始类型,所以直接return,最终得到2a
//常规对象
var obj1 = {
a:1,
b:2
}
console.log('2'+obj1);
//输出结果 2[object Object],调用toString,返回一个[object Object]的字符串,最终得到2[object Object]
//几种特殊对象
'2' + {} // "2[object Object]"
'2' + [] // "2"
'2' + function (){} // "2function (){}"
'2' + ['koala',1] // 2koala,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 何时自动转为number?
- 有加法运算符,但是无String类型的时候,都会优先转换为Number类型
true + 0 // 1
true + true // 2
true + false //1
2
3
- 除了加法运算符,其他运算符都会把运算自动转成数值
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
//一元运算符(注意点)
+'abc' // NaN
-'abc' // NaN
+true // 1
-false // 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意
null转为数值时为0,而undefined转为数值时为NaN。
# 何时自动转为boolen?
- 布尔比较时
if(obj)
,while(obj)
等判断时或者 三元运算符只能够包含布尔值
# 总结
- JS数据类型一共分为两大类,分别是基本数据类型
(string,boolean,number,symbol,null,undefined)
和引用数据类型(Object,以及Object子类型function,array)
- 判断数据类型的方法大致有三种
typeof
,instanceof
,Object.prototype.toString()
typeof
可以判断除null
以外的其他基本数据类型,无法判断引用类型(null
也会返回Object
)instanceof
可以判断引用类型(基于变量的原型对象去判断
),但是不够准确(所有引用类型都有Object这个原型对象
),修改原型链会导致误判Object.prototype.toString()
是最准确的判断类型的方法,他依托于Object.prototype.toString() 方法得到对象内部属性 [[Class]]
- 变量在做运算时会发生强制类型转换,常见的转换有
转为string
,转为number
,转为boolen
- 对象转为
string
会先调用tostring
函数,如果返回的不是原始值再调用valueof
函数,如果返回的还不是原始值则抛出错误
- 对象转为
number
的逻辑和转为string
相同,只是调用函数的顺序变为先调用valueof
再调用tostring
- 转为
string
的情况一般发生在加法运算符并且有一个字符串参与运算
- 转为
number
的情况一般发生在有加法但是无string参与
,以及除加法运算符以外的其他运算符
- 转为
boolen
的情况一般发生在逻辑判断
,三元运算
等
以上就是本文全部内容,相信看完之后一般的类型问题已经难不倒你了!