为什么要搞懂这个东西,一个是笔试题里经常会考这点,另一个是我们开发过程中经常也会用到它。那我们首先搞清楚,为什么会隐式转化、隐式转化成什么类型、如何转化,最后通过面试题巩固我们的理解!
1、变量有哪些类型
1.1、原始类型
Null
表示空值,可以用来设置初始值;
null == false // true
null == "" // true
null == 0 // true
null == undefined // true
Undefined
已声明,未定义
var a;console.log(a); // undefined
Boolean
布尔值,true/false
显式转化:Boolean()
隐式转化:if、while、switch case、||、&&等逻辑语句
Number
常见的有整数、浮点数、Infinity、-Infinity、NaN
显示转化:Number()
隐式转化:一元运算符(++,--)、二元运算符(+,-,*,/)、比较操作(>, <, <=, >=)、相等操作(== 或者 != )
这里我简单解释一下NaN:无法转换成数字,最后只能统一转换成NaN,它也是一种number类型
String
显示转化:String()
隐式转化:+ 拼接字符串
Symbol
一种新的原始数据类型,表示独一无二的值;Symbol 值通过`Symbol`函数生成
1.2、引用类型Object
Function、RegExp、Array、Date等都是属于Object类型
2、类型转换的基本规则
从这个图中,我们可以很简单的掌握Boolean,Number,String,Null,Undefined的类型转换,
那么对象类型的转换为什么是这样? 又是怎么转换的呢?
2.1、原始值包装类型
为了方便操作原始值,ECMAscript提供了3种特殊的引用类型:Boolean、Number、String。 这些类型具备其它引用类型一样的特点,但也具备与各自原始值类型对应的特殊行为。每当用到某个原始值的方法或属性时,后台会自动创建一个相应的原始值包装类型的对象,从而暴露出操作原始值的各种方法。例如:
let s1 = "some text";
let s2 = s1.subString(2);
我们知道原始值本身不是对象,因此逻辑上不应该有方法。但为什么又能调用subString()呢,这是因为后台做了很多处理,主要是执行了以下3步:
- 创建一个String类型的实例
- 调用实例上的特定方法
- 销毁实例 可以把这3步想象成执行了如下3行代码:
let s1 = new String("some text");
let s2 = s1.subString(2);
s1 = null
这种行为可以让原始值拥有对象的行为。对于布尔值和数值而言,以上3步也会在后台发生,只不过使用的是Boolean和Number类型的包装类型而已。
true === new Boolean(true); // false
123 === new Number(123); // false
'ConardLi' === new String('ConardLi'); // false
console.log(typeof new String('ConardLi')); // object
console.log(typeof 'ConardLi'); // string
2.2、装箱和拆箱
- 装箱:把基本类型转换为对应的包装类型【具体可见2.1 原始值包装类型】
- 拆箱:把引用类型转换为基本类型【参考下一篇文章:细节解读JS隐式类型转换(二)】
2.3、valueOf()与toString()
首先在类型转换中,经常会用到valueOf和toString(),所有对象都拥有这两个方法;
null和undefined没有这个方法;
boolean、number、string 以及对应的基本包装类型和对象可以调用此方法;
valueOf把对象转化成基本类型,转化不了就返回原对象
toString 返回,能表示这个对象的字符串
2.3.1、valueOf()
Boolean、Number、String及其对应的包装类型返回对应的原值
Boolean
const obj = new Boolean(true);
console.log(obj.valueOf());//true
console.log(typeof obj.valueOf());//boolean
//如果是包装类型的基本类型,则返回原基本类型值
const a = true;
console.log(a.valueOf());//true
console.log(typeof a.valueOf());//boolean
Number
const obj = new Number("123");
console.log(obj.valueOf());//123
console.log(typeof obj.valueOf());//number
//如果是包装类型的基本类型,则返回原基本类型值
const a = 123;
console.log(a.valueOf());//123
console.log(typeof a.valueOf());//number
String
const obj = new String("hello");
console.log(obj.valueOf());//hello
console.log(typeof obj.valueOf());//string
//如果是包装类型的基本类型,则返回原基本类型值
const a = "hello";
console.log(a.valueOf());//hello
console.log(typeof a.valueOf());//string
Function返回原函数
const a = function(){};
console.log(a.valueOf());//function(){};
console.log(a === a.valueOf());//true
Array返回原数组
const a = [1];
console.log(a.valueOf());//[1]
console.log(a === a.valueOf());//true
Obejct
const obj = {a:1};
console.log(obj.valueOf());//{a:1}
console.log(obj === obj.valueOf());//true
Date返回表示当前时间的数值
let obj = new Date();
console.log(obj);//Wed May 10 2017 12:19:05 GMT+0800 (中国标准时间)
console.log(obj.valueOf());//1494389910179
console.log(obj === obj.valueOf());//false
console.log(obj.getTime() === obj.valueOf());//true
总结一下:
- undefined和null没有此方法(基本类型肯定没有方法,String、Number和Boolean是因为有对应的基本包装类型,才可以调用方法);
- 基本包装类型和对应的基本类型,调用valueOf()返回对应的基本类型值;
- 对象类型(除Date类型)返回原对象;
- Date类型返回表示日期的毫秒数
2.3.2、toString()
a1、null和undefined没有这个方法
null.toString() // 报错
undefined.toString() // 报错
a2、Boolean类型返回对应的'true'和'false'
true.toString();//'true'
false.toString();//'false'
Boolean.toString();//"function Boolean() { [native code] }"
a3、字符串类型原值返回
'1'.toString();//'1'
''.toString();//''
'abc'.toString();//'abc'
String.toString();//"function String() { [native code] }"
a4、数值类型的情况较复杂
- 正浮点数及NaN、Infinity加引号返回
1.23.toString();//'1.23'
NaN.toString();//'NaN'
Infinity.toString();//'Infinity'
- 负浮点数或加'+'号的正浮点数直接跟上.toString(),相当于先运行toString()方法,再添加正负号,转换为数字
+1.23.toString();//1.23
typeof +1.23.toString();//'number'
-1.23.toString();//-1.23
typeof -1.23.toString();//'number'
- 整数直接跟上.toString()形式,会报错,提示无效标记,因为整数后的点会被识别为小数点
0.toString();//Uncaught SyntaxError: Invalid or unexpected token
因此,为了避免以上无效及报错的情况,数字在使用toString()方法时,加括号可解决
(0).toString();//'0'
(-0).toString();//'0'
(+1.2).toString();//'1.2'
(-1.2).toString();//'-1.2'
(NaN).toString();//'NaN'
此外,数字类型的toString()方法可以接收表示转换基数(radix)的可选参数,如果不指定此参数,转换规则将是基于十进制。同样,也可以将数字转换为其他进制数(范围在2-36)
var n = 17;
n.toString();//'17'
n.toString(2);//'10001'
n.toString(8);//'21'
n.toString(10);//'17'
n.toString(12);//'15'
n.toString(16);//'11'
a5、对象Object类型及自定义对象类型加括号返回[object Object]
{}.toString();//报错,Unexpected token .
({}).toString();//[object Object]
({a:123}).toString();//[object Object]
Object.toString();//"function Object() { [native code] }"
function Person(){
this.name = 'test';
}
var person1 = new Person();
person1.toString();//"[object Object]"
类型识别
常常使用Object.prototype.toString()来进行类型识别,返回代表该对象的[object 数据类型]字符串表示 [注意]Object.prototype.toString()可以识别标准类型及内置对象类型,但不能识别自定义类型
console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
function Person(){};
console.log(Object.prototype.toString.call(new Person));//[object Object]
其他识别
除了类型识别之外,还可以进行其他识别,如识别arguments或DOM元素
(function(){
console.log(Object.prototype.toString.call(arguments));//[object Arguments]
})()
console.log(Object.prototype.toString.call(document));//[object HTMLDocument]
a6、函数Function类型返回函数代码
当我们对一个自定义函数调用toString()方法时,可以得到该函数的源代码;如果对内置函数使用toString()方法时,会得到一个'[native code]'字符串。因此,可以使用toString()方法来区分自定义函数和内置函数
function test(){
alert(1);//test
}
test.toString();/*"function test(){
alert(1);//test
}"*/
Function.toString();//"function Function() { [native code] }"
a7、数组Array类型返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串
[].toString();//''
[1].toString();//'1'
[1,2,3,4].toString();//'1,2,3,4'
Array.toString();//"function Array() { [native code] }"
a8、时间Date类型返回表示当前时区的时间的字符串表示
(new Date()).toString();//"Sun Jun 05 2016 10:04:53 GMT+0800 (中国标准时间)"
Date.toString();//"function Date() { [native code] }"
a9、正则表达式RegExp类型返回正则表达式字面量的字符串表示
/ab/i.toString();//'/ab/i'
/mom( and dad( and baby)?)?/gi.toString();//'mom( and dad( and baby)?)?/gi'
RegExp.toString();//"function RegExp() { [native code] }"
a10、错误Error类型
Error.toString();//"function Error() { [native code] }"
RangeError.toString();//"function RangeError() { [native code] }"
ReferenceError.toString();//"function ReferenceError() { [native code] }"
SyntaxError.toString();//"function SyntaxError() { [native code] }"
TypeError.toString();//"function TypeError() { [native code] }"
URIError.toString();//"function URIError() { [native code] }"