JS 数据类型的识别与转换

数据类型识别

// 代码示例:根据类型识别来进行不同的实现 function toDate(param){}
	/*
	 *输入格式:
	 *'2015-8-15'
	 *'1438744815232'
	 *{y:2015,m:8,d:5}
	 *{2015,8,5}
	 *返回格式:Date
	 */
 
 function toDate(param){
 	if(typeof(param)=='string' || typeof(param)=='number'){
		return new Date(param);
 	}
 	if(param instanceof Array){
		var date=new Date(0);
		date.setYear(param[0]);
		date.setMonth(param[1]-1);
		date.setDate(param[2]);
		return date;
	}
	if(typeof(param)=='object'){
		var date=new Date(0);
		date.setYear(param.y);
		date.setMonth(param.m-1);
		date.setDate(param.d);
		return date;
	}
	return -1;
}

JavaScript有复杂的类型系统,类型识别则是基本的功能,JS 提供了四种类型识别的方法


[1] typeof 运算符                             // 运算符后面带不带圆括号都可以

typeof 是一元运算符,放在单个操作数的前面,返回值为表示操作数类型的首字母小写的字符串

console.log(typeof 'a');    // 'string'
console.log(typeof ('a'));    // 'string'

识别 ] 可以识别标准类型( 将Null 识别为 'object' );但,不能识别具体的对象类型( Function 除外 )

console.log(typeof "jerry");   // "string"
console.log(typeof 12);   // "number"
console.log(typeof true);   // "boolean"
console.log(typeof undefined);   // "undefined"
console.log(typeof null);   // "object"
console.log(typeof {name: "jerry"});   // "object"

console.log(typeof function(){});   // "function"
console.log(typeof []);   // "object"
console.log(typeof new Date);   // "object"
console.log(typeof /\d/);   // "object"
function Person(){};
console.log(typeof new Person);   // "object"

另,判断一个值是否为 null 类型的最佳方法是直接和 null 进行恒等比较

console.log(typeof null);   // 'object'
console.log(null === null);   // true
console.log(undefined === null);   // false
console.log('null' === null);   // false

[2] instanceof 运算符                                         // 所有的对象都是 Object 的实例

instanceof 是一个二元运算符,左操作数是一个对象,右操作数是一个构造函数。

  • 如果左侧的对象是右侧构造函数的实例对象,则表达式返回 true,否则返回 false
  • 如果左操作数不是对象,返回 false,如果右操作数不是函数,则抛出一个类型错误异常 TypeError
console.log(123 instanceof function(){});//false
//Uncaught TypeError: Right-hand side of 'instanceof' is not an object
console.log({} instanceof 123);

[ 识别 ]

1. 可以识别内置对象类型、自定义类型及其父类型(可以识别所有的对象类型

2. 不能识别标准类型,会返回 false

3. 不能识别undefined、null,会报错

console.log("jerry" instanceof String);   // false
console.log(12 instanceof Number);   // false
console.log(true instanceof Boolean);   // false
console.log(undefined instanceof Undefined);   // 报错
console.log(null instanceof Null);   // 报错
console.log({name: "jerry"} instanceof Object);   // true

console.log(function(){} instanceof Function);   // true
console.log([] instanceof Array);   // true
console.log(new Date instanceof Date);   // true
console.log(/\d/ instanceof RegExp);   // true
function Person(){};
console.log(new Person instanceof Person);   // true
console.log(new Person instanceof Object);   // true

[3] constructor 属性

实例对象的constructor属性指向其构造函数。

  • 如果是内置类型,则输出 function 数据类型(){[native code]};
  • 如果是自定义类型,则输出 function 数据类型(){}

[ 识别 ]

  • 可以识别标准类型、内置对象类型及自定义类型
  • 不能识别undefined、null,会报错,因为它俩没有构造函数
console.log(("jerry").constructor);//function String(){[native code]}
console.log((12).constructor);//function Number(){[native code]}
console.log((true).constructor);//function Boolean(){[native code]}
console.log((undefined).constructor);//报错
console.log((null).constructor);//报错
console.log(({name: "jerry"}).constructor);//function Object(){[native code]}

console.log((function(){}).constructor);//function Function(){[native code]}
console.log(([]).constructor);//function Array(){[native code]}
console.log((new Date).constructor);//function Date(){[native code]}
console.log((/\d/).constructor);//function RegExp(){[native code]}
function Person(){};
console.log((new Person).constructor);//function Person(){}

[ 通用 ] 可以将 constructor 属性封装成一个类型识别方法

function type(obj){
    var temp = obj.constructor.toString();
    return temp.replace(/^function (\w+)\(\).+$/,'$1');
}
function type(obj){
    var temp = obj.constructor.toString().toLowerCase();
    return temp.replace(/^function (\w+)\(\).+$/,'$1');
}
console.log(type("jerry"));  // "string"
console.log(type(12));  // "number"
console.log(type(true));  // "boolean"
console.log(type(undefined));  // 错误
console.log(type(null));  // 错误
console.log(type({name: "jerry"}));  // "object"

console.log(type(function(){}));  // "function"
console.log(type([]));  // "array"
console.log(type(new Date));  // "date"
console.log(type(/\d/));  // "regexp"
function Person(){};
console.log(type(new Person));  // "person"

[4] Object.prototype.toString() 方法

对象的类属性是一个字符串,用以表示对象的类型信息。JS 没有提供设置这个属性的方法,但有一种间接方法可以查询它。Object.prototype.toString() 方法返回了如下格式的字符串:[object 数据类型]

[ 识别 ]

  • 可以识别标准类型及内置对象类型
  • 不能识别自定义类型
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]

