无论是Number(obj)、还是String(obj),如果obj[Symbol.toPrimitive]方法存在的话,都会先调用obj[Symbol.toPrimitive]方法,如果Symbol.toPrimitive方法返回的不是基本类型数据,直接报错!!!如果没有定义该方法才会按照下面的情况走:

  • Number(obj)会优先调用valueOf方法,如果返回的不是基础数据类型,则继续调用toString方法,如果toString方法也返回的不是基础数据类型,则报错
  • String(obj)会优先调用toString()方法,如果返回的不是基础数据类型,则继续调用valueOf方法,如果valueOf方法也返回的不是基础数据类型,则报错。如果没有定义toString方法(即使定义obj.toString = null也算定义了),即使定义了valueOf方法,valueOf方法不会调用,直接返回[object object]。

使用对象(函数,obejct)进行运算或者console.log(fn),会优先调用valueOf()方法,如果valueOf返回的不是基本数据类型(字符串,数字,布尔值),则会调用toString方法,如果toString方法也不返回基本数据类型,则会报错。
.0

  1. var obj = {
  2. toString: function() {
  3. console.log('toString')
  4. return 6
  5. },
  6. valueOf: function() {
  7. console.log('valueOf')
  8. return 5
  9. }
  10. }
  11. /*
  12. * 先调用valueOf,再调用toString方法。
  13. * 如果只显式定义了toString方法也会调用toString方法。
  14. */
  15. console.log(obj + 1) // valueOf 6
  16. console.log(Number(obj)) // valueOf 5
  17. console.log(String(obj)) // toString 6
  18. obj = {
  19. toString: function() {
  20. console.log('toString')
  21. return 6
  22. }
  23. }
  24. console.log(obj + 1) // toString 7 没有定义valueOf,也会调用toString
  25. obj = {
  26. valueOf: function() {
  27. console.log('valueOf')
  28. return 5
  29. }
  30. }
  31. console.log(String(obj)) // [object object] 根本不会调用valueOf方法
  32. //使用对象进行运算,会优先调用valueOf方法
  33. let fn = function(){
  34. }
  35. fn.toString = function(){
  36. return 5
  37. }
  38. fn.valueOf = function(){
  39. return 6
  40. }
  41. console.log(1+fn) // 7 先调用valueOf
  42. console.log(Number(fn)) // 6
  43. console.log(String(fn)) // 5

ToString

  • null:转为”null”

  • undefined:转为”undefined”

  • 布尔类型:true和false分别被转为”true”和”false”

  • 数字类型:转为数字的字符串形式,如10转为”10”, 1e21转为”1e+21”,1e10转为”10000000000”

  • 数组:转为字符串是将所有元素按照”,”连接起来,相当于调用数组的Array.prototype.join()方法,如[1, 2, 3]转为”1,2,3”,空数组[]转为空字符串,数组中的null或undefined,会被当做空字符串处理

  • 普通对象:转为字符串相当于直接使用Object.prototype.toString(),返回”[object Object]“,前提是没有重写默认的toString方法

  1. String(null) // 'null'
  2. String(undefined) // 'undefined'
  3. String(true) // 'true'
  4. String(10) // '10'
  5. String(1e21) // '1e+21'
  6. String([1,2,3]) // '1,2,3'
  7. String([]) // ''
  8. String([null]) // ''
  9. String([1, undefined, 3]) // '1,,3'
  10. String({}) // '[object Objecr]'

ToNumber

  • null: 转为0

  • undefined:转为NaN

  • 字符串:如果是纯数字形式,则转为对应的数字空字符转为0, 否则一律按转换失败处理,转为NaN

  • 布尔型:true false 被转为 1 0

  • 数组:数组首先会被转为原始类型,即ToPrimitive默认先调用valueOf方法,如果返回的不是原始类型,继续调用toString方法。可以这样理解:Number([1]) => Number(‘1’) => 1;Number([1,2]) => Number(‘1,2’) => NaN。空数组返回 0

  • 对象:同数组的处理

  1. Number(null) // 0
  2. Number(undefined) // NaN
  3. Number('10') // 10
  4. Number('10a') // NaN
  5. Number('') // 0 注意这个
  6. Number(true) // 1
  7. Number(false) // 0
  8. Number([]) // 0 注意这个, 先把数组转基本数据类型(先调用valueOf,如果返回的不是基本类型,再调用toString),
  9. Number(['1']) // 1
  10. Number({}) // NaN

