JavaScript中的数据类型

本文将主要总结一下JavaScript中获取数据类型的方式,以及数据类型之间的转换规则。

JS中的数据类型

原始值类型:StringNumberBigIntBooleanNullUndefinedSymbol
引用类型:Object

typeof

类型 结果
String ‘string’
Number ‘number’
BigInt ‘bigint’
Boolean ‘boolean’
Null ‘object’
Undefined ‘undefined’
Symbol ‘symbol’
Object ‘object’
Function对象 ‘function’

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 数值
console.log(typeof 123) // number
console.log(typeof 3.14) // number
console.log(typeof NaN) // number
console.log(typeof Number(1)) // number

console.log(typeof 42n) // bigint

// 字符串
console.log(typeof '123') // string
console.log(typeof '') // string
console.log(typeof String(1)) // string

// 布尔值
console.log(typeof true) // boolean
console.log(typeof false) // boolean
console.log(typeof Boolean(1)) // boolean
console.log(typeof !!(1)) // boolean

// Symbols
console.log(typeof Symbol()) // symbol
console.log(typeof Symbol('foo')) // symbol
console.log(typeof Symbol.iterator) // symbol

// Undefined
console.log(typeof undefined) // undefined

// 对象
console.log(typeof {a: 1}) // object

// 无法区分数组
// 使用 Array.isArray 或者 Object.prototype.toString.call
console.log(typeof [1, 2, 3]) // object

console.log(typeof new Date()) // object
console.log(typeof /regex/) // object

// 函数
console.log(typeof function(){}) // function
console.log(typeof (() => {})) // function
console.log(typeof class C {}) // function
console.log(typeof Math.sin) // function

下面是一些使用typeof判断类型时的特(mi)殊(huo)情况:

null

1
console.log(typeof null)          // object

所以在判断是否是object时,一定要记得排除null

1
2
3
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}

使用 new 操作符

除了Function,其他使用new操作符创建的数据类型都是object

1
2
3
4
5
console.log(typeof new Boolean(true))   // object
console.log(typeof new String('abc')) // object
console.log(typeof new Number(123)) // object

console.log(typeof new Function()) // function

判断未声明的变量

如果判断的是稍后用var声明的变量,结果是undefine

如果判断的是稍后用letconst声明的变量,结果会报错:Uncaught ReferenceError: Cannot access xxx before initialization

Object.prototype.toString

类型 结果
String ‘[object String]’
new String() ‘[object String]’
Number ‘[object Number]’
new Number() ‘[object Number]’
BigInt ‘[object BigInt]’
Boolean ‘[object Boolean]’
new Boolean() ‘[object Boolean]’
字面量数组 ‘[object Array]’
new Array() ‘[object Array]’
Null ‘[object Null]’
Undefined ‘[object Undefined]’
Symbol ‘[object Symbol]’
Function对象 ‘[object Function]’
new Date() ‘[object Date]’
/regex/ ‘[object RegExp]’
自定义对象 ‘[object Object]’

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
var toString = Object.prototype.toString

console.log(toString.call(123)) // [object Number]
console.log(toString.call(3.14)) // [object Number]
console.log(toString.call(NaN)) // [object Number]
console.log(toString.call(Number(1))) // [object Number]

console.log(toString.call(42n)) // [object BigInt]

console.log(toString.call('123')) // [object String]
console.log(toString.call('')) // [object String]
console.log(toString.call(String(1))) // [object String]

console.log(toString.call(true)) // [object Boolean]
console.log(toString.call(false)) // [object Boolean]
console.log(toString.call(Boolean(1))) // [object Boolean]
console.log(toString.call(!!(1))) // [object Boolean]

console.log(toString.call(Symbol())) // [object Symbol]
console.log(toString.call(Symbol('foo'))) // [object Symbol]
console.log(toString.call(Symbol.iterator)) // [object Symbol]

console.log(toString.call(undefined)) // [object Undefined]

console.log(toString.call(null)) // [object Null]

console.log(toString.call({a: 1})) // [object Object]

console.log(toString.call([1, 2, 3])) // [object Array]
console.log(toString.call(new Array())) // [object Array]

