[深入06] 隐式转换 和 运算符

Posted woow_wu7

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[深入06] 隐式转换 和 运算符相关的知识,希望对你有一定的参考价值。

导航

[[深入01] 执行上下文](https://juejin.im/post/684490...
[[深入02] 原型链](https://juejin.im/post/684490...
[[深入03] 继承](https://juejin.im/post/684490...
[[深入04] 事件循环](https://juejin.im/post/684490...
[[深入05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...
[[深入06] 隐式转换 和 运算符](https://juejin.im/post/684490...
[[深入07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...
[[深入08] 前端安全](https://juejin.im/post/684490...
[[深入09] 深浅拷贝](https://juejin.im/post/684490...
[[深入10] Debounce Throttle](https://juejin.im/post/684490...
[[深入11] 前端路由](https://juejin.im/post/684490...
[[深入12] 前端模块化](https://juejin.im/post/684490...
[[深入13] 观察者模式 发布订阅模式 双向数据绑定](https://juejin.im/post/684490...
[[深入14] canvas](https://juejin.im/post/684490...
[[深入15] webSocket](https://juejin.im/post/684490...
[[深入16] webpack](https://juejin.im/post/684490...
[[深入17] http 和 https](https://juejin.im/post/684490...
[[深入18] CSS-interview](https://juejin.im/post/684490...
[[深入19] 手写Promise](https://juejin.im/post/684490...
[[深入20] 手写函数](https://juejin.im/post/684490...

[[react] Hooks](https://juejin.im/post/684490...

[[部署01] nginx](https://juejin.im/post/684490...
[[部署02] Docker 部署vue项目](https://juejin.im/post/684490...
[[部署03] gitlab-CI](https://juejin.im/post/684490...

[[源码-webpack01-前置知识] AST抽象语法树](https://juejin.im/post/684490...
[[源码-webpack02-前置知识] Tapable](https://juejin.im/post/684490...
[[源码-webpack03] 手写webpack - compiler简单编译流程](https://juejin.im/post/684490...
[[源码] Redux React-Redux01](https://juejin.im/post/684490...
[[源码] axios ](https://juejin.im/post/684490...
[[源码] vuex ](https://juejin.im/post/684490...

强制转换

  • Number,String,Boolean
  • js是动态类型的语言,变量没有类型限制,可以随意赋予任意值
  • <font color=red>虽然变量的数据类型不确定,但各种(运算符)对变量的(数据类型)是有限制的</font>

Number()强制转换

  • 参数:两种情况,参数是原始类型的值,参数是对象
  • 结果:将任意的数据类型转换成数值,<font color=red>数值</font> 或者 <font color=red>NaN</font>
  • Number的转换要比parseInt严格,只要一个字符不能转换成数值,整个字符串就会被转换成NaN
  • <font color=red>Null => 0</font>
  • <font color=red>undefined => NaN</font>

Number参数:原始类型的值

Number() 参数是原始类型的值

1. 为0的情况
Number(false)
Number(\'\')
Number(Null)

2. 为NaN的情况
Number(undefined)
Number(\'123abc\') // 不能转成数值的字符串

// Number比parseInt严格的多,只要一个字符不能转换成数值,整个字符串将被转换成NaN
// parseInt(\'123abc\') // 123
// Number(\'123abc\') // NaN

3. 为1的情况 
Number(true)

Number参数:对象

  • Number() 的参数类型是对象时,只要不是单个数值的数组,都会转成NaN

    Number() 参数是对象
    
    Number([7]) // 7
    Number([1,2]) // NaN
    Number({a:1}) // NaN

Number具体的转换规则

Number具体的转换规则

1. 调用对象自身的 valueOf 方法
- 如果返回原始类型的值,则直接使用Number函数,不在进行后续步骤
- 如果返回对象,则继续判断

2. valueOf返回对象,则调用对象自身的 toString 方法
- 如果返回原始类型的值,则直接使用Number函数,不在进行后续步骤
- 如果返回对象,则继续判断

3. toString返回对象,则报错


总结: 
valueOf => 对象 => toStirng => 对象 => 报错
以上步骤,如果返回原始类型的值,就调用Number(),并终止后续步骤
// valueOf : 对象的valueOf 返回对象本身
// toString : 对象的toString 返回对象的字符串形式
// valueOf和toString可以自定义

String()强制转换

  • 将任意类型的值转换成字符串
  • 参数:原始类型的值 或 对象
  • 结果:字符串

    String()强制类型转换
  • 参数:原始类型的值
    String(123) // "123"
    String(\'abc\') // "abc"
    String(true) // "true"
    String(undefined) // "undefined"
    String(null) // "null"
  • 参数:对象
  • 参数是对象,返回类型字符串
  • 参数是数组,返回数组的字符串形式
    String({name: \'woow_wu7\'}) // 参数是对象,返回类型字符串 ==> \'[object Object]\'
    String([1,2]) // 参数是数组,返回数组的字符串形式,==> \'1,2\'
  • 具体的转换规则
  • 调用对象的 toStirng 方法
  • 如果返回原始类型的值,则使用String()方法转换
  • 如果返回的是对象,继续以下步骤
  • 调用对象的 valueOf 方法
  • 如果返回原始类型的值,则使用String()方法转换
  • 如果返回对象,继续以下步骤
  • valueOf返回对象,则报错

    总结:
    先调用 toString() -> 对象 -> valueOf() -> 对象 -> 报错
    以上步骤,如果返回原始类型的值,则调用 String(),并中止后续步骤
    // String参数是对象时
    // 参数是对象:返回类型字符串
    // 参数是数字:返回数组的字符串形式

Boolean()强制转换

  • 将任意类型的值转成布尔值

    Boolean类型转换
    
    除了以下6个值是false,其他都为true
    Boolean(+0) // false
    Boolean(-0) // false
    Boolean(\'\') // false
    Boolean(null) // false 
    Boolean(undefined) // false
    Boolean(NaN) // false

自动转换

  • 自动转换发生的时机

    • 不同类型的数据相互运算
    • 对非布尔类型的数据求布尔值
    • 对非数值类型的值使用一元运算符
  • 自动转换的规则

    • 预期什么类型的值,就调用该类型的转换函数

自动转化为字符串

  • 字符串的自动转换,主要发生在字符串的加法运算时
  • 当一个值是字符串,另一个值是非字符串,相加,非字符串转换成字符串

    \'5\' + 1 // \'51\'
    \'5\' + true // "5true"
    \'5\' + false // "5false"
    \'5\' + {} // "5[object Object]" ======> {} => toString => 返回类型字符串 \'[object Object]\'
    \'5\' + [] // "5" =====================> [] => toString => 返回数组的字符串形式 \'\' => \'5\' + \'\'
    \'5\' + function (){} // "5function (){}"
    \'5\' + undefined // "5undefined"
    \'5\' + null // "5null"

    自动转化为数值

  • <font color=red>除了 + 有可能将运算子转换成字符串,其他运算符都会把运算子转换成数值</font>
  • 一元运算符也会把运算子转成数值

    \'5\' - \'2\' // 3
    \'5\' * \'2\' // 10
    true - 1  // 0
    false - 1 // -1
    \'1\' - 1   // 0
    \'5\' * []    // 0 ----------------------- 5 * 0
    false / \'5\' // 0 ----------------------- 0 / 5
    \'abc\' - 1   // NaN --------------------- NaN - 1
    null + 1 // 1 -------------------------- 0 + 1
    undefined + 1 // NaN ------------------- NaN + 1
    
    
    
    一元运算符也会把运算子转成数值
    +\'abc\' // NaN
    -\'abc\' // NaN
    +true // 1
    -false // 0

运算符

加法运算符

  • <font color=red>重载(overload): 加法运算符存在 相加 和 连接,运算子的不同,导致不同的语法行为。</font>
  • 除了加法运算符,其他的运算符都不存在重载,将运算子都转换成数值,再进行数学运算
  • <font color=red>如果运算子是对象,必须先将对象转成原始类型的值,然后再相加</font>

    • true // 2
    • \'a\' // "1a" ------------------- 数值和字符串新加,+存在重载,所以 1 被转成字符串 \'1\' => \'1a\'

    (2) 重载导致运算结果的不同
    \'3\' + 4 + 5 // "345"

    • 4 + \'5\' // "75" ---------- 注意顺序导致结果不一样

    (3) 运算子是对象
    var obj = { p: 1 };
    obj + 2 // "[object Object]2"
    ------- valueOf => 对象本身{ p: 1 } => toString() => \'[object Object]\' + 2 => \'[object Object]2\'
    ------- 对象的valueOf返回对象本身
    ------- 注意:字符串和数值相加,由于+存在重载, 此时 + 表示相连
    ------- 注意:toString()之后,+具有重载性,可能调用Number()也可能调用String()

null和undefined

  • null和undefined与自身严格相等

    undefined === undefined // true
    null === null // true
    
    
    var v1; // undefined
    var v2; // undefined
    v1 === v2 // true
    

相等运算符 ==

  • <font color=red>相等运算符,用来比较相同类型的数据时,与 严格相等运算符 等价</font>
  • 相同类型的数据:== 和 === 等价
  • 不同类型的数据:== 会先进行类型转化,再使用 === 比较

    • 原始类型的数据:转成数值,再比较
    • 对象类型的数据:先将对象转成原始类型的值,再比较
    • null和undefined:相互比较true,和任意其他类型比较false
  • 缺点:存在隐式转化,容易出错

    原始类型的值:先转换成数值,再进行比较
  • == true // true
    // 等同于 1 === Number(true)

    \'true\' == true // false
    // 都转换成数值
    // 等同于 Number(\'true\') === Number(true)
    // 等同于 NaN === 1

    \'1\' == true // true
    // 等同于 Number(\'1\') === Number(true)
    // 等同于 1 === 1

    false == \'false\' // false // Number(false) == Number(\'false\') => 0 === NaN => false
    false == \'0\' // true


    对象类型的值: 先将对象转成原始类型的值,再比较

    [1] == 1 // true
    // 等同于 Number([1]) == 1

    [1] == \'1\' // true
    // 等同于 Number([1]) == Number(\'1\')

    [1] == true // true
    // 等同于 Number([1]) == Number(true)


    null和undefined:相互比较true,和任意其他类型比较false

    false == null // false
    false == undefined // false

  • == null // false
  • == undefined // false

    undefined == null // true

    false == undefined // false
    --------------- undeined和null和任意其他类型==比较,都返回false,相互==返回true
    --------------- 原理:Number(false) == Number(undefined) => 0 === NaN => false
    false == null // false
    null == undefined // true

优先级

  • 最高优先级:属性访问拥有最高的优先级 . ()
  • 最低优先级:赋值运算符 = 拥有最低的优先级
  • 操作符中,一元运算符拥有最高优先级

结合性

  • 优先级相同时,如 乘和除,优先级相同时,考虑结合性
  • <font color=red>结合性分类:左结合 和 右结合</font>
  • <font color=red>右结合:一元操作符,三元操作符,赋值操作符是右结合,其他所有的操作符都是左集合</font>
  • 左结合:其他都是左结合

    运算顺序

  • 从左向右运算

isNaN 和 Number.isNaN

  • isNaN()先将参数转换成number类型,再用isNaN判断
  • Number.isNaN()先判断参数是不是number类型,不是返回false,是再用isNaN就行判断
var n = 1 / \'foo\'
var a = \'str\'

console.log(n) // 1/Number(\'foo\') => 1/NaN => NaN
console.log(n == NaN) // NaN == NaN => false
console.log(NaN === NaN) // false
console.log(isNaN(n)) // true
console.log(isNaN(a)) // isNaN(Number(\'str\')) => isNaN(NaN) => true
console.log(Number.isNaN(n)) // Number.isNaN(NaN) => true
console.log(Number.isNaN(a)) // Number.isNaN()先判断参数是否是Number类型,false返回false, true再用isNaN判断 => 直接false

案列1

[] == ![] 
// true

解析:
1. 优先级: ! > == 所以先算 ![] => false // 除了\'\',+-0,null,undefined,NaN是ture以外,其他都是false
2. [] == false
3. == 相等运算符的运算子有对象时,先将[]转换成原始类型的值,再比较
4. Number([]) == false => valueOf => [] => toString => \'\' => Number(\'\') => 0 == false
5. 0 == false
6. 0 == Number(false) => 0 == 0 类型相同 == 等于 ===   => 0 === 0 => true
7. true

案列2

[] + []
// \'\'

解析:
1. + 的运算子是对象,先转化成原始类型的值,然后再相加
2. [] => valueOf([]) => [] => toString([]) => \'\'
3. \'\' + \'\'
4. 字符串相加,+存在重载,这里表示连接
5. \'\'

以上是关于[深入06] 隐式转换 和 运算符的主要内容,如果未能解决你的问题,请参考以下文章

深入详解函数的柯里化

为啥“as”运算符在 C# 中不使用隐式转换运算符?

深入理解Scala的隐式转换系统

C# 隐式转换和 == 运算符

转载:深入理解Scala的隐式转换系统

用户定义的隐式转换运算符和重载解析