ToBoolean

js中的假值只有false、null、undefined、空字符、0和NaN,其它值转为布尔型都为true。

  1. Boolean(null) // false
  2. Boolean(undefined) // false
  3. Boolean('') // flase
  4. Boolean(NaN) // flase
  5. Boolean(0) // flase
  6. Boolean([]) // true
  7. Boolean({}) // true
  8. Boolean(Infinity) // true

ToPrimitive

对象类型类型(如:对象、数组)转换为原始类型的操作。

  • 当对象类型需要被转为原始类型时,会首先调用 [Symbol.toPrimitive] 方法,该方法必须返回原始类型值否则会直接报错。注意这里不会再去调用 valueOf 或 toString 方法。

  • 如果没有 Symbol.toPrimitive 方法,则会先调用该对象的 valueOf 方法,如果 valueOf 方法返回原始类型的值,则 ToPrimitive 的结果就是这个值。

  • 如果 valueOf 不存在或者 valueOf 方法返回的不是原始类型的值,就会尝试调用对象的 toString 方法,也就是会遵循对象的 toString 规则,然后使用toString的返回值作为 ToPrimitive 的结果。如果两个返回都不是原始类型则报错。

注意:对于不同类型的对象来说,ToPrimitive 的规则有所不同,比如 Date 对象会先调用 toString 。其他对象都是默认先转 Number。

JS引擎内部转换为原始值ToPrimitive(obj,preferredType)函数接受两个参数,第一个obj为被转换的对象,第二个preferredType为希望转换成的类型(默认为空,接受的值为Number或String) 在执行ToPrimitive(obj,preferredType)时如果第二个参数为空并且obj为Date的实例时,此时preferredType会被设置为String,其他情况下preferredType都会被设置为Number。

参考:https://www.zhihu.com/question/21484710

  1. let o ={
  2. valueOf(){
  3. return 1
  4. },
  5. toString(){
  6. return 2
  7. },
  8. [Symbol.toPrimitive](){
  9. return 3
  10. }
  11. }
  12. console.log(o+4) // 7

布尔类型跟其他类型==比较

  • 只要布尔类型参与比较,该布尔类型的值首先会被转换为数字类型

  • 根据布尔类型的ToNumber规则,true转为 1false转为 0

  1. false == 0 // true
  2. true == 1 // true
  3. true == 2 // false 注意这个

数字类型 和 字符串类型的相等比较

  • 数字类型 字符串类型做相等比较时,字符串类型会被转换为数字类型

  • 根据字符串的ToNumber规则,如果是纯数字形式的字符串,则转为对应的数字,空字符转为0, 否则一律按转换失败处理,转为NaN

  1. 0 == '' // true
  2. 1 == '1' // true
  3. 1e21 == '1e21' // true
  4. Infinity == 'Infinity' // true
  5. true == '1' // true
  6. false == '0' // true
  7. false == '' // true

对象类型 和 原始类型的相等比较

  • 对象类型 原始类型做相等比较时,对象类型会依照 ToPrimitive 规则转换为原始类型
  1. // 这里其实是先 {} 调用 valueOf 方法返回 {},因不是原始类型,
  2. // 继续调用 toString,返回 '[object Object]' 。所以才会相等
  3. '[object Object]' == {} // true
  4. '1,2,3' == [1, 2, 3] // true
  5. [2] == 2 // true 注意这个

总结:布尔类型、字符串进行 == 运算都会先将布尔类型、字符串转为数字类型再比较。对象则会先转为原始类型再比较。