console.log(toString.call(new Date())) // [object Date]
console.log(toString.call(/regex/)) // [object RegExp]
console.log(toString.call(new Boolean(true))) // [object Boolean]
console.log(toString.call(new String('abc'))) // [object String]
console.log(toString.call(new Number(123))) // [object Number]
console.log(toString.call(new Function())) // [object Function]
console.log(toString.call(function(){})) // [object Function]
console.log(toString.call(class C {})) // [object Function]
console.log(toString.call(Math.sin)) // [object Function]

var Cat = function(name) {
this.name = name
}

var myCat = new Cat('Kitty')
console.log(toString.call(myCat)) // [object Object]

使用Object.prototype.toString可以详细地区分出JS中内置的各种对象类型。

但是千万不要调用对象实例的toString方法去判断,因为对象可能重写了toString方法,导致返回的不是上述的结果。比如[].toString(),会返回空字符串。

instanceof

instanceof用来检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var simpleStr = "This is a simple string"; 
var myString = new String();
var newStr = new String("String created with constructor");
var myDate = new Date();
var myObj = {};
var myNonObj = Object.create(null); // 一种创建非 Object 实例的对象的方法

console.log(simpleStr instanceof String) // false
console.log(myString instanceof String) // true
console.log(newStr instanceof String) // true
console.log(myString instanceof Object) // true
console.log(simpleStr instanceof Object) // false

console.log(myObj instanceof Object) // true
console.log(myNonObj instanceof Object) // false

console.log(myString instanceof Date) // false

console.log(myDate instanceof Date) // true
console.log(myDate instanceof Object) // true
console.log(myDate instanceof String) // false

需要注意的是,如果表达式obj instanceof Foo返回true,并不意味着会一直返回true,因为Foo.prototype可能会改变,改变之后的值就不一定在obj的原型链上了。

constructor

undefinednull没有constructor属性,如果用它们调用了constructor会报错。

constructor来判断类型也不能确保一直正确,因为对象的constructor属性是可以修改的。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var num = 123
var num1 = Number(123)
var num2 = new Number(123)
console.log(num.constructor === Number)
console.log(num1.constructor === Number)
console.log(num2.constructor === Number)

var str = 'abc'
var str1 = String('abc')
var str2 = new String('abc')
console.log(str.constructor === String)
console.log(str2.constructor === String)
console.log(str2.constructor === String)

var arr = [1, 2, 3]
var arr1 = Array(1, 2, 3)
var arr2 = new Array(1, 2, 3)
console.log(arr.constructor === Array)
console.log(arr1.constructor === Array)
console.log(arr2.constructor === Array)

var bool = true
console.log(bool.constructor === Boolean)

var obj = {name: 'Kitty'}
console.log(obj.constructor === Object)

var Cat = function() {
}

var myCat = new Cat('Kitty')
console.log(myCat.constructor === Cat)

var fun = function() {}
console.log(fun.constructor === Function)

以上都会输出true

valueOf

类型 结果
String 返回字符串值
new String() 返回字符串值
Number 返回数字值
new Number() 返回数字值
Boolean 返回布尔值true或false
new Boolean() 返回布尔值true或false
Array 返回数组对象本身
Object 返回对象本身
Function 返回本身
Date 返回当前时间距1970年1月1日午夜的毫秒数

UndefineNullMatchError没有valueOf方法,对其调用valueOf会报错。

