Javascript 里的类型转换规则

更新日期: 2022-05-29 阅读: 1.5k 标签: 类型

Javascript 里的类型转换是一个你永远绕不开的话题,不管你是在面试中还是工作写代码,总会碰到这类问题和各种的坑,所以不学好这个那是不行滴。关于类型转换我也看过不少的书和各种博客、帖子,也查过规范和做过各种测试,这里就谈谈我的总结和理解吧。

首先,为了掌握好类型转换,我们要理解一个重要的抽象操作:ToPrimitive。


ToPrimitive

为什么说这是个抽象操作呢?因为这是 Javascript 内部才会使用的操作,我们不会显式调用到。

对于 Undefined、Null、Boolean、Number 和 String 这些基础类型会返回原值不会转换;当需要将对象转换为相应的基本类型值时,ToPrimitive 就会调用对象的内部方法 [[DefaultValue]] 来完成。

ToPrimitive 操作接收两个参数,一个是 input 需要转换的值,第二个是可选参数 hint 代表期望的转换类型。并且在调用 [[DefaultValue]] 的时候 hint 会传递过去。

这里我们首先只需要知道 [[DefaultValue]] 会调用 valueOf() 和 toString() 来完成基本类型值的转换,如果任何一个调用返回了基础值则调用就结束了,没有返回基础值就接着调用另一个。但是注意:valueOf() 和 toString() 的调用逻辑顺序并不是固定的取决于 hint 参数,规则如下:

  • 如果 hint 参数是 String,那么先调用 toString() 再调用 valueOf()。
  • 如果 hint 参数是 Number 或者没有值(ES6+ 有默认值 'default'),则先调用 valueOf() 再调用 toString();但是这里有个例外是 Date 对象,如果是 Date 对象则 hint 当做 String 处理。
  • 如果 valueOf() 和 toString() 都没有返回基础值,则抛出异常:TypeError: Cannot convert object to primitive value。

更新内容:

  1. 对象的 Symbol.toPrimitive 属性,会影响 ToPrimitive。如果对象定义了这个方法,则转换的基础值就是这个方法的返回值,如果这个方法没有返回初始值,则后续也不会再调用 toString() 和 valueOf() 了,会直接抛出异常:TypeError: Cannot convert object to primitive value。 Symbol.toPrimitive 属性的用法请看 这里


基本规则

JavaScript 中的类型转换总是返回基本类型值,如字符串、数字和布尔值,不会返回对象和函数。那么这也对应了三种抽象操作:ToString、ToNumber 和 ToBoolean,下面就来逐一说明。


ToString

var a = {};
console.log(String(a));
// 显式类型转换,输出为:"[object Object]"

以上代码我们通常称为显式类型转换,这里面就包含 ToString 抽象操作,也就是把非字符串值转换为字符串的操作。

先来看看非对象的基本类型值的 ToString 转换规则:

输入类型输出结果
Undefined"undefined"
Null"null"
Boolean输入 true,输出 "true"
输入 false,输出 "false"
Number输入 NaN,输出 "NaN"
输入 +0 或 -0,输出 "0"
如果输入小于 0 的数字,如:-2,输出将包括负号:"-2"
输入 Infinity,输出 "Infinity"

接着我们重点来看一下输入是对象的转换规则。

这个时候 ToPrimitive 就出场了,并且 hint 参数是 String。还记得 ToPrimitive 内部是调用的[[DefaultValue]]吗,**并且这个时候 hint 是 String **。下面来看下这种情况下 ToPrimitive 的调用逻辑:

  1. toPrimitive 调用 [[DefaultValue]] 并传递 hint,然后返回调用结果

  2. [[DefaultValue]] 根据 hint 是 String 执行以下调用顺序:

    • 如果对象存在 toString() 并返回一个基本类型值,即返回这个值

    • 如果 toString() 不存在或返回的不是一个基本类型值,就调用 valueOf()

    • 如果 valueOf() 存在并返回一个基本类型值,即返回这个值

    • 如果 valueOf() 不存在或返回的不是一个基本类型值,则抛出 TypeError 异常