[ 通用 ] 可以将 Object.prototype.toString() 方法封装成一个类型识别方法

function type(obj){
    return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();
}
console.log(type("jerry"));//"string"
console.log(type(12));//"number"
console.log(type(true));//"boolean"
console.log(type(undefined));//"undefined"
console.log(type(null));//"null"
console.log(type({name: "jerry"}));//"object"

console.log(type(function(){}));//"function"
console.log(type([]));//"array"
console.log(type(new Date));//"date"
console.log(type(/\d/));//"regexp"
function Person(){};
console.log(type(new Person));//"object"

[注意] 如果是包装对象,Object.prototype.toString() 方法将返回其原始类型

console.log(Object.prototype.toString.call(new Number(123)));//[object Number]
console.log(Object.prototype.toString.call(123));//[object Number]
console.log(Object.prototype.toString.call(new String('abc')));//[object String]
console.log(Object.prototype.toString.call('abc'));//[object String]
console.log(Object.prototype.toString.call(new Boolean(true)));//[object Boolean]
console.log(Object.prototype.toString.call(true));//[object Boolean]

[5] 数组检测方式?↓ 


数据类型的转换:隐式 & 显式

[1] 隐式类型转换                     // JS 是弱类型语言,语言本身会进行一系列的隐式类型转换。

// 出现场景
   1. 数字运算符(+):字符串+数字,数字会被转换成字符串进行计算;
   2. 点号(.):数字型/字符串直接量用点号调用方法,会被转换成对应的对象类型,然后调用其方法,实现相应的功能。
   3. if 语句:条件部分的表达式会被隐式的转换成Boolean值。
   4. ==运算符    var num="4";num==4;    // true

隐式类型转换又称为自动类型转换,JS 中的运算符和语句中存在着大量的自动类型转换,其规则是:预期什么类型的值,就调用该类型的转换函数。类似地,将隐式类型转换分为转为布尔、转为数值和转字符串。


[ 转为布尔 ]

1. 逻辑非运算符(!)首先会将它的操作数转换成一个布尔值,然后再对其求反。如果同时使用两个逻辑非操作符,实际上就会模拟Boolean()转型函数的行为

console.log(!!undefined);//false
console.log(!!null);//false
console.log(!!0);//false
console.log(!!-0);//false
console.log(!!NaN);//false
console.log(!!'');//false
console.log(!!false);//false

2. 条件运算符(?:)首先会将它问号(?)前的第一个操作数转换成一个布尔值,如果它是真值,那么将计算第二个操作数,并返回其计算结果。否则,如果第一个操作数是假值,那么将计算第三个操作数,并返回其计算结果

console.log(true ? 1 : 0);//1
console.log({} ? 1 : 0);//1
console.log([123] ? 1 : 0);//1
console.log('' ? 1 : 0);//0

3. if条件语句的条件的求值结果会转换为一个布尔值,其实条件运算符只是条件语句的简写形式

var a = 1;
if(a){console.log(1)};//1
if(10){console.log(1)};//1

4. 类似地,while循环语句的条件的求值结果也会转换为一个布尔值

var a = 1;
while(a){
    console.log(1);
    break;
};//1
while(10){
    console.log(1);
    break;
}//1

[ 转为数字 ]