null 、undefined

null、undefined 两个是 == 的,跟其他的都不 ==。

  1. null == undefined // true
  2. null == 0 // false
  3. undefined == 0 // false
  4. null == '' // false
  5. undefined == '' // false

1、解释下面的这道题,为什么返回true呢???
  1. [] == ![] // true

1、首先右边的 ! 取反会先运算,得到 false,Boolean 比较先转换为数字 Number( false ) 得到 0;
2、左边的[]会先转成基本数据类型,再转数字进行比较。实际的执行过程: Number([]) => Number(‘’) => Number(0) => 0
3 0 == 0 所以返回 true

2、实现 a==1 && a==2 && a==3
  1. const a = {
  2. num: 0,
  3. valueOf: function() {
  4. return this.num += 1
  5. }
  6. };
  7. const equality = (a==1 && a==2 && a==3);
  8. console.log(equality); // true

3、
  1. var o = {}
  2. console.log( o + '' )
  3. console.log( {} + '' )
  4. o + ''
  5. {} + ''

上面的两个的输出是什么呢?

答案是: 前三个都会输出 “[object Object]” ,因为对象进行字符串计算会先调用 toString 方法,最后一个 {} + ‘’,前面的 {} 会被编译成一个块级作用域,而不是理解的对象。所以 {} + ‘’ => +’ ‘ => 0
**

关于 + 运算符

规则:

  • 只要有一个是 string ,则另一个值也转为 string,再相加。
  • 否则都转为 number,在相加,如果转为 number 有 NaN ,则结果也是 NaN.
  1. true + 1 // 1,布尔型转化为了数字
  2. // 空数组转化为了字符串:数组先调用 valueOf() 方法,返回的是 [],继续调用 toString 方法,才返回 ''
  3. [] + 1 // '1'
  4. {} + 1 // 1,空对象被当成了空代码块,相当于 + 1
  5. var now = new Date()
  6. typeof (now + 1) // 'string',日期对象转化为了字符串
  7. 1 + undefined // NaN
  8. 1 + {} // '1[ojbect Object]'
  9. true + true // 2
  10. true + {} // 'true[object Object]'
  11. +'' // 0

JS中进行 A+B 这种操作时会经历这样一个过程:

  1. 将A和B都转换为原始值(primitive,执行ToPrimitive),这里记为A1,B1
  2. 如果A1和B1中有一个值为string,则将A1、B1都转换为 string(执行ToString),其值记为A2、B2,将A2 B2连接后就是A+B的结果
  3. 否则的话将A1、B1都转换为 number(执行ToNumber),其值记为A3、B3,将A3 B3相加即为A+B的结果

ToPrimitive(obj,preferredType)
**
JS引擎内部转换为原始值ToPrimitive(obj,preferredType)函数接受两个参数,第一个obj为被转换的对象,第二个preferredType为希望转换成的类型(默认为空,接受的值为Number或String)

在执行ToPrimitive(obj,preferredType)时如果第二个参数为空并且obj为Date的实例时,此时preferredType会被设置为String,其他情况下preferredType都会被设置为Number。

如果preferredType为Number,ToPrimitive执行过程如下:

  1. 如果obj为原始值,直接返回;
  2. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
  3. 否则调用obj.toString(),如果执行结果是原始值,返回之;
  4. 否则抛异常。

如果preferredType为String,将上面的第2步和第3步调换,即:

  1. 如果obj为原始值,直接返回;
  2. 否则调用obj.toString(),如果执行结果是原始值,返回之;
  3. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
  4. 否则抛异常。

**

从 []==![] 为 true 来剖析 JavaScript 各种蛋疼的类型转换:https://github.com/jawil/blog/issues/1

一道面试题引发的对 javascript 类型转换的思考:https://www.cnblogs.com/coco1s/p/6509141.html

从一道面试题说起—js隐式转换踩坑合集:https://juejin.im/post/5bc5c752f265da0a9a399a62