js在比较运算过程中经常会发生隐式转换,常常会给人意料外的结果,而隐式转换在面试过程中又经常会被考到,所以打算好好整理一下隐式转换这个知识点,本文算是个人对隐式转换的学习梳理。

1.toString和valueOf

toString:toString()函数的作用是返回object的字符串表示

  • Array 返回数组元素的字符串,默认以逗号链接。
  • Boolean 布尔值的字符串值
  • Date 日期UTC标准格式
  • Function 函数的字符串值
  • Number 数字值的字符串值
  • Object [Object Object]
  • String 字符串值
  • Reg 正则的字符串值

如下代码演示(以下演示情况是toString方法没有被重写)

let num = 1
let str = 'a'
let bool = true
let obj = {}
let date = new Date()
let reg = /\d/
let arr = [1, 2, 3]
let fun = function () {
}console.log(num.toString())   // '1'
console.log(str.toString())   // 'a'
console.log(bool.toString())  // 'true'
console.log(obj.toString())   // '[object Object]'
console.log(date.toString())  // 'Thu Mar 28 2019 17:07:40 GMT+0800 (中国标准时间)'
console.log(reg.toString())   // '/\d/'
console.log(arr.toString())   // '1,2,3'
console.log(fun.toString())   // 'function(){}'
复制代码

valueOf:valueOf()函数将对象转换为原始值

  • Array 返回数组对象本身
  • Boolean 布尔值
  • Date 返回时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC
  • Function 函数本身
  • Number 数字值
  • Object 对象本身,这是默认情况。
  • String 字符串值
  • Reg 正则本身

如下代码演示(以下演示情况是valueOf方法没有被重写)

console.log(num.valueOf())   // 1
console.log(str.valueOf())   // 'a'
console.log(bool.valueOf())  // true
console.log(obj.valueOf())   // {}
console.log(date.valueOf())  // 1553766610534
console.log(reg.valueOf())   // /\d/
console.log(arr.valueOf())   // [1, 2, 3]
console.log(fun.valueOf())   // fun()
复制代码

以上铺垫了这么多,是因为在隐式转换过程中经常会有用到调用对象的toString和valueOf方法

2. 隐式转换规则

以下为隐式转换时的规则:

  1. 转化成字符串:使用字符串连接符 +
  2. 转化成数字: 2.1 ++/-- (自加/自减) 2.2 + - * / % (算术运算)2.3 > < >= <= == != === !== (关系运算符)
  3. 转成布尔值:使用!非运算符

2.1 字符串连接符和算法运算符混淆

先看看以下代码:

let a = 1
console.log(a + '1')          // '11'
console.log(a + null)         // 1
console.log(a + undefined)    // NaN (Number(undefined) = NaN)
console.log(a + true)         // 2
console.log(a + {})           // '1[object Object]'
console.log(a + [1, 2, 3])    // '11,2,3'
console.log(a + new Date())   // '1Fri Mar 29 2019 10:12:41 GMT+0800 (中国标准时间)'
console.log(a + /\d/)         // '1/\d/'
console.log(a + function(){}) // '1function(){}'
复制代码

从打印的结果可以知道

  1. 当 + 号为字符串连接符时,则调用对象的toString方法转化为字符串然后相加
  2. 当 + 号为算术运算符时,则调用Number()方法转化然后相加

在这里我们需要注意的是null、布尔值和undefined这三类对象使用 + 进行操作,当有一边确定为数字的时候,这三类值会尝试用Number()进行转化,如果有一边类型确定为字符串的时候,直接就是进行字符串相加

let a = '1'
console.log(a + null)         // '1null'
console.log(a + undefined)    // '1undefined'
console.log(a + true)         // '1true'
复制代码

2.2 关系运算符会把其他数据类型转换成number之后再比较关系

先看看以下代码:

console.log('2' > 10)    // false
console.log('2' > '10')  // true
console.log('a' > 'b')   // false
console.log('ab' > 'aa') // true
复制代码