1. 算术运算符将它的操作数转为数字

var a = '123';
console.log(+a);//123
console.log(a-0);//123
console.log(a*1);//123
console.log(a/1);//123
console.log(a%Infinity);//123
// 在涉及到加法的运算中,对象转换为原始值(先toString()后valueOf())后,如果两个操作数都不是字符串,则两个操作数都将转换成数字
   console.log(undefined + undefined);//NaN
   console.log(null + null);//0
   console.log(true + true);//2

2. 位运算符将它的操作数转为数字

var a = '123';
console.log(~~a);//123
console.log(a & 1);//1
console.log(a | 0);//123
console.log(a ^ 0);//123
console.log(a<<0);//123
console.log(a>>0);//123
console.log(a>>>0);//123

// 除了按位与(&)操作之外,其他运算符都可以实现小数取整的效果

3. 涉及关系运算符(==、!=、>=、>、<、<=)的运算中,对象转换为原始值(先valueOf()后toString())后,如果至少有一个操作数不是字符串,则两个操作数都将通过Number()转型函数转换成数字进行数值比较

console.log([1] == 1);//true,相当于1 == 1
console.log([] == true);//false,相当于0 == 1
console.log({} > true);//false,相当于 NaN > 1
console.log('true' <= 0);//false,相当于NaN <= 0

[ 转字符串 ]

1. 在涉及加法运算符的运算中,对象转换为原始值(先toString()后valueOf())后,只要有一个操作数是字符串,另一个操作数也会转换成字符串

console.log(1 + {});//'1[object Object]'
console.log(1 + [1,2]);//'11,2'
console.log(1 + new Date());//'Fri Jul 15 2016 22:12:05 GMT+0800 (中国标准时间)'
console.log(1 + /0/);//'1/0/'
console.log('' + undefined);//'undefined'
console.log('' + null);//'null'
console.log('' + false);//'false'
console.log('' + true);//'true'

2. 在涉及关系运算符(==、!=、>=、>、<、<=)的运算中,在对象转换为原始值(先valueOf()后toString())之后,如果两个操作数都是字符串,则进行字符串的比较

console.log(new Date() == 'Fri Jul 15 2016 22:12:05 GMT+0800 (中国标准时间)');//true

// 一个值转换为另一个值并不意味着两个值相等

console.log(Number(null));//0
console.log(null == 0);//false

console.log(Boolean(NaN));//false
console.log(NaN == false);//false

[2] 显式类型转换

显式类型转换,又称为强制类型转换,以下分别为转成布尔、转成数字和转字符串的强制类型转换。

[ 转成布尔 ]  将一个值转为布尔值可使用Boolean()转型函数

假值:转换成false的值称为假值(falsy value),这7个值包括undefined、null、+0、-0、NaN、false、""(空字符串)

console.log(Boolean(undefined));   // false
console.log(Boolean(null));   // false
console.log(Boolean(0));   // false
console.log(Boolean(-0));   // false
console.log(Boolean(NaN));   //false
console.log(Boolean(''));   // false
console.log(Boolean(false));   // false
// 在Number()方法中空字符串和空白字符串都转换为0,而在Boolean()方法中,空字符串""转换为false,而空白字符串" "转换为true
   console.log(Number(''));   // 0
   console.log(Number(' '));   // 0

   console.log(Boolean(''));   // false
   console.log(Boolean(' '));   // true

除了这7个假值外,其他的值转换为布尔值都是true,也称为真值(truthy value)

[注意]所有对象(包括空对象)的转换结果都是true,甚至连false对应的布尔对象new Boolean(false)也是true

console.log(Boolean({}));   // true
console.log(Boolean([]));   // true

console.log(Boolean(new Boolean(false)));   // true
console.log(Boolean(false));   // false
console.log(Boolean(new Boolean(null)));   // true
console.log(Boolean(null));   // false

[ 转成数值 ]  有3个函数可以把非数值转换成数值:Number()、parseInt()和parseFloat()。

其中,Number()可以将任意类型的值转化成数值,而parseInt()和parseFloat()只应用于字符串向数字的转换。

1. Number()

当把Number()当作一个函数来调用,而不是作为构造器,它执行一个类型转换。使用Number()函数可以将任意类型的值转化成数值。

// 数值:十进制数字
console.log(Number(11),Number(011),Number(0x11));  // 11 9 17

// undefined:转成 NaN
Number(undefined)  // NaN

// null:转成0
Number(null)  // 0

