【JavaScript】一文搞懂JavaScript当中晦涩难懂的类型转换

本文主要是介绍【JavaScript】一文搞懂JavaScript当中晦涩难懂的类型转换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章参考:
What the f*ck JavaScript?
再谈:JavaScript 中的对象是如何进行类型转换的?

一、类型转换在啥情况下会发生

1、类型转换的种类
  • 隐式类型转换
  • 显示类型转换
2、何时发生隐式类型转换?

通常发生在比较运算(==!=><)、算术运算(+-*/%)和if语句,而且运算符两边的操作数不是同一类型。

注: 相等运算符 == 中有特殊情况,null == undefined 的比较结果返回 true。

3、何时发生显示类型转换?

NumberStringBoolean这都是显示转化

二、基础数据类型转换规则

1、转Number

在使用 Number() toNumber() 方法将一个字符串转换为数字时,如果字符串中出现非数字字符,则会返回 NaN

2、转String

基础数据类型均会转为字符串

3、转Boolean

只有以下几种情况会转为false

  • undefined
  • null
  • +0、-0和NaN
  • “”
  • “false”

三、引用数据类型转换规则

1、对象属性: [Symbol.toPrimitive]

toPrimitive(primitive:原始的),作用:转为基础数据类型。

对象发生类型转换时,首先会检查对象上是否存在 [Symbol.toPrimitive] 属性,如果存在的话就调用。该属性存在一些限制:它必须是一个函数,而且返回值必须是基本类型,否则就要报错

例如:

1). 不是函数
在这里插入图片描述

2). 函数返回值不是基本类型
在这里插入图片描述

hint 值

[Symbol.toPrimitive] 在调用时,系统会自动给与一个参数 hint,这个 hint 可以理解为,此次对象发生转换的预期类型为何。

hint 有三种可能取值:stringnumberdefault

下面我们来演示下,这三种情况的发生场景。
【运行结果】
在这里插入图片描述
【代码片段】

var obj = {[Symbol.toPrimitive](hint) {console.log('[Symbol.toPrimitive]', hint)}
}
undefined
// 第一种情况(期望类型是 number):
+obj
// [Symbol.toPrimitive] number
// NaN
// 第二种情况(期望类型是 string):
String(obj)
// [Symbol.toPrimitive] string
// "undefined"
// 第三种情况(未知):
obj + ':('
// [Symbol.toPrimitive] default
// "undefined:("
obj + 1
// [Symbol.toPrimitive] default
// NaN

【结论解释】
[Symbol.toPrimitive] 方法里我们没有定义返回值,因此方法返回值是默认的 undefined

第一种情况:+obj -> +undefined -> Number(undefined) -> NaN。
第二种情况:String(obj) -> String(undefined) -> “undefined”。
第三种情况:

  • obj + ‘: (’ -> undefined + ‘: (’ -> “undefined: (”。
  • obj + 1 -> undefined + 1 -> NaN。

然而当对象不存在 [Symbol.toPrimitive] 的时候,转换规则又是怎样的呢?
这就关系到 Object.prototype 对象上的两个方法了:valueOf 和 toString

2、Object.prototype.valueOf / Object.prototype.toString

当对象上不存在 [Symbol.toPrimitive] 属性的时候,若发生类型转换,就要用到 Object.prototype 对象上的 valueOf toString 两个方法了。

两个方法调用有先后,可能都会调用,也可以只调用一个就完成转换,返回结果。这跟方法返回值和hint值有关系。

具体是:

  • 如果 hint 值为 ‘default’,则与 hint 为 ‘number’ 时一样对待。
  • 如果 hint 值为 ‘number’,则
    • 先调用对象的 valueOf 方法,如果方法返回的是一个基本类型值,则对象的转换结果就是这个返回值;
    • 否则,接着调用对象的 toString 方法。
  • 如果 hint 值为 ‘string’,则先调用对象的 toString 方法,
    • 如果方法返回的是一个基本类型值,则对象的转换结果就是这个返回值;
    • 否则,接着调用对象的 valueOf 方法。

你可能要问了,如果两个方法的返回值都是对象的话,岂不是得不到对象最终的转换结果了?一点都没错,我们来试一下:

在这里插入图片描述
上图里,我们在对象 obj 上定义了 valueOf 和 toString 方法,在发生类型转换时,覆盖掉原型对象上的同名方法,以便我们能更加真切地感受到对象内部的实际的转换流程。

我们制造了一个极端情况,两个方法都没有返回基本类型只而是对象,结果呢?然后控制台就报错了,告诉我们不要这样玩。

需要说明的是——在 JavaScript 中,当我们在一个对象上调用 valueOf 方法的时候,实际上调用的是 Object.prototype.valueOf 这个原型方法。默认这个方法的返回值始终是调用对象自身,也就是说 valueOf 方法的返回的始终是对象,而非一个基本类型值。

注:
1、Date 对象除外,因为 Date.prototype 上定义的 valueOf 方法覆盖掉了 Object.protortype 上的。在 Date 对象上调用 valueOf方法,返回的是时间对象内部的时间戳表示。
2、另外,Date.prototype 也定义了自己的 [Symbol.toPrimitive] 属性,默认是不可写入的。
在这里插入图片描述

这就得到了一个结论:对象发生到基本类型转换时,最终转换结果就是 obj.toString() 返回值!这就解释了下面代码里的输出结果:

var obj = {}
// (1)
obj + ' :)' // "[object Object] :)"
// (2)
obj - 1 // NaN

【结果分析】
obj + ’ : )’ -> ‘[object Object]’ + : )’ -> ‘[object Object] : )’
obj - 1 -> ‘[object Object]’ - 1 -> Number(‘[object Object]’) - 1 -> NaN - 1 -> NaN