从打印的结果可以知道

  1. 当关系比较有一边为数字的时候,会把其他数据类型调用Number()转化为数字后进行运算
  2. 当关系比较两边都为字符串的时候,会同时把字符串转化为数字进行比较,但是不是用Number()进行转化,而是按照字符串的unicode编码进行转化(string.charCodeAt,默认为字符的第一位)
console.log('a' > 'b')   // false
// 'a'.charCodeAt() > 'b'.charCodeAt()
console.log('ab' > 'aa') // true
// 第一位都是a相等,所以比较第二位的 b.charCodeAt() > a.charCodeAt()
复制代码

2.3 复杂数据类型在隐式转换时会先转成String,然后再转成Number运算

复杂类型数据指的是对象或数组这类数据进行隐式转换时,会先调用valueOf后调用toString方法转化成数据,再调用Number()转化成数字进行运算。

如果这个对象的valueOf方法和toString方法被重写过,则会根据valueOf返回的数据类型判断是否执行toString。

接下来代码示范:

let a = {valueOf: function () {console.log('执行valueOf')return 'a'},toString: function () {console.log('执行toString')return 'a'}
}
console.log(a == 'a')
// 执行valueOf
// true
复制代码

接下来尝试把valueOf返回值改成数字:

let a = {valueOf: function () {console.log('执行valueOf')return 1},toString: function () {console.log('执行toString')return 'a'}
}
console.log(a == 'a')
// 执行valueOf
// false
复制代码

尝试把valueOf返回值改成对象

let a = {valueOf: function () {console.log('执行valueOf')return {}},toString: function () {console.log('执行toString')return 'a'}
}
console.log(a == 'a')
// 执行valueOf
// 执行toString
// true
复制代码

通过上面的例子我们可以得出结论:

  1. valueOf返回的数据类型决定是否调用toString,如果返回的类型是数字或者字符串(其实用基础数据类型更准确点),toString方法就不执行了。
  2. 转化成字符串后再调用Number()转化成数字进行比较

这里还有个问题就是如果toString方法返回不是基础类型,进行比较的时候则会报错。

2.4 逻辑非隐式转换与关系运算符隐式转换混淆

当使用!逻辑非运算符进行转化的时候,会尝试把数据转化成布尔值

以下情况使用Boolean()转化将会得到false

0、-0、undefined、null、NaN、false、''(空字符串)、document.all

console.log([] == 0)    // true
console.log(![] == 0)   // true
// [] == 0 --> [].valueOf().toString()得到空字符串,Number('') == 0 成立
// ![] == 0 --> Boolean([])得到true再取反,最后转化成数字0,Number(!true) == 0 成立console.log([] == ![])  // true
console.log([] == [])   // false
// [] == ![] --> [].valueOf().toString()得到空字符串,Number('')取得0,Boolean([])得到true再取反,转化成数字0,最后Number('') == Number(!true) 成立
// [] == [] --> 两个数组比较是因为两个数据的引用指向不一致,所以 [] == [] 不成立console.log({} == !{})  // false
console.log({} == {})   // false
// {} == !{} --> {}.valueOf().toString()得到'[object Object]',Boolean({})得到true再取反,所以 '[object Object]' == false 不成立
// {} == {} --> 两个对象比较是因为两个数据的引用指向不一致,所以 {} == {} 不成立
复制代码

最后总结一下,在复杂数据类型隐式转化过程中会调用valueOf和toString方法,所以如果这两个方法被改写了往往会得到一些意料外的结果。

