类型转换
- 显示类型转换:把值从一个类型转成另一种类型
- 强制类型转换:会在内部进行一些内置的操作,转换为目标类型
valueOf():将(内置)对象转换为原始值
对象 | 返回值 |
---|---|
Array | 返回数组对象本身 |
Boolean | 布尔值 |
String | 字符串值 |
Number | 数字值 |
Date | 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC |
Function | 函数本身 |
Object | 对象本身。这是默认情况 |
Math 和 Error 对象 | 没有 valueOf 方法 |
- Array:返回数组对象本身
var array = ["emoji", true, 12, -5]; console.log(array.valueOf()); // ["emoji", 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
- Boolean:返回布尔值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
总结: 基本数据类型的valueOf
返回的是自身的原始类型值,而对象都返回自身,Date
返回时间戳,null
、undefined
没有valueOf
方法
toString():返回这个对象的字符串表示
- 每个对象都有
toString()
方法,如果自定义对象没有重写该方法,toString
会返回[object type]
,type
是对象的数据类型var o = new Object(); o.toString(); // "[object Object]"
-
可以使用
toString()
来检测对象的类型:
每个对象都能通过Object.prototype.toString()
来检测的前提是需要用Function.prototye.call()/apply()
的形式来调用toString()
Object.prototype.toString.call(thisArg);
// 参数:需要检查的对象var toString = Object.prototype.toString; 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 String) // "[object String]" Object.prototype.toString.call(new Object) // "[object Object]"
类型转换只有三中情况:转成布尔值、转成字符串、转成数值
- 内部转换过程 — 检测类型的四个
js
内部方法:对象转换类型的时候,会调用内置的[[ToPrimitive]]
函数获取[[DefaultValue]]
,下面是内置函数的具体流程:
-
ToPrimitive(input[,PreferedType])
参数input
:是输入的值
参数PreferedType
:是转换的目标类型- 转换的目标类型是
Number
- 如果输入的值已经是原始类型,那就不需要转换,直接返回这个值;
- 如果输入的值是对象,调用
input.valueOf()
获取(内置)对象的原始值,是基本数据类型就返回这个值; - 如果上一步获取不到原始类型值,调用
input.toString()
获取,如果转换为基础类型,就返回转换的值 - 如果都没有返回原始类型,就报错
- 转换的目标类型是
String
第二第三步交换,先执行toString()
也可以省略preferedType
(转换的目标类型),此时,日期会被认为是字符串,而其他的值会被当做Number
- 转换的目标类型是
ToString(argument)(Object.prototype.toString())
对象 | 返回值 |
---|---|
Null | Return 'null' |
Undefined | Return 'undefined' |
Boolean | 如果 argument 为 true, return 'true'. 如果 argument 为 false, return 'false' |
String | Return argument |
Number | 将数值用字符串表示 |
Date | 用字符串表示 |
Object | 先primValue = ToPrimitive(argument, Number),再对primValue 使用 ToString(primValue) |
- 对普通对象来说,除非自定义重写
toString
方法,否则都返回内部属性[[class]]
的值, 如"[object object]"
- 就是如果对象有自己的
toString()
方法,字符串化的时候会先调用自己的方法 - 类型转换的时候是通过什么来实现的(类型转换内部实现):
数组的默认toString()
方法是进行过重写的,所以数组转换为字符串时的返回与对象不同
var a = [1, 2, 3];
a.toString(); // "1,2,3"
将值转换为相应的基本类型值,抽象操作ToPrimitive
会首先检查该值是否有valueOf()
方法,如果有并且返回基本类型值,就使用该值强制类型转换,如果没有就使用toString()
的返回值来进行强制类型转换
-
ToNumber(argument)
:对象 返回值 Null Return 0 Undefined Return NaN Boolean 如果 argument 为 true, return 1. 如果 argument 为 false, return +0 String 将字符串中的内容转化为数字(比如"23"->23),如果转化失败则返回NaN(比如"23a"->NaN) Number Return argument Date 返回时间戳 Object 先primValue = ToPrimitive(argument, Number),再对primValue 使用 ToNumber(primValue) -
ToBoolean()
:- 假值有:
undefined
、null
、false
、0
、NaN
、""
除了上面的假值,其他都是真值
// 通过document.all强制类型转换为布尔值来判断浏览器是否是老版本ie Boolean(document.all); // false Boolean(window.name); // false
- 假值对象:
浏览器在某些特定情况下,在常规js
语法基础上自己创建了一些外来值,相当与不是统一规范都有的属性,是每个浏览器特有的属性,像document.location
大家都一样,将他们强制类型转换为布尔值,结果返回false - 封装了假值的对象:
// 封装了假值的对象 var a = new Boolean(false); // Boolean {false} var b = new Boolean(0); // Boolean {false} var c = new Boolean(""); // Boolean {false} var d = a && b && c; // Boolean {false} var e = Boolean(a && b && c); // true
- 假值有:
- 为什么使用e返回true,而不是false?
因为abc返回的都是对象object,是一个真值- 字符串和数字之间的转换
通过原生构造函数:String()
、Number()
- 获取时间戳:
`var timestamp = +new Date();` // 方法获取 推荐获取指定时间的时间戳 `var timestamp = new Date().getTime();` // 静态方法获取 推荐使用 `var timestamp = Date.now();````
-
~
~x
大致等同于-(x+1)
一般我们判断字符串的位置,检查字符串中是否包含指定的字符串用indexOf()
,它返回字符串所在的位置,不存在就返回-1
,我们会用if条件来判断是否满足我们需要的条件,但是这样的写法不是很好,代码中暴露了底层实现细节,指indexOf
底层查找失败就返回-1
这种,这些细节我们在写代码的时候应该被屏蔽掉;var a = "Hello world"; if(a.indexOf("lo") >= 0){ // true // 找到匹配 } if(a.indexOf("lo") == -1){ // true // 没有找到匹配 }
~
搭配indexOf
可以将indexOf
查找的结果强制类型转换为真或者假值
可以使用这种方式来判断,更简洁:var a = "Hello world"; if(~a.indexOf("lo")){ // -4 true // 找到匹配 } ~a.indexOf("ol"); // 0 是一个假值
- 字位截除
Math.floor(-49.6); // -50 ~~-49.6 // -49 -49.6 | 0。 // -49 ~~x 、 x | 0 都可以把值截除成一个整数(32位整数)
- 显示解析数字字符串
解析和转换:
解析:从左到右解析,如有非数字的字符就停止返回数字部分var a = "42"; var b = "42px"; Number(a); // 42 Number(b); // NaN parseInt(a); // 42 默认转换为10进制数,如果要在es5之前环境,要设置第二个参数为10 parseInt(b); // 42
转换:不允许有非数字的字符,会转换失败返回NaN
(转换就是原生构造函数不带new的那些)-
parseInt(1/0, 19); // 18
parseInt
的第一个参数是字符串,会先将参数转为字符串
- 先将参数:
1/0
转换为字符串((1/0).toString())
是"Infinity"
-
19
是基数,parseInt("Infinity", 19)
第一个字符I以19为基数时,值为18
,第二个n
不是游戏啊的数字字符串,所以解析到n
就结束了
0 1 2 3 4 5 6 7 8 9 a:10 b:11 c:12 d:13 e:14 f:15 g:16 h:17 i:18 parseInt( 0.000008 );// 0 0来自于 "0.000008" parseInt( 0.0000008 );// 8 8来自于 8e-7 e不是数字就结束
- JS在处理数值的时候,如果数值小数位数超过6位,就会转换为科学计数法,整数的多于21位也会转为科学计数法
parseInt( false, 16 );// 250 "fa" 来自于 "false" fa的16进制转为十进制 a*16^0 + f*16^1 = 250 parseInt( parseInt, 16 );// 15 "f" 来自于 "function.." parseInt( "0x10" );// 16 0x是16进制的表示,16进制的10转为十进制 0*16^0 + 1*16^1 = 16 parseInt( "103", 2 );// 2 按2进制为基础解析字符串“103”,2进制只有0和1,返回10,转为10进制 0*2^0 + 1*2^1 = 2
-
- 隐式强制类型转换:
减少冗余,让代码更简洁
什么时候代表相加,什么时候是进行字符串拼接,为什么下面是这样的结果:
因为数组的var a = [1,2]; var b = [3,4]; a + b; // "1,23,4"
valueOf()
操作无法得到简单基本类型值,于是它转而调用toString()
。因此上例中的两 个数组变成了"1,2"
和"3,4"
。+
将它们拼接后返回"1,23,4"
。
如果+
的其中一个操作数是字符串或者是通过valueOf
或者toString
能得到字符串,就进行拼接,不然就相加
需要注意:'a'++'b' // "aNaN" +'b' 获取number类型 ->NaN
- 它们返回不同结果:
[]+{} -> ""+{} -> "[object Object]"
{}+[] -> 0 // 有的js解释器会将开头的 {} 看作一个代码块,而不是一个js对象,于是真正参与运算的是+[],就是将[]转换为number,于是得出答案0
- 数字强制类型转换为字符串:
var a = 42; var b = a + ""; b; // "42"
a + ""
(隐式)和前面的String(a)
(显式)之间有一个细微的差别需要注意。根据ToPrimitive
抽象操作规则,a + ""
会对a
调用valueOf()
方法,然后通过ToString
抽象操作将返回值转换为字符串。而String(a)
则是直接调用ToString()
。- 字符串强制类型转换为数字:
var a = "3.14"; var b = a - 0; b; // 3.14 var a = [3]; var b = [1]; a - b; // 2
a
和b
都需要被转换为数字,它们首先被转换为字符串(通过ToString()
),然后再转换为数字。-
隐式强制类型转换为布尔值:
1.if (..)
语句中的条件判断表达式。
2.for ( .. ; .. ; .. )
语句中的条件判断表达式(第二个)。
3.while (..)
和do..while(..)
循环中的条件判断表达式。
4.? :
中的条件判断表达式。
5. 逻辑运算符||
(逻辑或)和&&
(逻辑与)左边的操作数(作为条件判断表达式)。
以上情况中,非布尔值会被隐式强制类型转换为布尔值,遵循前面介绍过的ToBoolean
抽象操作规则。- || 和 &&
返回操作数中的一个值,根据这个值来判断真假
var a = 42; var b = "abc"; var c = null; a || b; // 42 a && b; // "abc" c || b; // "abc" c && b; // null
- 首先判断第一个操作数,不是布尔值进行
ToBoolean
转换再判断 - 判断第一个操作数结果为
true
,||
返回第一个操作数,false
返回第二个操作数(有一个真就真) - 判断第一个操作数结果为
true
,&&
返回第二个操作数,false
返回第一个操作数(有一个假为假)
-
==
允许在相等比较中进行强制类型转换,而===
不允许
就是在判断的时候,宽松相等和严格相等都会检查操作数的类型,就是处理方式不一样,==
会进行类型转换 - 字符串和数字之间的相等比较:
var a = 42; var b = "42"; a === b; // false a == b; // true
这里会有疑问,是把数值转为字符串,还是把字符串转为数值进行比较呢?具体内部转换规则:转为数字来比较!
(1) 如果Type(x)
是数字,Type(y)
是字符串,则返回x == ToNumber(y)
的结果。
(2) 如果Type(x)
是字符串,Type(y)
是数字,则返回ToNumber(x) == y
的结果。- 布尔值和其他类型之间的比较
var a = "42"; var b = true; a == b; // false
将是布尔值的转为数值来比较;
(1) 如果 Type(x) 是布尔类型,则返回 ToNumber(x) == y 的结果;
(2) 如果 Type(y) 是布尔类型,则返回 x == ToNumber(y) 的结果。
===不允许强制类型转换,不涉及ToNumber- 对象与非对象之间的比较
var a = 42; var b = [ 42 ]; a == b; // true
(1) 如果
Type(x)
是字符串或数字,Type(y)
是对象,则返回x == ToPrimitive(y)
的结果;
(2) 如果Type(x)
是对象,Type(y)
是字符串或数字,则返回ToPromitive(x) == y
的结果。- 假值相等比较:
有一个为null
或者undefined
,return false
;
有一个为NaN
,return false
;
有布尔值Boolean
、字符串String
、数字Number
类型不一致的,都转成Number
再比较
有对象的,转换为原始值再比较(先调用ToPrimitive
)
"0" == null; // false "0" == undefined; // false "0" == false; // true "0" == NaN; // false "0" == 0; // true "0" == ""; // true false == null; // false false == undefined; // false false == NaN; // false false == 0; // true Number(false) -> 0 false == ""; // true false == []; // true false == {}; // false Number({}) ->NaN "" == null; // false "" == undefined; // false Number(undefined) ->NaN "" == NaN; // false "" == 0; // true "" == []; // true "" == {}; // false 0 == null; // false 0 == undefined; // false 0 == NaN; // false 0 == []; // true 0 == {}; // false [] == ![]; // true ![] ->false 2 == [2]; // true "" == [null]; // true 0 == "\n"; // true 空字符串被ToNumber强制转为0 "true" == true; // false Number("true") ->NaN
- 抽象关系比较
< >
:
如果是对象,先调用ToPrimitive
如果有一个是非字符串,就把字符串转成数字比较
如果两个都是字符串就按字符顺序来比较
var a = [ "42" ]; var b = [ "043" ]; a < b; // false var a = { b: 42 }; "[object, object]" var b = { b: 43 }; a < b; // false
<=
表示不大于的意思>=
不小于的意思var a = { b: 42 }; var b = { b: 43 }; a <= b; // true b < a ->反转结果false ->true a >= b; // true b < a ->反转结果false ->true
- || 和 &&
- 它们返回不同结果:
- 字符串和数字之间的转换