3、数组的转换

数组本质也是对象,因此上面的转换规则也适应于它。不同的是数组原型对象上定义了自己的实现方法:Array.prototype.toString,这个方法覆盖掉了 Object.prototype 上的同名方法,toString会以去拼接显示。

在这里插入图片描述
重写之后的 toString 方法的逻辑(类似)如下:

Array.prototype.toString = function () { return this.join() }

因为并未覆盖 valueOf 方法(也是返回对象本身,对应到这里就是数组实例),因此数组转换结果就是调用 array.join() 的返回结果。

下面举几个例子:

var emptyArray = []
var array1 = [123]
var array2 = ['hi', 'How are you']// (1)
Number(emptyArray) // 0
// (2)
array1 - 100 // 23
// (3)
array2 + '?' // "hi,How are you?"

对应的转换过程如下:

  • emptyArray 首先会转换成空字符 ‘’,Numbe(‘’) 的结果就是 0,
  • array1 会转换成字符串 ‘123’,‘123’ - 100 相当于 Number(‘123’) - 100,也就是 23,
  • array2 会转换成字符串 ‘hi,How are you’,‘hi,How are you’ + ‘?’ 的结果自然是 ‘hi,How are you?’。
4、总结:对象转换算法步骤

总结下来,一个对象转换到基本类型的算法步骤如下:

  • 首先,检查对象上是否有 [Symbol.toPrimitive] 属性:
    • 有的话,调用此方法(属性),此方法的返回值即对象最终的转换值。
    • 没有的话,进入第二步。
  • 检查当前转换的 hint 值:
    • 如果为 ‘default’ / ‘number’:
      • 先调用对象的 valueOf 方法,如果方法返回的是一个基本类型值,则对象的转换结果就是这个返回值。
      • 否则,接着调用对象的 toString 方法。
    • 如果为 ‘string’:
      • 先调用对象的 toString 方法,如果方法返回的是一个基本类型值,则对象的转换结果就是这个返回值。
      • 否则,接着调用对象的 valueOf 方法。

到这里的话,对象类型转换的内容就讲完了,希望能帮助到大家!

这篇关于【JavaScript】一文搞懂JavaScript当中晦涩难懂的类型转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/197440

相关文章

Nginx实现前端灰度发布

《Nginx实现前端灰度发布》灰度发布是一种重要的策略,它允许我们在不影响所有用户的情况下,逐步推出新功能或更新,通过灰度发布,我们可以测试新版本的稳定性和性能,下面就来介绍一下前端灰度发布的使用,感... 目录前言一、基于权重的流量分配二、基于 Cookie 的分流三、基于请求头的分流四、基于请求参数的分

一文详解kafka开启kerberos认证的完整步骤

《一文详解kafka开启kerberos认证的完整步骤》这篇文章主要为大家详细介绍了kafka开启kerberos认证的完整步骤,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、kerberos安装部署二、准备机器三、Kerberos Server 安装1、配置krb5.con

基于Canvas的Html5多时区动态时钟实战代码

《基于Canvas的Html5多时区动态时钟实战代码》:本文主要介绍了如何使用Canvas在HTML5上实现一个多时区动态时钟的web展示,通过Canvas的API,可以绘制出6个不同城市的时钟,并且这些时钟可以动态转动,每个时钟上都会标注出对应的24小时制时间,详细内容请阅读本文,希望能对你有所帮助...

HTML5 data-*自定义数据属性的示例代码

《HTML5data-*自定义数据属性的示例代码》HTML5的自定义数据属性(data-*)提供了一种标准化的方法在HTML元素上存储额外信息,可以通过JavaScript访问、修改和在CSS中使用... 目录引言基本概念使用自定义数据属性1. 在 html 中定义2. 通过 JavaScript 访问3.

CSS模拟 html 的 title 属性(鼠标悬浮显示提示文字效果)

《CSS模拟html的title属性(鼠标悬浮显示提示文字效果)》:本文主要介绍了如何使用CSS模拟HTML的title属性,通过鼠标悬浮显示提示文字效果,通过设置`.tipBox`和`.tipBox.tipContent`的样式,实现了提示内容的隐藏和显示,详细内容请阅读本文,希望能对你有所帮助... 效

一文详解Nginx的强缓存和协商缓存

《一文详解Nginx的强缓存和协商缓存》这篇文章主要为大家详细介绍了Nginx中强缓存和协商缓存的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、强缓存(Strong Cache)1. 定义2. 响应头3. Nginx 配置示例4. 行为5. 适用场景二、协商缓存(协

前端bug调试的方法技巧及常见错误

《前端bug调试的方法技巧及常见错误》:本文主要介绍编程中常见的报错和Bug,以及调试的重要性,调试的基本流程是通过缩小范围来定位问题,并给出了推测法、删除代码法、console调试和debugg... 目录调试基本流程调试方法排查bug的两大技巧如何看控制台报错前端常见错误取值调用报错资源引入错误解析错误

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

一文详解如何在Python中使用Requests库

《一文详解如何在Python中使用Requests库》:本文主要介绍如何在Python中使用Requests库的相关资料,Requests库是Python中常用的第三方库,用于简化HTTP请求的发... 目录前言1. 安装Requests库2. 发起GET请求3. 发送带有查询参数的GET请求4. 发起PO