关于JS数据类型和强制类型转换的一些点
从一些例题来引入话题:
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
var bar = true;
console.log(bar + 0); // 1
console.log(bar + 'xyz'); // truexyz
console.log(bar + true); // 2
console.log(bar + false); // 1
console.log('1' > bar); // false
console.log(1 + '2' + false); // 12false
console.log('2' + ['koala', 1]); // 2koala,1
var obj1 = {
a: 1,
b: 2
};
console.log('2' + obj1); // 2[object, Object]
var obj2 = {
toString: () => {
return 'a';
}
};
console.log('2' + obj2); // 2a
var b = 1;
const outer = () => {
var b = 2;
console.log(b);
var b = 3;
};
数据类型
- 基本类型
- string
- boolean
- number
- null
- undefined
- symbol(ES6)
- 对象类型
- 对象类型也叫引用类型,
array, function
都是对象的子类型,对象在逻辑上是属性的无序集合,是存放值的容器,对象本身存储的是引用地址。
- 对象类型也叫引用类型,
前五种类型称为原始类型(Primitive),表示不能再细分下去的基本类型,symbol
是ES6新增的类型,表示唯一值,直接由let s = Symbol()
生成,和string, number, boolean
一样,不要用new
生成,不然得到的是对象。
另外null, undefined
是两种特殊类型,值唯一,
1
2
3
4
null == undefined; // true
null === undefined; // false
typeof null; // object
typeof undefined; // undefined
弱类型语言和强制类型转换
JS是弱类型语言,声明变量时可以不指定变量类型,变量类型就是值的类型,且当值改变类型时,变量类型也会改变;let val = 'str'; val = 1;
由string
类型转换成number
强制类型转换规则
ToPrimitive
针对引用类型(object),其目的是将引用类型转换成原始类型。
1
2
3
4
5
/**
* @obj 需要转换的对象
* @type 期望转换为的原始数据类型,可选
*/
ToPrimitive(obj,type)
针对不同type
,处理方式有所不同。
string
:- 调用
obj.toString()
,如果结果是原始类型则return
,否则第二步 - 调用
obj.valueOf()
,如果是原始类型则return
,否则第三步 - 抛出
TypeError
异常
- 调用
number
- 调用
obj.valueOf()
,return
or next step - 调用
obj.toString()
,return
or next step - 抛出
TypeError
异常
- 调用
type
为空- 该对象为
Date
,则type
被设置为String
- 否则,
type
被设置为Number
- 该对象为
Object.prototype.toString()
对于这个方法,MDN给出的描述:每个对象都有一个toString()
方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString()
方法被每个Object对象继承。如果此方法在自定义对象中未被覆盖(Array, Function的toString方法都被重写了
),toString()
返回 "[object type]"
,其中type是对象的类型。
提炼出两点:对象的toString()
方法,在对象被当做文本值或字符串方式引用时,被自动调用;如果对象原型上的toString()
方法没有被重写或者覆盖,就会返回"[object type]"
。
Object.prototype.valueOf()
JavaScript调用valueOf
方法将对象转换为原始值。你很少需要自己调用valueOf
方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。
不同类型对象的valueOf
方法的返回值:
- Array: 返回对象本身
- Boolean: 布尔值
- Date: 时间戳,即毫秒数
- Function: 函数本身
- Number: 数字值
- Object: 默认是对象本身
- String: 字符串值
- Math和Error对象没有valueOf方法
看下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var ary = new Array(1, 2, 3);
console.log(ary.valueOf()); // [1, 2, 3]
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
Number
Number
的转换规则:调用Number()
null => 0
undefined => NaN
true => 1; false => 0
- 字符串转换时遵循数字常量规则,失败返回
NaN
String
String
转换规则:
null => 'null'
undefined => 'undefined'
true => 'true'; false => 'false'
- 数字转换就遵循通用规则,极大极小的数字使用指数形式
1
2
3
4
5
6
7
8
9
10
11
12
13
String(null) // 'null'
String(undefined) // 'undefined'
String(true) // 'true'
String(1) // '1'
String(-1) // '-1'
String(0) // '0'
String(-0) // '0'
String(Math.pow(1000,10)) // '1e+30'
String(Infinity) // 'Infinity'
String(-Infinity) // '-Infinity'
String({}) // '[object Object]'
String([1,[2,3]]) // '1,2,3'
String(['koala',1]) //koala,1
转换规则的应用场景
- 转换成
string
类型- 非对象类型下,字符串的加法运算时,如果其中一个值是字符串,则另一个值会被转换成字符串;
- 对象类型进行加法运算时,根据前面的
toPrimitive
方法且type = number
的规则进行转换();这里可能会有疑问,为什么是type = number
不是string
呢,原因是加法运算符会优先把对象转换成数字类型,注意是对象,非对象类型的基本类型不会先做这种转换,会保持他们原有的类型。 - 关于如何验证会优先将对象转换成数字类型,下面提供了一个方法,可以看到会先去调用
valueOf
方法,这和先转换成number
类型的步骤一致。1 2 3 4 5 6 7 8 9 10
let obj = { toString: () => { return 'toPrimitive(obj, string)'; }, valueOf: () => { return 'toPrimitive(obj, number)'; } } console.log('result: ' + obj); // result: toPrimitive(obj, number)
一些加法运算符对象转字符的例子,都是先调用valueOf,如果调用返回的不是原始类型,会接着调用toString
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
'2' + 1 // '21' '2' + true // "2true" '2' + false // "2false" '2' + undefined // "2undefined" '2' + null // "2null" //toString的对象 var obj2 = { toString:function(){ return 'a' } } console.log('2'+obj2) //输出结果2a //常规对象 var obj1 = { a:1, b:2 } console.log('2'+obj1); //输出结果 2[object Object] //几种特殊对象 '2' + {} // "2[object Object]" '2' + [] // "2" '2' + function (){} // "2function (){}" '2' + ['koala',1] // 2koala,1
- 转换成number类型
- 有加法,但没用string类型的时候,会优先转成number;
- 除了加法运算符,其他运算符会转成数值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
true + 0 // 1 true + true // 2 true + false //1 '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
- 判断等号
==
运算符与其他符号不同,是Number优先的,意思是会将等号两边的值转换成number类型再去比较。
- 两边都是number,直接比较。
1 == 2 //false
- 如果有对象,调用
toPrimitive(obj, number)
进行转换,再比较。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj1 = {
valueOf:function(){
return '1'
}
}
1 == obj1 //true
//obj1转为原始值,调用obj1.valueOf()
//返回原始值'1'
//'1'toNumber得到 1 然后比较 1 == 1
[] == ![] //true
//[]作为对象ToPrimitive得到 ''
//![]作为boolean转换得到0
//'' == 0
//转换为 0==0 //true
- 存在布尔值,按
Number()
方法将boolean转换成1或0,再进行比较。
1
2
3
4
5
//boolean 先转成number,按照上面的规则得到1
//3 == 1 false
//0 == 0 true
3 == true // false
'0' == false //true
- 如果一个值是string,一个是number,前面都有这种情况,会先把string转成number,不举例子了。
- 布尔值转换
两种情况会发生转换成布尔值,一是布尔值比较,二是if, while, 三元运算符
等包含布尔值条件的,还有我们经常看到这种写法,!!expression
,把表达式转换成布尔值。
判断数据类型
typeof
typeof
能判断大部分类型,常用来判断基本类型和函数,对于数组和null,只能判断出object
1
2
3
4
5
6
7
8
9
10
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'
instanceof
instanceof
可以通过测试构造函数的prototype
是否出现在被检测对象的原型链上来判断。
1
2
3
4
5
let arr = []
let obj = {}
arr instanceof Array // true
arr instanceof Object // true
obj instanceof Object // true
很明显数组实例的原型链上是有Array
构造函数的,又因为Array
是Object
的子类型,arr.__proto__ === Array.prototype; Array.__proto__ === Object.prototype
,所以导致了数组实例既属于数组,又属于对象类型。
Object.prototype.toString()
终极方法Object.prototype.toString()
来判断数据类型,前面也提到过了,会返回'[object type]'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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]'
该方法简单明了,本质是由Object.prototype.toString()
方法得到对象内部属性[[Class]]
,传入原始类型却能够判定输出结果是因为对值进行了包装,null, undefined
是JS内部实现有做相应的处理。
NaN
NaN
是一种特殊的Number
类型,通常在一些特殊运算时会返回;
- 无穷大除以无穷大
- 负数开平方
- 算术运算符与无法转换成数字类型的操作数一直使用
- 字符串解析成数字时,无法解析,返回NaN
String(), toString()
toString()
可以将除null, undefined
以外的数据转换成字符串。-
toString()
的参数可选,表示进制,范围从2-36 String()
可以将null, undefined
转为字符串,但不能进行进制转换。