那么这里就可以总结为:对象在类型转换为字符串时, toString() 的调用顺序在 valueOf() 之前,并且这两个方法如果都没有返回一个基本类型值,则抛出异常;如果返回了基本类型值 primValue,则返回 String(primValue)

基本类型值的 ToString 结果参看前面那个表格。

我们来测试一下。先看下这节开头的例子:

var a = {};
console.log(String(a));

字面量对象的原型是 Object.prototype,Object.prototype.toString() 返回内部属性 [[Class]] 的值,那么结果就是 [object Object]。然后测试一下 ToPrimitive 的调用逻辑。来看下这段代码:

var a = Object.create(null);

上面的意思是创建一个没有原型的对象(没有原型就没有继承的 toString() 和 valueOf() 了)。接下来:

console.log(String(a));
// Uncaught TypeError: Cannot convert object to primitive value

这里因为没有 toString() 和 valueOf() 所以就抛出 TypeError 异常了。OK,跟前面的总结一致。

下面来测试一下 toString() 和 valueOf() 的调用顺序逻辑,上代码:

a.toString = function () {
  console.log('toString');
  return 'hello';
};

a.valueOf = function () {
  return true;
};

console.log(String(a)); // 先输出 'toString',再输出 'hello'

跟前面的总结一致,确实是 toString() 先返回结果。接着做一下变化:

a.toString = function () {
  return {};
};
// 或是直接去掉这个方法,a.toString = undefined;

a.valueOf = function () {
  return true;
};

console.log(String(a)); // 'true'

当 toString() 返回的不是一个基本类型值或不存在 toString() 时,返回 valueOf() 的结果,并且遵循基本类型值的 ToString 转换结果。OK,验证没有问题 ✌️ 其他的情况也可以根据前面的总结逻辑自己验证下。

在《Javascript 高级程序设计(第 4 版)》和《你不知道的 Javascript(中卷)》上均未提到类型转换到字符串会与 valueOf() 有关系。


ToNumber

首先照例先来看下非对象的基本类型值的 ToNumber 转换规则:

输入类型输出结果
UndefinedNaN
Null0
Boolean输入 true,输出 1
输入 false,输出 0
String输入 '',输出 0
输入 'Infinity',输出 Infinity
输入有效数字的字符串(包括二、八和十六进制),输出数字的十进制数值
如果输入包含非数字格式的字符串,输出 NaN

字符串转数字上面只说了一些常用的情况,更多细节请看 这里

然后来看看对象 ToNumber 的情况。这里与对象转字符串的情况类似,也会调用 ToPrimitive 来转换(hint 是 Number)。但细节与 ToString 稍有不同,这里直接给出结论:

对象在类型转换为数字时, valueOf() 的调用顺序在 toString() 之前,并且这两个方法如果都没有返回一个基本类型值,则抛出异常;如果返回了基本类型值 primValue,则返回 Number(primValue)

这里验证了 ToPrimitive 里面说到的,[[DefaultValue]] 会根据 hint 参数决定 toString() 和 valueOf() 的调用顺序。

接着来用代码说话:

var a = Object.create(null);
console.log(Number(a));
// Uncaught TypeError: Cannot convert object to primitive value

这里因为没有 toString() 和 valueOf() 所以就抛出 TypeError 异常了。OK,跟前面的总结一致。

我们先加入 valueOf() 方法:

a.valueOf = function () {
  return 123;
}
console.log(Number(a)); // 123

valueOf() 返回了数字 123,所以输出没问题。再修改一下:

a.valueOf = function () {
  return true;
}
console.log(Number(a)); // 1

valueOf() 返回了 true,这也是一个基本类型,然后根据基本类型转换规则 true 转换为 1,也是对的。

再来:

a.valueOf = function () {
  return NaN;
}
console.log(Number(a)); // NaN

NaN 是一个特殊的数值,所以也是基本类型。OK,也是对的。

这里的结果说明了《Javascript 高级程序设计(第 4 版)》关于对象转换为数字的解释是有错误的,书上是这么说的:如果转换的结果是 NaN,则调用对象的 toString() 方法。

