js数据类型

基本数据类型(原始值):

  • undefined
  • Null
  • Boolean
  • String
  • Number
  • Symbol(ES6新增)
  • BigInt(ES10新增)

复杂数据类型(对象值):

  • Object

    三种隐式转换

    将值转换为原始值

    js内部引擎有这样的操作:
    ToPrimitive(input, PreferredType?)
    input表示要转换的值,PreferredType表示转换标志,可选值(可以是Number、String类型
    转换后的结果不一定就是PreferredType代表的类型值,但是一定是一个原始值或者报错,因为转换成类型值有如下操作流程:
    PreferredType为Number:
    1. 1、如果输入的值已经是一个原始值,则直接返回它
    2. 2、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
    3. 如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
    4. 3、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
    5. 4、否则,抛出TypeError异常。
    PreferredType为String:
    1. 1、如果输入的值已经是一个原始值,则直接返回它
    2. 2、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
    3. 3、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
    4. 如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
    5. 4、否则,抛出TypeError异常。
    如果ToPrimitive的PreferredType没有选择,则有一个默认的值:
    1. 1、该对象为Date类型,则PreferredType被设置为String
    2. 2、否则,PreferredType被设置为Number
    valueOf和toString方法

这两个方法在对象中是一定存在的,Object.protoType里面一定有这两种方法
先看看对象的valueOf方法,对于js常见的内置对象:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function

1、Number、Boolean、String这三种构造函数生成的基础值对象形式,通过valueOf会得到想要的原始值:

  1. var num = new Number('123');
  2. num.valueOf(); // 123
  3. var str = new String('12df');
  4. str.valueOf(); // '12df'
  5. var bool = new Boolean('fd');
  6. bool.valueOf(); // true

2、Date是一个特殊的内置对象,Date.protoType上面重写的valueOf方法,将日期转换为日期对应的时间戳

  1. var a = new Date();
  2. a.valueOf(); // 1515143895500

3、其他内置对象,valueOf会返回对象本身

  1. var a = new Array();
  2. a.valueOf() === a; // true
  3. [].valueOf() // []
  4. var b = new Object({});
  5. b.valueOf() === b; // true
  6. ({}).valueOf() // {}

再看看toString函数对于js内置对象:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function
1、Number、Boolean、String、Array、Date、RegExp、Function这几种构造函数生成的对象,通过toString方法后,都会变成相应的字符串形式,因为这些构造函数上面都封装了自己的toString方法:

  1. Number.prototype.hasOwnProperty('toString'); // true
  2. Boolean.prototype.hasOwnProperty('toString'); // true
  3. String.prototype.hasOwnProperty('toString'); // true
  4. Array.prototype.hasOwnProperty('toString'); // true
  5. Date.prototype.hasOwnProperty('toString'); // true
  6. RegExp.prototype.hasOwnProperty('toString'); // true
  7. Function.prototype.hasOwnProperty('toString'); // true
  8. var num = new Number('123sd');
  9. num.toString(); // 'NaN'
  10. var str = new String('12df');
  11. str.toString(); // '12df'
  12. var bool = new Boolean('fd');
  13. bool.toString(); // 'true'
  14. var arr = new Array(1,2);
  15. arr.toString(); // '1,2'
  16. var d = new Date();
  17. d.toString(); // "Wed Oct 11 2017 08:00:00 GMT+0800 (中国标准时间)"
  18. var func = function () {}
  19. func.toString(); // "function () {}"

2、除上面之外的构造函数,返回的都是该对象的类型

  1. var obj = new Object({});
  2. obj.toString(); // "[object Object]"
  3. 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. ({} + {}) = ?
  2. 两个对象的值进行+运算符,肯定要先进行隐式转换为原始类型才能进行计算。
  3. 1、进行ToPrimitive转换,由于没有指定PreferredType类型,{}会使默认值为Number,进行ToPrimitive(input, Number)运算。
  4. 2、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
  5. 3、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
  6. 故得到最终的结果,"[object Object]" + "[object Object]" = "[object Object][object Object]"

指定类型的例子:

  1. 2 * {} = ?
  2. 1、首先*运算符只能对number类型进行运算,故第一步就是对{}进行ToNumber类型转换。
  3. 2、由于{}是对象类型,故先进行原始类型转换,ToPrimitive(input, Number)运算。
  4. 3、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
  5. 4、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
  6. 5、转换为原始值后再进行ToNumber运算,"[object Object]"就转换为NaN
  7. 故最终的结果为 2 * NaN = NaN

==隐式转化

==规律不那么强,整理了以下流程:

  1. 比较运算 x==y, 其中 x y 是值,返回 true 或者 false。这样的比较按如下方式进行:
  2. 1、若 Type(x) Type(y) 相同,
  3. 1* Type(x) Undefined 返回 true
  4. 2* Type(x) Null 返回 true
  5. 3* Type(x) Number
  6. (1)、若 x NaN 返回 false
  7. (2)、若 y NaN 返回 false
  8. (3)、若 x y 为相等数值, 返回 true
  9. (4)、若 x +0 y 0 返回 true
  10. (5)、若 x 0 y +0 返回 true
  11. (6)、返回 false
  12. 4* Type(x) String, 则当 x y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true 否则, 返回 false
  13. 5* Type(x) Boolean, x y 为同为 true 或者同为 false 时返回 true 否则, 返回 false
  14. 6* x y 为引用同一对象时返回 true。否则,返回 false
  15. 2、若 x null y undefined 返回 true
  16. 3、若 x undefined y null 返回 true
  17. 4、若 Type(x) Number Type(y) String,返回比较 x == ToNumber(y) 的结果。
  18. 5、若 Type(x) String Type(y) Number,返回比较 ToNumber(x) == y 的结果。
  19. 6、若 Type(x) Boolean 返回比较 ToNumber(x) == y 的结果。
  20. 7、若 Type(y) Boolean 返回比较 x == ToNumber(y) 的结果。
  21. 8、若 Type(x) String Number,且 Type(y) Object,返回比较 x == ToPrimitive(y) 的结果。
  22. 9、若 Type(x) Object Type(y) String Number 返回比较 ToPrimitive(x) == y 的结果。
  23. 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类型进行原始转换后,按上面流程进行原始值比较。
解析例子:

  1. var a = {
  2. valueOf: function () {
  3. return 1;
  4. },
  5. toString: function () {
  6. return '123'
  7. }
  8. }
  9. true == a // true;
  10. 首先,xy类型不同,xboolean类型,则进行ToNumber转换为1,为number类型。
  11. 接着,xnumberyobject类型,对y进行原始转换,ToPrimitive(a, ?),没有指定转换类型,默认number类型。
  12. 而后,ToPrimitive(a, Number)首先调用valueOf方法,返回1,得到原始类型1
  13. 最后 1 == 1 返回true

稍微复杂一点的例子:

  1. [] == !{}
  2. //
  3. 1、! 运算符优先级高于==,故先进行!运算。
  4. 2、!{}运算结果为false,结果变成 [] == false比较。
  5. 3、根据上面第7条,等式右边y = ToNumber(false) = 0。结果变成 [] == 0
  6. 4、按照上面第9条,比较变成ToPrimitive([]) == 0
  7. 按照上面规则进行原始值转换,[]会先调用valueOf函数,返回this
  8. 不是原始值,继续调用toString方法,x = [].toString() = ''
  9. 故结果为 '' == 0比较。
  10. 5、根据上面第5条,等式左边x = ToNumber('') = 0
  11. 所以结果变为: 0 == 0,返回true,比较结束。

最后一个例子:

  1. const a = {
  2. i: 1,
  3. toString: function () {
  4. return a.i++;
  5. }
  6. }
  7. if (a == 1 && a == 2 && a == 3) {
  8. console.log('hello world!');
  9. }
  10. 1、当执行a == 1 && a == 2 && a == 3 时,会从左到右一步一步解析,首先 a == 1,会进行上面第9步转换。ToPrimitive(a Number) == 1
  11. 2ToPrimitive(a, Number),按照上面原始类型转换规则,会先调用valueOf方法,avalueOf方法继承自Object.prototype。返回a本身,而非原始类型,故会调用toString方法。
  12. 3、因为toString被重写,所以会调用重写的toString方法,故返回1,注意这里是i++,而不是++i,它会先返回i,在将i+1。故ToPrimitive(a, Number) = 1。也就是1 == 1,此时i = 1 + 1 = 2
  13. 4、执行完a == 1返回true,会执行a == 2,同理,会调用ToPrimitive(a, Number),同上先调用valueOf方法,在调用toString方法,由于第一步,i = 2此时,ToPrimitive(a, Number) = 2 也就是2 == 2, 此时i = 2 + 1
  14. 5、同上可以推导 a == 3也返回true。故最终结果 a == 1 && a == 2 && a == 3返回true