// 布尔值:true 转成1,false 转成0
console.log(Number(true),Number(false));  // 1 0
Number()函数解析字符串时会识别出字符串的前置空格并去掉。

1. 若字符串只包含十进制或十六进制数字,则转成十进制的数字。
	[注意1]Number()不识别八进制数字的字符串,会按照十进制数字处理;
	[注意2]字符串'1.2.'不会报错,但数字1.2.会报错;
2. 若字符串为空字符串或空格字符串,则转成0。
3. 其他情况的字符串,则转成NaN。

console.log(Number('    123'));  // 123
console.log(Number('1.2.'));  // NaN
console.log(Number(1.2.));  // 报错
console.log(Number(''),Number(' '));  // 0 0 
console.log(Number('11'),Number('011'),Number('0x11'));  // 11 11 17
console.log(Number('abc'));  // NaN
console.log(Number('123abc'));  // NaN
Number()函数解析对象时,会按照以下步骤进行处理 :
1. 调用对象的valueOf()方法,如果返回原始类型的值,则直接对该值使用Number()函数。
2. 如果valueOf()方法返回的还是对象,则调用对象的toString()方法,如果返回原始类型的值,则对该值
    使用Number()函数。
3. 如果toString()方法返回的依然是对象,则结果是NaN。

在第一步中,由于只有时间Date()对象返回的是原始类型的值数字,所以Number(new Date())返回现在
到1970年1月1日00:00:00的数值类型的毫秒数。

Number(new Date())  // 1465976459108

在第二步中,数组Array类型返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串,如果
字符串中只存在数字,则返回数字,其他情况返回NaN;由于其他对象的toString()方法返回的字符串中不只
包括数字,所以返回NaN。

Number([]);  // 0
Number([0]);  // 0
Number([-0]);  // 0
Number([10]);  // 10
Number([1,2]);  // NaN
Number(其他对象);  // NaN

2. parseInt()

1. parseInt()专门用于把字符串转换成整数。在转换字符串时,会忽略字符串前面的空格,直到找到第一个
非空格字符。如果第一个字符不是数字字符或者负号,parseInt()就会返回NaN。如果是,则继续解析,直
到解析完成或者遇到非数字字符。

console.log(parseInt('    123.8px'));  // 123
console.log(parseInt('   123.8   '));  // 123
console.log(parseInt(' -123.8px'));  // -123
console.log(parseInt('a123.8px'));  // NaN
console.log(parseInt('0 123.8px'));  // 0

2. parseInt()可以识别出各种进制的数字,输出的是运算后的十进制的数字,如1.0或1.或01会以1输出。
在解析八进制字面量的字符串,ECMAScript3会解析八进制,但ECMAScript5没有解析八进制的能力。

console.log(parseInt('11'));  // 11
console.log(parseInt(11));  // 11
console.log(parseInt('11.1'));  // 11
console.log(parseInt(11.1));  // 11
console.log(parseInt('011'));  // 11
console.log(parseInt(011));  // 9
console.log(parseInt('011.1'));  // 11
console.log(parseInt(011.1));  // 报错
console.log(parseInt('0x11'));  // 17
console.log(parseInt(0x11));  // 17
console.log(parseInt('0x11.1'));  // 17
console.log(parseInt(0x11.1));  // 报错

[注意]对于那些会自动转为科学计数法的数字,parseInt会将科学计数法的表示方法视为字符串,因此导致
一些奇怪的结果。

console.log(parseInt(1000000000000000000000.5));  // 1
// 等同于
console.log(parseInt('1e+21'));  // 1

console.log(parseInt(0.0000008));  // 8
// 等同于
console.log(parseInt('8e-7'));  // 8

3. parseInt()方法还可以接受第二个参数(2到36之间),表示被解析的值的进制,返回该值对应的十进制数。
默认情况下,parseInt的第二个参数为10,即默认是十进制转十进制。

console.log(parseInt('11',2));  // 3
console.log(parseInt('11',8));  // 9
console.log(parseInt('11',10));  // 11
console.log(parseInt('11',16));  // 17

如果第二个参数不是数值,会被自动转为一个整数。这个整数只有在2到36之间,才能得到有意义的结果,
超出这个范围,则返回NaN。如果第二个参数是0、undefined和null,则直接忽略。

console.log(parseInt('10', 37));  // NaN
console.log(parseInt('10', 1));  // NaN
console.log(parseInt('10', 0));  // 10
console.log(parseInt('10', null));  // 10
console.log(parseInt('10', undefined));  // 10