示例(来源[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Array:返回数组对象本身
var array = ["ABC", true, 12, -5];
console.log(array.valueOf() === array); // true

// Date:当前时间距1970年1月1日午夜的毫秒数
var date = new Date(2013, 7, 18, 23, 11, 59, 230);
console.log(date.valueOf()); // 1376838719230

// Number:返回数字值
var num = 15.26540;
console.log(num.valueOf()); // 15.2654

var newNum = new Number(15.26540)
console.log(newNum.valueOf())

// 布尔:返回布尔值true或false
var bool = true;
console.log(bool.valueOf() === bool); // true

// new一个Boolean对象
var newBool = new Boolean(true);
// valueOf()返回的是true,两者的值相等
console.log(newBool.valueOf() == newBool); // true
// 但是不全等,两者类型不相等,前者是boolean类型,后者是object类型
console.log(newBool.valueOf() === newBool); // false

// Function:返回函数本身
function foo(){}
console.log( foo.valueOf() === foo ); // true
var foo2 = new Function("x", "y", "return x + y;");
console.log( foo2.valueOf() );
/*
ƒ anonymous(x,y
) {
return x + y;
}
*/

// Object:返回对象本身
var obj = {name: "张三", age: 18};
console.log( obj.valueOf() === obj ); // true

// String:返回字符串值
var str = "http://www.xyz.com";
console.log( str.valueOf() === str ); // true

// new一个字符串对象
var str2 = new String("http://www.xyz.com");
// 两者的值相等,但不全等,因为类型不同,前者为string类型,后者为object类型
console.log( str2.valueOf() === str2 ); // false

类型转换

关于类型转换,可以看阮一峰老师的这篇文章:数据类型转换,讲的很细致。我这里加上个人的理解做个整理。

在日常开发中涉及到的类型转换主要就发生在数值、字符串、布尔值之间的转换,可以使用Number()String()Boolean()显示转换,也可以依赖JS引擎隐式转换。所以主要就是搞懂隐式转换和隐式转换依据的规则是什么。

Number()

原始类型 的转换如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 数值:转换后还是原来的值
Number(123) // 123

// 字符串:如果可以被解析为数值,则转换为相应的数值
Number('123') // 123

// 字符串:如果不能解析为数值,则转换为NaN
Number('123abc') // NaN

// 空字符串:转换为0
Number('') // 0

// 布尔值:true会转为1,false会转为0
Number(true)
Number(false)

// undefined:转换为NaN
Number(undefined) // NaN

// null:转换为0
Number(null) // 0

对象 的转换规则如下:

  1. 调用对象自身的valueOf方法,如果返回原始类型的值,直接对该值使用Number函数,不再进行后续步骤。
  2. 如果valueOf方法返回的是对象,则调用该对象的toString方法,如果返回原始类型的值,直接对该值使用Number函数,不再进行后续步骤。
  3. 如果toString方法返回的是对象,就报错。
1
2
3
4
5
6
7
8
9
10
var obj = {x: 1}
Number(obj) // NaN

// 等同于
if typeof obj.valueOf() === 'object' {
// obj.toString() 返回 "[object Object]",所以转换后是NaN。
Number(obj.toString())
} else {
Number(obj.value())
}

String()

原始类型 的转换如下:

1
2
3
4
5
6
7
8
9
10
// 数值:转换为对象的字符串
String(123)
// 字符串:转换后还是原来的字符串
String('abc')
// 布尔值:true转换为"true"字符串,false转换为"false"字符串
String(true)
// undefined:转换为"undefined"字符串
String(undefined)
// null:转换为"null"字符串
String(null)

对象 的转换规则如下:

  1. 调用对象本身的toString方法,如果返回原始类型的值,直接对该值使用String函数,不再继续后面的步骤。
  2. 如果上一步toString方法返回的是对象,则调用该对象的valueOf方法,如果返回原始类型的值,直接对该值使用String函数,不再继续后面的步骤。
  3. 如果valueOf方法返回对象,就报错。

Number()和String()对对象类型的值的转换规则相似,只是换了一下valueOftoString的执行顺序。

Boolean()

Boolean()可以将任意类型的值转为布尔值,下面的5个值会转换为false,其他值都会转换为true

1
2
3
4
5
Boolean(undefined)   // false
Boolean(null) // false
Boolean(0) // false
Boolean('') // false
Boolean(NaN) // false

Boolean()对任意 对象 转换后得到的都是true,下面这些得到的都是true

1
2
3
Boolean([])                     // true
Boolean({}) // true
Boolean(new Boolean(false)) // true

隐式转换

隐式转换一般发生在下面三种情况时,JavaScript会自动调用String()、Number()或Boolean()对需要转换的值进行转换:

第一种情况,不同类型的数据互相运算:

1
2
3
4
5
6
7
// 加法运算时,有一方是字符串,则会把另一个值也转为字符串,最后得到一个字符串值
123 + 'abc' // "123abc"

// 其他运算时,会把参与运算的两个值都转为数值,再计算结果,如果有一方不能转为数值,最后会得到NaN
'5' - '2' // 3
'5' * '2' // 10
'5' * [] // 0

第二种情况,对非布尔类型的数据求布尔值,除了上面说的5个值会转为false,其他情况都会是true

1
2
3
if ('abc') {
console.log('hello')
} // "hello"

第三种情况,一元运算符时,会将运算子转为数值:

1
2
3
+'abc'       // NaN
-'abc' // NaN
+true // 1

本文作者:意林
本文链接:http://shinancao.cn/2019/08/19/JS-Obj-Type/
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!