再来验证一下 toString() 的调用顺序:

a.valueOf = function () {
  console.log('valueOf');
  return {};
}
a.toString = function () {
  return '123';
}
console.log(Number(a)); // 先输出 'valueOf',再输出 123

因为 valueOf() 返回了对象非基本类型值,转而执行 toString(),返回的 '123' 根据字符串转换数字的规则就是 123,对于 valueOf() 和 toString() 的执行顺序验证也是 OK 的。


ToBoolean

最后我们来看看转换为布尔值。这个比较简单,一个列表可以全部归纳了:

输入类型输出结果
Undefinedfalse
Nullfalse
Number输入 +0,-0,NaN,输出 false
输入其他数字,输出 true
String输入 length 为 0 的字符串(如:''),输出 false
输入其他字符串,输出 true
Object输入任何对象类型,输出 true

ToBoolean 转换规则比较简单,只有一个需要注意的地方,那就是封箱操作:

var a = new Boolean(false);
console.log(Boolean(a));
// 输出是 true 不是 false 喔

new Boolean(false) 返回的是对象不是布尔值,所以最好避免进行类似的操作。


总结

以上即是我总结的 JS 类型转换基本规则,当你明显感知类型转换即将发生时可以拿上面的规则去套(也就是我们通常说的显式类型转换,以上转换规则面试时特别有用喔)。

既然规则有了,下一篇准备聊一下隐式类型转换,有了这篇的基础掌握隐式转换会容易很多。

欢迎 star 和关注我的 JS 博客:小声比比 JavaScript

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/11621

相关推荐

Js如何将String转化为Int

字符串到整型的一个转换,在面试过程中或者在工作中都会频繁遇到,那么string到js怎么转换呢 ?parseInt方法在format\'00\'开头的数字时会当作2进制转10进制的方法进行转换

Typescript内置类型与自定义类型

大家用过 Typescript 都清楚,很多时候我们需要提前声明一个类型,再将类型赋予变量。例如在业务中,我们需要渲染一个表格,往往需要定义:

Js实现base64,file和blob相互转换

JavaScript实现base64,file和blob相互转换:base64转为Blob;Blob转为base64;base64转换为file;js图片转换为base64;在Java中base64和File相互转换

Js的6种基本数据类型

在JS中一共有六种数据类型 String:字符串 Number:数值 Boolean:布尔值 Null:空值 Undefined:未定义 Object:对象 ,其中String,Number,Boolean,Null,Undefined属于基本数据类型而Object属于引用数据类型

JavaScript判断字符串是否为数字类型

JavaScript中有Number.isInteger可以判断一个字符串是否为整数。不过目前JS没有内置的函数来判断一个数字是否为包含小数的数字:

TypeScript never 类型

在类型理论(数学逻辑中的一种理论)中, 底部类型 是没有值的类型。也称为零或空类型,有时用 falsum(⊥)表示。 数学理论与计算机的发展是相辅相成的,底部类型在计算机科学中也有一定的应用场景。

JS数据类型转换表

下表显示了将不同的JavaScript值转换为Number,String和Boolean的结果:注意:引号(\\\"\\\")中的值表示字符串值。在红色的值是程序员可能不希望被转换为的值。

Js的typeof返回哪些数据类型?

JavaScript中的数据类型:值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。引用数据类型:对象(Object)、数组(Array)、函数(Function)。

为你的 JavaScript 项目添加智能提示和类型检查

近在做项目代码重构,其中有一个要求是为代码添加智能提示和类型检查。智能提示,英文为 IntelliSense,能为开发者提供代码智能补全、悬浮提示、跳转定义等功能,帮助其正确并且快速完成编码。

JS中的布尔 数字 字符串

JS中所有的值都可以转换成布尔类型 使用Boolean()或者 !!(两个感叹号),JS中所有的值都可以转换成数字类型,使用Number()或+。数字类型转换场景目的只有一个,用于计算,将后台传递的数据,从字符串转换为数字并参与计算

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!