如果字符串包含对于指定进制无意义的字符,则从最高位开始,只返回可以转换的数值。
如果最高位无法转换,则直接返回NaN。

console.log(parseInt('1546', 2));  // 1
console.log(parseInt('546', 2));  // NaN

4. parseInt()是专门用来处理字符串转换数字的,parseInt处理非字符串和数字类型时输出NaN。但是,实际上
parseInt()包含着隐式的toString()方法,所以parseInt([数字或字符串])输出对应的数字。

console.log(parseInt(null),parseInt(undefined));  // NaN NaN
console.log(parseInt(true),parseInt(false));  // NaN NaN
console.log(parseInt([]),parseInt(['2.5px']),parseInt([2.5]));  // NaN 2 2
console.log(parseInt(''),parseInt(' '),parseInt({}));  // NaN NaN NaN

3. parseFloat()

1. parseFloat()专门用于字符串转换浮点数。同样地,解析时会忽略字符串前面的空格,直到找到第一个
非空格字符,然后一直解析到字符串末尾或一个无效的浮点数字字符为止。

console.log(parseFloat('    0123.px'));//123
console.log(parseFloat('    123.px'));//123
console.log(parseFloat('    123.1px'));//123.1
console.log(parseFloat('   123.1.2px   '));//123.1
console.log(parseFloat(' -123.0px'));//-123
console.log(parseFloat('.123.1px'));//0.123
console.log(parseFloat('0 123px'));//0

[注意]如果字符串符合科学计数法,则会进行相应的转换。

console.log(parseFloat('314e-2')); // 3.14
console.log(parseFloat('0.0314E+2')); // 3.14

2. parseFloat()可以识别不同进制的数字,但只能解析十进制字符串。

console.log(parseFloat('11'));//11
console.log(parseFloat(11));//11
console.log(parseFloat('11.1'));//11.1
console.log(parseFloat(11.1));//11.1
console.log(parseFloat('011'));//11
console.log(parseFloat(011));//9
console.log(parseFloat('011.1'));//11.1
console.log(parseFloat(011.1));//报错
console.log(parseFloat('0x11'));//0
console.log(parseFloat(0x11));//17
console.log(parseFloat('0x11.1'));//0
console.log(parseFloat(0x11.1));//报错

3. parseFloat()是专门用来处理字符串转换浮点数的,parseFloat处理非字符串和数字类型时输出NaN。
但是,实际上parseFloat()包含着隐式的toString()方法,所以parseFloat([数字或字符串])输出对应的数字。

console.log(parseFloat(null),parseFloat(undefined));//NaN NaN
console.log(parseFloat(true),parseFloat(false));//NaN NaN
console.log(parseFloat([]),parseFloat([2.1]),parseFloat(['2.1px']));//NaN 2.1 2.1 
console.log(parseFloat(''),parseFloat({}));//NaN NaN

[注意]Number('')的结果是0,parseInt('')和parseFloat('')的结果是NaN

[ 转成字符串 ]  把一个值转换为字符串有两种方式,toString()和String(),或 '' + 某个值,将该值转换为字符串。

1. toString()

第一种是使用几乎每个值都有的toString()方法,这个方法返回相应值的字符串表现。

[注意]undefined和null没有该方法。

undefined.toString();  // 错误
null.toString();  // 错误
true.toString();  // 'true'
false.toString();  // 'false'
'abc'.toString();  // 'abc'
1.23.toString();  // '1.23'
({}).toString();  // [object Object]
[1,2,3,4].toString();  // '1,2,3,4'
(new Date()).toString();  // "Sun Jun 05 2016 10:04:53 GMT+0800 (中国标准时间)"
/ab/i.toString();  // '/ab/i'

2. String()              // 在不知道要转换的值是不是undefined或null时,可以使用转型函数String()。

转型函数String()遵循下列规则:
1. 如果值是null,则返回'null';如果值是undefined,则返回'undefined'。
2. 如果值不是null或undefined,则调用toString()方法并返回原始类型值。
3. 若使用toString()方法返回的是对象,则再调用valueOf()方法返回原始类型值,若使用valueOf()方法返回的
   是对象,会报错。

// "3"
String({toString: function () {
    return 3;
  }
})

// "[object Object]"
String({valueOf: function () {
    return 2;
  }
})

// "3"
String({
  valueOf: function () {
    return 2;
  },
  toString: function () {
    return 3;
  }
})