学习valueOf和toString,理解隐式转化规则相关推荐

  1. MySQL隐式转化整理

    MySQL隐式转化整理 原文:http://www.cnblogs.com/rollenholt/p/5442825.html 前几天在微博上看到一篇文章:价值百万的 MySQL 的隐式类型转换感觉写 ...

  2. 【MaxCompute学习】隐式转化的问题

    有一次计算一个数据的百分比,想把小数结果取2位,并拼接一个百分号展示在结果报表中.用到的sql如下 select concat(round(10230/1497409,4)*100,'%') from ...

  3. 类型转换:隐式转化(算数转换,整型提升,混合提升,赋值转换),强制转换【C语言】

    类型转换 隐式转化 算数转换 整型提升 混合提升 赋值转换 强制转换 编译器使用注意 小结 类型转换 隐式转化 不需要人为参与而产生的默认转称为隐式转化. 隐式转化,是计算机语言实现层面最难的,指针是 ...

  4. hadoop cdh5的pig隐式转化(int到betyarray)不行了

    cdh3上,pig支持int到chararray的隐式转化,但到cdh5不行. pig code is as follows: %default Cleaned_Log /user/usergroup ...

  5. javascript深入浅出——学习笔记(六种数据类型和隐式转换)

    在慕课之前学过JS深入浅出,最近发现很多东西都记不太清楚了,再复习一遍好了,感觉这个课程真的超级棒的,做做笔记,再添加一些学习内容?随时补充 课程大纲 1.数据类型 2.表达式和运算符 3.语句 4. ...

  6. 读书笔记 effective c++ Item 41 理解隐式接口和编译期多态

    1. 显示接口和运行时多态 面向对象编程的世界围绕着显式接口和运行时多态.举个例子,考虑下面的类(无意义的类), 1 class Widget { 2 public: 3 Widget(); 4 vi ...

  7. scala学习之旅(十三):隐式转换和隐式参数

    文章地址:http://www.haha174.top/admin/article/list 1.引言 scala 提供的隐式转换和隐式参数功能,是非常有特色的功能.是java 等编程语言所没有的功能 ...

  8. contiki学习笔记(十)隐式网络时间同步、protothreads

    十三.隐式网络时间同步 这个粗糙而简单的网络时间同步模块对网络中所有节点的时钟进行同步. Files file timesynch.cA simple time synchronization mec ...

  9. java this 逸出_java如何理解隐式地使this引用逸出

    最近在看<Java 并发编程实战>,个人的理解: 首先,看里面的 doSomething(e) 方法,这个方法应该是在 ThisEscape 中,不然就无法解释.也就是说,通过 doSom ...

最新文章

  1. windows2008域下exchange2007sp1部署系列一
  2. 实例解说Linux命令行uniq (转)
  3. python删除长目录_python中删除目录名两端
  4. azure云数据库_如何使用Cloud Shell创建Azure SQL数据库
  5. mvc设计模式_MVC设计模式
  6. yum的更多用法和源码编译安装apache
  7. 【hdu1556】Color the ball——树状数组
  8. 知识图谱构建技术综述
  9. Codeforces 760B Frodo and pillows
  10. 外汇买入价、外汇卖出价、现钞买入价有什么区别?
  11. Palm 与 webOS 之死
  12. php webshell探索-常见小马
  13. 关于软件定时器的一些讨论
  14. 计算机上安装的网络协议,怎么安装网络协议
  15. video.js 实现视频只能后退,不能快进
  16. 光纤HDMI线不再脆弱,开博尔铠装HDMI光纤抗拖拽
  17. 计算机技术应用体验,2018教师信息技术应用体验学习个人心得体会2篇
  18. 第二代商用计算机,紫光计算机第二代商用台式机 Unis 526S/526T G2 上市
  19. Java SE基础(更新中)
  20. 计算机软件 csc csu,4-TS-软件设计说明模板(GJB438A).doc

热门文章

  1. python和sass区别_CSS 的预处理程序(Sass、LESS、Stylus 等)分别都有哪些优缺点?...
  2. Objetive-C枚举位移操作Swift枚举位移操作
  3. 解决ubuntu中遇到“E:Unable to locate package rar” 的问题
  4. python实现简单爬虫功能(网站图片)
  5. 使用大脑活动反馈的刺激技术自动化治疗脑部疾病
  6. 这种「基友」给我来一打!
  7. 波兰极客用一张软盘运行Linux系统,用的还是最新内核!
  8. 中国率先发布全球首份车路协同技术白皮书!清华百度联手,突破自动驾驶规模落地瓶颈...
  9. python的random模块生成随机数
  10. PHP http_build_query()方法