js数据类型
基本数据类型(原始值):
- undefined
- Null
- Boolean
- String
- Number
- Symbol(ES6新增)
- BigInt(ES10新增)
复杂数据类型(对象值):
- Object
三种隐式转换
将值转换为原始值
js内部引擎有这样的操作:
ToPrimitive(input, PreferredType?)
input表示要转换的值,PreferredType表示转换标志,可选值(可以是Number、String类型
转换后的结果不一定就是PreferredType代表的类型值,但是一定是一个原始值或者报错,因为转换成类型值有如下操作流程:
PreferredType为Number:
PreferredType为String:1、如果输入的值已经是一个原始值,则直接返回它
2、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
3、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
4、否则,抛出TypeError异常。
如果ToPrimitive的PreferredType没有选择,则有一个默认的值:1、如果输入的值已经是一个原始值,则直接返回它
2、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
3、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
4、否则,抛出TypeError异常。
valueOf和toString方法1、该对象为Date类型,则PreferredType被设置为String
2、否则,PreferredType被设置为Number
这两个方法在对象中是一定存在的,Object.protoType里面一定有这两种方法
先看看对象的valueOf方法,对于js常见的内置对象:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function
1、Number、Boolean、String这三种构造函数生成的基础值对象形式,通过valueOf会得到想要的原始值:
var num = new Number('123');
num.valueOf(); // 123
var str = new String('12df');
str.valueOf(); // '12df'
var bool = new Boolean('fd');
bool.valueOf(); // true
2、Date是一个特殊的内置对象,Date.protoType上面重写的valueOf方法,将日期转换为日期对应的时间戳
var a = new Date();
a.valueOf(); // 1515143895500
3、其他内置对象,valueOf会返回对象本身
var a = new Array();
a.valueOf() === a; // true
[].valueOf() // []
var b = new Object({});
b.valueOf() === b; // true
({}).valueOf() // {}
再看看toString函数对于js内置对象:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function
1、Number、Boolean、String、Array、Date、RegExp、Function这几种构造函数生成的对象,通过toString方法后,都会变成相应的字符串形式,因为这些构造函数上面都封装了自己的toString方法:
Number.prototype.hasOwnProperty('toString'); // true
Boolean.prototype.hasOwnProperty('toString'); // true
String.prototype.hasOwnProperty('toString'); // true
Array.prototype.hasOwnProperty('toString'); // true
Date.prototype.hasOwnProperty('toString'); // true
RegExp.prototype.hasOwnProperty('toString'); // true
Function.prototype.hasOwnProperty('toString'); // true
var num = new Number('123sd');
num.toString(); // 'NaN'
var str = new String('12df');
str.toString(); // '12df'
var bool = new Boolean('fd');
bool.toString(); // 'true'
var arr = new Array(1,2);
arr.toString(); // '1,2'
var d = new Date();
d.toString(); // "Wed Oct 11 2017 08:00:00 GMT+0800 (中国标准时间)"
var func = function () {}
func.toString(); // "function () {}"
2、除上面之外的构造函数,返回的都是该对象的类型
var obj = new Object({});
obj.toString(); // "[object Object]"
Math.toString(); // "[object Math]"
将值转换为数字类型—-ToNumber()
参数 | 结果 |
---|---|
undefined | NaN |
null | 0 |
布尔值 | true-1,false-0 |
数字 | 不转换 |
字符串 | 有字符串解析为数字,例如:‘324’转换为324,‘qwer’转换为NaN |
对象(obj) | 先进行 ToPrimitive(obj, Number)转换得到原始值,在进行ToNumber转换为数字 |
将值转换为字符串—ToString()
参数 | 结果 |
---|---|
undefined | ‘undefined’ |
null | ‘null’ |
布尔值 | true-‘ture’,false-‘false’ |
数字 | 数字转换字符串,比如:1.765转为’1.765’ |
字符串 | 不转换 |
对象(obj) | 先进行 ToPrimitive(obj, String)转换得到原始值,在进行ToString转换为字符串 |
使用例子:
不指定类型的例子:
({} + {}) = ?
两个对象的值进行+运算符,肯定要先进行隐式转换为原始类型才能进行计算。
1、进行ToPrimitive转换,由于没有指定PreferredType类型,{}会使默认值为Number,进行ToPrimitive(input, Number)运算。
2、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
3、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
故得到最终的结果,"[object Object]" + "[object Object]" = "[object Object][object Object]"
指定类型的例子:
2 * {} = ?
1、首先*运算符只能对number类型进行运算,故第一步就是对{}进行ToNumber类型转换。
2、由于{}是对象类型,故先进行原始类型转换,ToPrimitive(input, Number)运算。
3、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
4、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
5、转换为原始值后再进行ToNumber运算,"[object Object]"就转换为NaN。
故最终的结果为 2 * NaN = NaN
==隐式转化
==规律不那么强,整理了以下流程:
比较运算 x==y, 其中 x 和 y 是值,返回 true 或者 false。这样的比较按如下方式进行:
1、若 Type(x) 与 Type(y) 相同, 则
1* 若 Type(x) 为 Undefined, 返回 true。
2* 若 Type(x) 为 Null, 返回 true。
3* 若 Type(x) 为 Number, 则
(1)、若 x 为 NaN, 返回 false。
(2)、若 y 为 NaN, 返回 false。
(3)、若 x 与 y 为相等数值, 返回 true。
(4)、若 x 为 +0 且 y 为 −0, 返回 true。
(5)、若 x 为 −0 且 y 为 +0, 返回 true。
(6)、返回 false。
4* 若 Type(x) 为 String, 则当 x 和 y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true。 否则, 返回 false。
5* 若 Type(x) 为 Boolean, 当 x 和 y 为同为 true 或者同为 false 时返回 true。 否则, 返回 false。
6* 当 x 和 y 为引用同一对象时返回 true。否则,返回 false。
2、若 x 为 null 且 y 为 undefined, 返回 true。
3、若 x 为 undefined 且 y 为 null, 返回 true。
4、若 Type(x) 为 Number 且 Type(y) 为 String,返回比较 x == ToNumber(y) 的结果。
5、若 Type(x) 为 String 且 Type(y) 为 Number,返回比较 ToNumber(x) == y 的结果。
6、若 Type(x) 为 Boolean, 返回比较 ToNumber(x) == y 的结果。
7、若 Type(y) 为 Boolean, 返回比较 x == ToNumber(y) 的结果。
8、若 Type(x) 为 String 或 Number,且 Type(y) 为 Object,返回比较 x == ToPrimitive(y) 的结果。
9、若 Type(x) 为 Object 且 Type(y) 为 String 或 Number, 返回比较 ToPrimitive(x) == y 的结果。
10、返回 false。
总结:
上面主要分为两类,x、y类型相同时,和类型不相同时。
类型相同时,没有类型转换,主要注意NaN不与任何值相等,包括它自己,即NaN !== NaN。
类型不相同时,
1、x,y 为null、undefined两者中一个 // 返回true
2、x、y为Number和String类型时,则转换为Number类型比较。
3、有Boolean类型时,Boolean转化为Number类型比较。
4、一个Object类型,一个String或Number类型,将Object类型进行原始转换后,按上面流程进行原始值比较。
解析例子:
var a = {
valueOf: function () {
return 1;
},
toString: function () {
return '123'
}
}
true == a // true;
首先,x与y类型不同,x为boolean类型,则进行ToNumber转换为1,为number类型。
接着,x为number,y为object类型,对y进行原始转换,ToPrimitive(a, ?),没有指定转换类型,默认number类型。
而后,ToPrimitive(a, Number)首先调用valueOf方法,返回1,得到原始类型1。
最后 1 == 1, 返回true。
稍微复杂一点的例子:
[] == !{}
//
1、! 运算符优先级高于==,故先进行!运算。
2、!{}运算结果为false,结果变成 [] == false比较。
3、根据上面第7条,等式右边y = ToNumber(false) = 0。结果变成 [] == 0。
4、按照上面第9条,比较变成ToPrimitive([]) == 0。
按照上面规则进行原始值转换,[]会先调用valueOf函数,返回this。
不是原始值,继续调用toString方法,x = [].toString() = ''。
故结果为 '' == 0比较。
5、根据上面第5条,等式左边x = ToNumber('') = 0。
所以结果变为: 0 == 0,返回true,比较结束。
最后一个例子:
const a = {
i: 1,
toString: function () {
return a.i++;
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('hello world!');
}
1、当执行a == 1 && a == 2 && a == 3 时,会从左到右一步一步解析,首先 a == 1,会进行上面第9步转换。ToPrimitive(a, Number) == 1。
2、ToPrimitive(a, Number),按照上面原始类型转换规则,会先调用valueOf方法,a的valueOf方法继承自Object.prototype。返回a本身,而非原始类型,故会调用toString方法。
3、因为toString被重写,所以会调用重写的toString方法,故返回1,注意这里是i++,而不是++i,它会先返回i,在将i+1。故ToPrimitive(a, Number) = 1。也就是1 == 1,此时i = 1 + 1 = 2。
4、执行完a == 1返回true,会执行a == 2,同理,会调用ToPrimitive(a, Number),同上先调用valueOf方法,在调用toString方法,由于第一步,i = 2此时,ToPrimitive(a, Number) = 2, 也就是2 == 2, 此时i = 2 + 1。
5、同上可以推导 a == 3也返回true。故最终结果 a == 1 && a == 2 && a == 3返回true